⚡ Implementação de pipeline de renderização programável, anotação chinesa completa, ajudando os iniciantes a aprender princípios de renderização.
mkdir build
cmake -S . -B ./build
cmake --build ./build --config ReleaseObserve que copie a pasta Res para o caminho em que o arquivo executável está localizado.
Basta encontrar um arquivo de exemplo que começa com sample_ e compile -o diretamente com o arquivo único do GCC:
gcc -O2 sample_07_specular.cpp -o sample_07_specular -lstdc++ Parece que eu preciso adicionar -std=c++17 ao meu Mac. Pode ser necessário adicionar um -lm para exibir e declarar a biblioteca de matemática do link.
correr:
./sample_07_specular Em seguida, obtenha um arquivo de imagem output.bmp :
O modelo deste projeto usa o modelo de código aberto em Tinyrender.
Ele usa principalmente uma estrutura ShadContext, usada para passar parâmetros entre VS-> ps, e está cheia de vários tipos de variações.
// 着色器上下文,由 VS 设置,再由渲染器按像素逐点插值后,供 PS 读取
struct ShaderContext {
std::map< int , float > varying_float; // 浮点数 varying 列表
std::map< int , Vec2f> varying_vec2f; // 二维矢量 varying 列表
std::map< int , Vec3f> varying_vec3f; // 三维矢量 varying 列表
std::map< int , Vec4f> varying_vec4f; // 四维矢量 varying 列表
}; A camada externa precisa fornecer o ponteiro da função para o renderizador vs e chamar os três vértices do triângulo em sequência quando a função DrawPrimitive do drawer do renderizador é inicializada:
// 顶点着色器:因为是 C++ 编写,无需传递 attribute,传个 0-2 的顶点序号
// 着色器函数直接在外层根据序号读取响应数据即可,最后需要返回一个坐标 pos
// 各项 varying 设置到 output 里,由渲染器插值后传递给 PS
typedef std::function<Vec4f( int index, ShaderContext &output)> VertexShader; Cada vez que a chamada é chamada, o renderizador passará os números 0 , 1 index 2 dos três vértices para o programa VS, por sua vez, para facilitar a leitura dos dados do vértice do lado de fora.
O renderizador chama o shader de pixel para todos os pontos que precisam ser preenchidos no triângulo:
// 像素着色器:输入 ShaderContext,需要返回 Vec4f 类型的颜色
// 三角形内每个点的 input 具体值会根据前面三个顶点的 output 插值得到
typedef std::function<Vec4f(ShaderContext &input)> PixelShader;A cor retornada pelo programa de sombreamento de pixels será atraída para a posição correspondente do buffer de quadro.
Chamar a seguinte interface pode desenhar um triângulo:
bool RenderHelp::DrawPrimitive ()Esta função é o núcleo do renderizador.
Em seguida, duas camadas de loop iteram sobre cada ponto do retângulo de conexão do triângulo na tela e, em seguida, determinam que, dentro da faixa do triângulo, chame o programa VS para calcular qual é a cor do ponto.
Agora você deseja escrever um desenho de triângulo do D3D 12. Você não pode lidar com isso sem mil linhas, mas agora precisamos apenas das seguintes linhas:
# include " RenderHelp.h "
int main ( void )
{
// 初始化渲染器和帧缓存大小
RenderHelp rh ( 800 , 600 );
const int VARYING_COLOR = 0 ; // 定义一个 varying 的 key
// 顶点数据,由 VS 读取,如有多个三角形,可每次更新 vs_input 再绘制
struct { Vec4f pos; Vec4f color; } vs_input[ 3 ] = {
{ { 0.0 , 0.7 , 0.90 , 1 }, { 1 , 0 , 0 , 1 } },
{ { - 0.6 , - 0.2 , 0.01 , 1 }, { 0 , 1 , 0 , 1 } },
{ { + 0.6 , - 0.2 , 0.01 , 1 }, { 0 , 0 , 1 , 1 } },
};
// 顶点着色器,初始化 varying 并返回坐标,
// 参数 index 是渲染器传入的顶点序号,范围 [0, 2] 用于读取顶点数据
rh. SetVertexShader ([&] ( int index , ShaderContext& output) -> Vec4f {
output. varying_vec4f [VARYING_COLOR] = vs_input[ index ]. color ;
return vs_input[ index ]. pos ; // 直接返回坐标
});
// 像素着色器,返回颜色
rh. SetPixelShader ([&] (ShaderContext& input) -> Vec4f {
return input. varying_vec4f [VARYING_COLOR];
});
// 渲染并保存
rh. DrawPrimitive ();
rh. SaveFile ( " output.bmp " );
return 0 ;
}Resultados em execução:
| Nome do arquivo | ilustrar |
|---|---|
| Renderhelp.h | O arquivo de implementação do renderizador, quando usado inclui, é suficiente |
| Model.h | Carregando o modelo |
| Sample_01_triangle.cpp | Exemplo de desenho de triângulos |
| Sample_02_texture.cpp | Como usar texturas, como definir a matriz da câmera, etc. |
| Sample_03_box.cpp | Como desenhar uma caixa |
| Sample_04_gouraud.cpp | Coloração simples de galaude da caixa |
| Sample_05_model.cpp | Como carregar e desenhar um modelo |
| Sample_06_Normal.cpp | Aprimore os detalhes do modelo com mapa normal |
| Sample_07_specular.cpp | Desenhe os destaques |
Mais de dez anos atrás, escrevi um Mini3D do Soft Renderor, que explicou claramente os princípios principais dos renderizadores de software.
O método de implementação deste projeto é modelado no método de implementação da equação de borda da GPU. O método de implementação deste projeto simula a GPU é relativamente simples e intuitivo, mas o cálculo é muito grande e não é adequado para a CPU em tempo real, mas é adequado para o processamento paralelo aproximado da GPU.
Há muitos tutoriais para implementar pipelines de renderização programáveis na Internet, mas muitos deles têm problemas Por exemplo, quando a amostragem de textura, a conversão de coordenadas inteiras deve ser arredondada, caso contrário, a posição de vários vértices da textura não é estável o suficiente, e haverá sinais de um pouco em movimento; . . .
Existem muitos aspectos muito detalhados da implementação do renderizador.
Outro é a legibilidade.
O arquivo principal RenderHelp.h .
Os princípios básicos, expliquei no seguinte artigo:
Ao ler, o código é basicamente algumas bibliotecas de ferramentas na frente, que podem ser lidas nas últimas 200 linhas.
Se você não entende o código, pode fazer uma pergunta no problema.