今日はこれを描きます:

このモデルはアーティストのSamuel Sharit(arshlevon)によって描かれました。コンピューターグラフィックスの講義コースの一部として使用できるようにしてくれたことに感謝します。
ステージ0:tinyrendererを読む
一般的に言えば、私の必要なものではありませんが、この講義は、 tinyrendererのコース全体を読んだ後に行うのが最善です。 英語を話さない人のために、この講座はハブで利用できますが 、ロシア語版はサポートしていません。 このレクチャーコースの一環として、わずか500行のコードで、この図だけを描画する方法を示しました。サードパーティのライブラリを完全に禁止した場合でもです。

驚いたことに、私の学生の多くは、このソフトウェアラスタライザーが単なるおもちゃではなく、OpenGLがどのように機能するかを実際に紹介していることを理解していません。 したがって、今日は、ハードウェアアクセラレーションを使用して糖尿病をレンダリングする方法を示し、ソフトウェアラスタライザーリポジトリのコードを多くの点で使用します。
注意、ドキュメントを読むことがすべてを理解する最良の方法であるという事実に依存しているので、私はコードのすべての行を説明するタスクを自分で設定しません。 私のコードが必要なのは、ドキュメント内の正確な内容と順序を知るためだけです。 さらに、 シェーダーとは何かを説明せず、 法線マップの読み方も説明しません。 私はtinyrendererに多くの時間を費やしました。
ステージ1、最も難しい:ウィンドウの作成
リポジトリ全体がここにあります。 githubは行われたすべての変更の非常に便利なビューアを提供するため、チュートリアルの各ステップに対して1つのコミットを作成しました。 ここから始めて 、目標はこのウィンドウを取得することです。

コードはCMakeを使用してコンパイルされます。 Linux(g ++)およびWindows(Visual Studio 2017)で確認しました。 Linuxでは、最新バージョンのコードは次のようにコンパイルされます。
git clone --recurse-submodules https://github.com/ssloy/hellOGL.git cd hellOGL mkdir build cd build cmake .. make
最新バージョンではなく、別のコミットをコンパイルする場合は、「git checkout」を使用します。 このコードは、gladとGLFWをロードし、必要なキーボードコールバックでウィンドウを作成し、空の頂点シェーダーとピクセルシェーダーをディスクからロードします。
ステージ2:3Dモデルの読み込み
この段階で行われたプロジェクトの変更については、こちらをご覧ください 。 この段階での目標は、3Dモデルファイルを解析し、現時点で照明を気にせずに最初の三角形を描画することです。

モデル自体とベクターを操作するためのライブラリの両方に注意してください。モデルパーサーはすべてtinyrendererから取得しました。 たぶんソフトウェアレンダラーはそれほど役に立たないでしょうか?
最新のOpenGLの基本的な考え方は非常に単純です。 最初に3Dモデルをアップロードしてから、サイズ3 * 3 *(三角形の数)の頂点配列を作成します。 各三角形には3つの頂点がありますか? 各頂点は、3つの数字(x、y、z)で記述されます。 合計で、モデル全体を記述するには3 * 3 * model.nfaces()で十分です。
std::vector<GLfloat> vertices(3*3*model.nfaces(), 0); for (int i=0; i<model.nfaces(); i++) { for (int j=0; j<3; j++) { for (int k=0; k<3; k++) vertices[(i*3+j)*3 + k] = model.point(model.vert(i, j))[k]; } }
そして、OpenGLに、ここがネイティブの配列、描画であることを伝えます!
while (!glfwWindowShouldClose(window)) { [...]  glDrawArrays(GL_TRIANGLES, 0, vertices.size()); [...] }
頂点シェーダーは興味深いことは何もせず、そのままフラグメントシェーダーにデータを渡すだけです。
#version 330 core // Input vertex data, different for all executions of this shader layout(location = 0) in vec3 vertexPosition_modelspace; void main() { gl_Position = vec4(vertexPosition_modelspace, 1); // Output position of the vertex, in clip space }
まあ、 フラグメントシェーダーも気取らないです。 現在のピクルスを単に赤で描画します。
#version 330 core // Output data out vec3 color; void main() { color = vec3(1,0,0); }
最も難しいことは、今や技術の問題です!
ステージ3:拡散照明
この段階で行われたプロジェクトの変更については、こちらをご覧ください 。 この画像を取得する必要があります。

Phongモデルの拡散照明は、ご存知のように、単純なスカラー積です。
法線ベクトルと照明ベクトル。 したがって、頂点配列に加えて、別の法線配列を追加しました。 コードを見ずに、サイズを教えてください。
最も興味深いのは、メインの.cppファイルのフラグメントシェーダーで、データのみがロードされることです。
#version 330 core // Output data out vec3 color; // Interpolated values from the vertex shaders in vec3 Normal_cameraspace; in vec3 LightDirection_cameraspace; void main() { vec3 n = normalize(Normal_cameraspace); // Normal of the computed fragment, in camera space vec3 l = normalize(LightDirection_cameraspace); // Direction of the light (from the fragment to the light) float cosTheta = clamp(dot(n,l), 0, 1); // Cosine of the angle between the normal and the light direction, color = vec3(1,0,0)*(0.1 + // ambient lighting 1.3*cosTheta); // diffuse lighting }
ステージ4:変換マトリックス
この段階で行われたプロジェクトの変更については、こちらをご覧ください 。 この時点で、Model、View、およびProjectionマトリックスをコーディングしました。 最初は単一ですが、スペースキーを押すと、モデルが回転し始めます。画像を描くたびに、モデルマトリックスをz軸の周りに0.01ラジアンだけ回転させます。
{ // rotate the model around the z axis with each frame Matrix R = rot_z(0.01); if (animate) M = R*M; }
ここで、関数rot_z()は、指定された角度でz軸の周りの回転行列を返します。 OpenGLは私のマトリックスクラスについて何も知らないため、floatへの単純なポインターにマトリックスエクスポートvoid export_row_major()を追加する必要がありました。

ステージ5:法線マップ
この段階で行われたプロジェクトの変更については、こちらをご覧ください 。 この時点で、テクスチャをオーバーレイする方法を学びます。 通常の拡散テクスチャは退屈なので、すぐに法線マップを適用し、接線空間にも適用します。 法線マップは次のようになります。

控えめに言っても、対応する計算は明らかではないため、tinyrendererの説明をもう一度読んでください 。 データに関しては、UV座標、タンジェントおよびバイタンジェントベクトルの配列など、いくつかのバッファを追加する必要があります。

ステージ5:拡散テクスチャ
さて、法線マップのカウント方法をすでに知っている場合、法線拡散テクスチャの適用は簡単です。 この段階で行われたプロジェクトの変更については、こちらをご覧ください 。

ステージ6:グレア
この段階で行われたプロジェクトの変更については、こちらをご覧ください 。 最終段階では、光沢のある表面からの照明のまぶしさをシミュレートできる別のテクスチャを追加します。

おわりに
このコードには改善できるものがたくさんあり、視覚効果は無限にひねることができます。 しかし、これは私の目標ではなく、ソフトウェアレンダリングで触れたすべての技術が現在のOpenGLコンテキストに適用できることを示すことです。 そして個人的には、グラフィックライブラリの魔法を使わずに絵を描くことで、3Dグラフィックに精通する必要があると考えています。
拡張機能として、たとえば、 影を追加したり、 グローバルライティングをカウントしたり、最後にグローマップを作成してみてください。結局のところ、Diabloの額の目とクリスタルが輝きます。