⚡ Programmierbare Rendering -Pipeline -Implementierung, vollständige chinesische Annotation, die Anfänger hilft, die Rendering -Prinzipien zu lernen.
mkdir build
cmake -S . -B ./build
cmake --build ./build --config ReleaseBeachten Sie, dass kopieren Sie den RES -Ordner in den Pfad, in dem sich die ausführbare Datei befindet.
Suchen Sie einfach eine Beispieldatei, die mit sample_ beginnt, und kompilieren Sie sie direkt mit einer einzelnen GCC -Datei:
gcc -O2 sample_07_specular.cpp -o sample_07_specular -lstdc++ Es scheint, dass ich meinem Mac -std=c++17 hinzufügen muss. Möglicherweise müssen Sie eine -lm hinzufügen, um die Link -Mathematikbibliothek anzuzeigen und zu deklarieren.
laufen:
./sample_07_specular Erhalten Sie dann eine output.bmp :
Das Modell dieses Projekts verwendet das Open -Source -Modell in Tinyrender.
Es wird hauptsächlich eine ShaderContext-Struktur verwendet, mit der Parameter zwischen VS-> PS geleitet werden, und es ist voll von verschiedenen Arten von Variationen.
// 着色器上下文,由 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 列表
}; Die äußere Ebene muss den Funktionszeiger auf den Renderer VS liefern und die drei Scheitelpunkte des Dreiecks nacheinander aufrufen, wenn die DrawPrimitive -Funktion des Renderers initialisiert wird:
// 顶点着色器:因为是 C++ 编写,无需传递 attribute,传个 0-2 的顶点序号
// 着色器函数直接在外层根据序号读取响应数据即可,最后需要返回一个坐标 pos
// 各项 varying 设置到 output 里,由渲染器插值后传递给 PS
typedef std::function<Vec4f( int index, ShaderContext &output)> VertexShader; Jedes Mal, wenn der Anruf aufgerufen wird, übergibt der Renderer die Nummern 0 , 1 index 2 der drei Scheitelpunkte an das VS -Programm, um das Lesen von Scheitelpunktdaten von außen zu erleichtern.
Der Renderer nennt den Pixel -Shader für jeden Punkt, der im Dreieck gefüllt werden muss:
// 像素着色器:输入 ShaderContext,需要返回 Vec4f 类型的颜色
// 三角形内每个点的 input 具体值会根据前面三个顶点的 output 插值得到
typedef std::function<Vec4f(ShaderContext &input)> PixelShader;Die vom Pixel -Shading -Programm zurückgegebene Farbe wird in die entsprechende Position des Rahmenpuffers gezogen.
Das Aufrufen der folgenden Schnittstelle kann ein Dreieck zeichnen:
bool RenderHelp::DrawPrimitive ()Diese Funktion ist der Kern des Renderers.
Anschließend zwei Schichten für die Schleife über jeden Punkt des Dreiecks, das das Rechteck auf dem Bildschirm verbindet, und dann feststellen, dass innerhalb des Dreieckbereichs das VS -Programm aufgerufen wird, um zu berechnen, welche Farbe der Punkt ist.
Jetzt möchten Sie eine Dreieckszeichnung von D3D 12 schreiben. Sie können es ohne tausend Zeilen nicht damit umgehen, aber jetzt brauchen wir nur die folgenden Zeilen:
# 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 ;
}Auslaufergebnisse:
| Dateiname | veranschaulichen |
|---|---|
| RenderHelp.h | Die Renderer -Implementierungsdatei, wenn sie verwendet wird, ist sie ausreichend |
| Modell.h | Laden des Modells |
| sample_01_triangle.cpp | Beispiel für das Zeichnen von Dreiecken |
| Sample_02_Texture.cpp | So verwenden Sie Texturen, So setzen Sie die Kameramatrix usw. |
| sample_03_box.cpp | Wie man eine Schachtel zeichnet |
| Sample_04_gouraud.cpp | Einfache Galaudefärbung der Box |
| sample_05_model.cpp | So laden und zeichnen Sie ein Modell |
| Sample_06_Normal.cpp | Verbesserung der Modelldetails mit normaler Karte |
| Sample_07_Specular.cpp | Zeichnen Highlights |
Vor mehr als zehn Jahren schrieb ich ein Soft -Renderer -Tutorial Mini3d, der die Kernprinzipien der Software -Renderer klar erklärte.
Die Implementierungsmethode dieses Projekts ist auf der Edge-Gleichung-Implementierungsmethode der GPU modelliert. Die Implementierungsmethode dieses Projekts simuliert die GPU ist relativ einfach und intuitiv, aber die Berechnung ist sehr groß und für Echtzeit-CPU nicht geeignet, ist jedoch für eine grobe parallele Verarbeitung der GPU geeignet.
Es gibt viele Tutorials zur Implementierung programmierbarer Rendering -Pipelines im Internet, aber viele von ihnen haben Probleme Wenn die Texturabtastung die Ganzzahlkoordinatenumwandlung abgerundet werden sollte, ist die Position mehrerer Scheitelpunkte der Textur nicht stabil genug, und es werden Anzeichen einer leicht bewegenden Software -Renderer in der Lage sein, die Textur mit korrekter Perspektive zu erkennen und immer noch die Nachahmungs -Textur -Mapping zu verwenden. . . .
Es gibt viele sehr detaillierte Aspekte der Renderer -Implementierung.
Eine andere ist die Lesbarkeit, um die Menge an Code absichtlich zu reduzieren, um viele Details zu verkürzen.
Die Hauptdatei- RenderHelp.h dieses Projekts hat insgesamt mehr als eintausend Zeilen, von denen ein Drittel chinesische Anmerkungen sind.
Die Grundprinzipien habe ich im folgenden Artikel erklärt:
Beim Lesen ist der Code im Grunde ein paar Werkzeugbibliotheken, die aus den letzten 200 Zeilen gelesen werden können.
Wenn Sie den Code nicht verstehen, können Sie eine Frage in der Ausgabe stellen.