⚡ Implementación programable de la tubería, anotación china completa, ayudando a los principiantes a aprender principios de representación.
mkdir build
cmake -S . -B ./build
cmake --build ./build --config ReleaseTenga en cuenta que copia la carpeta Res a la ruta donde se encuentra el archivo ejecutable.
Simplemente busque un archivo de ejemplo que comience con sample_ y compile directamente con el archivo único GCC:
gcc -O2 sample_07_specular.cpp -o sample_07_specular -lstdc++ Parece que necesito agregar -std=c++17 a mi Mac. Es posible que deba agregar un -lm para mostrar y declarar la biblioteca de matemáticas de enlace.
correr:
./sample_07_specular Luego obtenga una output.bmp del archivo de imagen. BMP:
El modelo de este proyecto utiliza el modelo de código abierto en Tinyrender.
Utiliza principalmente una estructura de sombreado, que se utiliza para pasar parámetros entre VS-> PS, y está lleno de varios tipos de variaciones.
// 着色器上下文,由 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 列表
}; La capa externa debe proporcionar el puntero de la función al renderizador vs, y llamar a los tres vértices del triángulo en secuencia cuando se inicializa la función DrawPrimitive del renderizador:
// 顶点着色器:因为是 C++ 编写,无需传递 attribute,传个 0-2 的顶点序号
// 着色器函数直接在外层根据序号读取响应数据即可,最后需要返回一个坐标 pos
// 各项 varying 设置到 output 里,由渲染器插值后传递给 PS
typedef std::function<Vec4f( int index, ShaderContext &output)> VertexShader; Cada vez que se llame a la llamada, el renderizador pasará los números 0 , 1 index 2 de los tres vértices al programa VS a su vez para facilitar la lectura de los datos del vértice desde el exterior.
El renderizador llama al sombreador de píxeles para cada punto que debe llenarse en el triángulo:
// 像素着色器:输入 ShaderContext,需要返回 Vec4f 类型的颜色
// 三角形内每个点的 input 具体值会根据前面三个顶点的 output 插值得到
typedef std::function<Vec4f(ShaderContext &input)> PixelShader;El color devuelto por el programa de sombreado de píxeles se dibujará a la posición correspondiente del búfer de cuadro.
Llamar a la siguiente interfaz puede dibujar un triángulo:
bool RenderHelp::DrawPrimitive ()Esta función es el núcleo del renderista.
Luego, dos capas de For Loop iterate sobre cada punto del rectángulo de conexión del triángulo en la pantalla, y luego determinan que dentro del rango de triángulo, llame al programa VS para calcular qué color es el punto.
Ahora desea escribir un dibujo de triángulo de D3D 12. No puede manejarlo sin mil líneas, pero ahora solo necesitamos las siguientes líneas:
# 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 de ejecución:
| Nombre del archivo | ilustrar |
|---|---|
| RenderHelp.H | El archivo de implementación del renderizador, cuando se usa, incluye que es suficiente |
| Modelo.h | Cargando el modelo |
| sample_01_triangle.cpp | Ejemplo de triángulos de dibujo |
| sample_02_texture.cpp | Cómo usar texturas, cómo configurar la matriz de la cámara, etc. |
| sample_03_box.cpp | Cómo dibujar una caja |
| sample_04_gouraud.cpp | Coloración simple de galaude de la caja |
| sample_05_model.cpp | Cómo cargar y dibujar un modelo |
| sample_06_normal.cpp | Mejorar los detalles del modelo con el mapa normal |
| sample_07_specular.cpp | Dibujar reflejos |
Hace más de diez años, escribí un tutorial de renderizador suave Mini3D, que explicaba claramente los principios centrales de los renderizadores de software.
El método de implementación de este proyecto se modela en el método de implementación de la ecuación de borde de la GPU. El método de implementación de este proyecto simula GPU es relativamente simple e intuitivo, pero el cálculo es muy grande y no es adecuado para la CPU en tiempo real, pero es adecuado para el procesamiento paralelo aproximado de la GPU.
Hay muchos tutoriales para implementar las tuberías programables en Internet, pero muchos de ellos tienen problemas. , cuando el muestreo de textura, la conversión de coordenadas de enteros debe redondearse, de lo contrario, la posición de varios vértices de la textura no es lo suficientemente estable, y habrá signos de móvil ligeramente conmovedoras ni siquiera puede ver la textura con una perspectiva correcta y aún estará utilizando la mapeo de la textura de imitación. . . .
Hay muchos aspectos muy detallados de la implementación del renderizador.
Otro es la legibilidad.
El archivo principal RenderHelp.h de este proyecto tiene más de mil líneas en total, un tercio de las cuales son anotaciones chinas.
Los principios básicos, he explicado en el siguiente artículo:
Al leer, el código es básicamente algunas bibliotecas de herramientas frente a él, que se pueden leer desde las últimas 200 líneas.
Si no comprende el código, puede hacer una pregunta en el tema.