OpenGL 開発が理解する必要がある 5 つの座標系

OpenGL は、各頂点シェーダーの実行後に、表示されるようにしたいすべての頂点が正規化されたデバイス座標内にあることを期待します。つまり、各頂点の x、y、z 座標は -1.0 ~ 1.0 の範囲内である必要があり、この範囲外の座標は表示されません。通常行うのは、自分で決めた範囲 (または空間) で座標を指定し、その座標を頂点シェーダーで正規化デバイス座標 (NDC) に変換することです。これらの NDC はラスタライザーに供給され、画面上の 2D 座標/ピクセルに変換されます。

ここに画像の説明を挿入

推奨事項: NSDT Designerを使用して、プログラム可能な 3D シーンを迅速に構築します。

座標の NDC への変換は通常、段階的に行われ、オブジェクトの頂点をいくつかの座標系に変換し、最後にそれらを NDC に変換します。これらを複数の中間座標系に変換する利点は、すぐに明らかになるように、特定の座標系では特定の操作/計算が容易になることです。私たちにとって重要な座標系は合計 5 つあります。

  • ローカル空間(またはオブジェクト空間)
  • ワールドスペース
  • ビュースペース(またはアイスペース)
  • クリップスペース
  • スクリーンスペース

これらは、頂点がフラグメントになる前に変換されるさまざまな状態です。

おそらく、空間や座標系が実際にどのようなものであるかについてかなり混乱していると思います。そこで、全体像とそれぞれの特定の空間が何を表すかを示すことで、より高度な方法でそれらを説明することから始めます。

1. パノラマ

ある空間から次の空間に座標を変換するには、いくつかの変換行列を使用します。その中で最も重要なものは、モデル、ビュー、投影行列です。頂点座標はローカル空間内でローカル座標として開始され、次にワールド座標、ビュー座標、クリップ座標にさらに処理され、最後にスクリーン座標になります。次の図はプロセスを示し、各変換が何を行うかを示しています。
ここに画像の説明を挿入

  • ローカル座標は、ローカル原点を基準としたオブジェクトの座標であり、オブジェクトの開始位置の座標です。
  • 次のステップは、ローカル座標を、より大きな世界を基準とした座標であるワールド空間座標に変換することです。これらの座標は世界のグローバルな原点を基準としており、他の多くのオブジェクトはその世界の原点を基準にして配置されます。
  • 次に、各座標がカメラまたは視聴者の視点から見えるように、ワールド座標をビュー空間座標に変換します。
  • 座標がビュー空間に表示されたら、それらをクリップ座標に投影します。クリップ座標は -1.0 および 1.0 の範囲として処理され、どの頂点が最終的​​に画面上に表示されるかが決まります。透視投影を使用している場合、クリップ空間座標に投影すると遠近感を追加できます。
  • 最後に、クリップ座標をスクリーン座標に変換します。これはビューポート変換と呼ばれるプロセスで、座標を -1.0 と 1.0 から glViewport で定義された座標範囲に変換します。結果の座標はラスタライザーに送信され、フラグメントに変換されます。

おそらく、それぞれのスペースが何のためにあるのか少しはわかっているでしょう。頂点をこれらすべての異なる空間に変換する理由は、特定の座標系では特定の操作がより意味があり、使いやすいためです。たとえば、オブジェクトを変更する場合は、ローカル空間で行うのが最も合理的ですが、他のオブジェクトを基準にしてオブジェクトに対する特定の操作を計算する場合は、ワールド座標などで行うのが最も合理的です。必要に応じて、ローカル空間からクリップ空間への変換行列を定義することもできますが、そうすると柔軟性が低下します。

以下で各座標系について詳しく説明します。

2. ローカル空間

ローカル空間は、オブジェクトに対してローカルな座標空間、つまりオブジェクトの開始位置です。Blender などのモデリング パッケージで立方体を作成したと想像してください。最終的なアプリケーションではキューブが別の場所に配置される可能性がありますが、キューブの原点は (0,0,0) にある可能性があります。おそらく、作成するすべてのモデルの初期位置は (0,0,0) になります。したがって、モデルのすべての頂点はローカル空間に配置され、オブジェクトに対してすべてローカルになります。

使用するコンテナーの頂点は、0.0 を原点として、-0.5 から 0.5 までの座標として指定されます。これらはローカル座標です。

3. ワールド空間

すべてのオブジェクトをアプリケーションに直接インポートした場合、それらはすべて、ワールド原点 (0,0,0) で互いの内側のどこかにある可能性がありますが、これは私たちが望んでいることではありません。各オブジェクトの位置を定義して、より大きな世界に配置したいと考えています。

ワールド空間の座標は、その名の通り、(ゲーム) 世界に対するすべての頂点の座標です。これは、オブジェクトがその位置に (できれば現実的な方法で) 散在するように変換する座標空間です。オブジェクトの座標はローカル空間からワールド空間に変換されます。これはモデル マトリックスを通じて行われます。

モデル マトリックスは、オブジェクトを変換、拡大縮小、回転してワールド/方向内の適切な位置に配置する変換マトリックスです。これは、家を縮小して (ローカル空間では少し大きすぎます)、郊外の町に変え、Y 軸上で少し左に回転させて、隣接するタウンハウスにぴったり合うように改造することだと考えてください。シーン全体にコンテナを配置した前の章のマトリックスは、一種のモデル マトリックスとして考えることができ、コンテナのローカル座標をシーン/ワールド内の別の位置に変換します。

4. 空間を見る

ビュー スペースは、通常 OpenGL のカメラと呼ばれるものです (カメラ スペースまたはアイ スペースとも呼ばれます)。ビュー空間は、ワールド空間座標をユーザーのビューの前の座標に変換した結果です。したがって、ビュー空間はカメラの視点から見た空間です。これは通常、移動と回転を組み合わせてシーンを移動/回転することで行われ、特定のアイテムがカメラの正面に表示されるように変換されます。これらの組み合わせた変換は通常、世界座標をビュー空間に変換するビュー マトリックスに保存されます。次の章では、カメラをシミュレートするためにこのようなビュー マトリックスを作成する方法について詳しく説明します。

5. クリッピングスペース

各頂点シェーダーの実行の終了時に、OpenGL は座標が特定の範囲内にあることを期待し、その範囲外の座標はクリップされます。クリップされた座標は破棄されるため、残りの座標は断片として画面上に表示されます。これがクリップ スペース名の由来です。

表示されているすべての座標が -1.0 から 1.0 の範囲内になるように指定するのは直感的ではないため、OpenGL が期待するように、使用する独自の座標セットを指定し、それらを NDC に変換し直します。

頂点座標をビューからクリップ空間に変換するには、いわゆる射影行列を定義します。これは、一連の座標 (たとえば、各次元に対して -1000 と 1000) を指定します。次に、射影行列は、指定された範囲内の座標を正規化されたデバイス座標 (-1.0, 1.0) に変換します (直接変換ではなく、間に「透視分割」と呼ばれるステップがあります)。この範囲外のすべての座標は -1.0 と 1.0 の間にマッピングされないため、クリップされます。射影行列で指定したこの範囲では、x 座標が範囲外であり、NDC で 1.0 を超える座標に変換されてクリップされるため、座標 (1250、500、750) は表示されません。

三角形などのプリミティブの一部のみがクリッピング ボリュームの外側にある場合、OpenGL はその三角形をクリッピング ボリューム内に収まるように 1 つ以上の三角形に再構築することに注意してください。

射影行列によって作成されるこの表示ボックスは表示錐台と呼ばれ、その表示錐台内にあるすべての座標は最終的にユーザーの画面上に表示されます。指定された範囲の座標を、2D ビュー空間座標に簡単にマッピングできる NDC に変換するプロセス全体は、投影と呼ばれます。これは、投影行列が 3D 座標を、2D に簡単にマッピングできる 2D 正規化デバイス座標に投影するためです。

すべての頂点がクリップ空間に変換されると、パースペクティブ除算と呼ばれる最終操作が実行され、位置ベクトルの x、y、z 成分をベクトルの同次 w 成分で除算します。パースペクティブ除算は、4D クリップ空間座標を 3D 正規化デバイス座標に変換します。このステップは、頂点シェーダー ステップの最後に自動的に実行されます。

この段階の後、結果の座標は (glViewport の設定を使用して) 画面座標にマッピングされ、フラグメントに変換されます。

ビュー座標をクリップ座標に変換する射影行列は通常、2 つの異なる形式をとり、それぞれが独自の固有の視錐台を定義します。正投影行列または透視投影行列を作成できます。

6. 正射影

正投影行列は、クリッピング スペースを定義する立方体のようなボリュームを定義し、そのボックスの外側のすべての頂点をクリッピングします。正投影行列を作成するときは、表示ボリュームの幅、高さ、長さを指定します。このボリューム内のすべての座標は、マトリックス変換後に NDC 範囲内になるため、クリップされません。ボリュームはコンテナに少し似ています。
ここに画像の説明を挿入

ボリュームは可視の座標を定義し、幅、高さ、近位面と遠位面によって指定されます。近い平面の前の座標はすべてクリップされ、遠い平面の後ろの座標にも同じことが当てはまります。正投影は、変換されたベクトルの w コンポーネントに触れないため、特別な副作用なしに、クリッピング ボリューム内のすべての座標を正規化されたデバイス座標に直接マッピングします。w コンポーネントが 1.0 のままであれば、透視分割によって座標は変更されません。

正投影行列を作成するには、GLM の組み込み関数 glm::ortho を使用します。

glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

最初の 2 つのパラメータはクリッピング ボリュームの左右の座標を指定し、3 番目と 4 番目のパラメータは下部と上部を指定します。これら 4 つの点を通じて、近い面と遠い面のサイズを定義し、5 番目と 6 番目のパラメーターは近い面と遠い面の間の距離を定義します。この特定の射影行列は、これらの x、y、z 範囲値の間のすべての座標を正規化されたデバイス座標に変換します。

正投影行列は座標をスクリーンの 2D 平面に直接マッピングしますが、実際には、投影では遠近法が考慮されていないため、直接投影では非現実的な結果が生成されます。これは、透視投影行列が解決してくれる問題です。

7. 透視図法

現実のグラフィックスを楽しんだことがある方なら、遠くにあるオブジェクトははるかに小さく見えるでしょう。この奇妙な効果は遠近効果と呼ばれるものです。以下の画像に示すように、遠近感は、無限の高速道路や鉄道の終点を見下ろすときに特に顕著です。

ここに画像の説明を挿入

ご覧のとおり、遠近法の関係で、線は十分に離れたところで一致しているように見えます。これはまさに透視投影が模倣しようとしているものであり、透視投影行列を使用してこれを行います。

射影行列は、指定された錐台範囲をクリップ空間にマッピングしますが、頂点座標がビューアから遠ざかるほど w コンポーネントが高くなるように各頂点座標の w 値を操作します。座標がクリップ空間に変換されると、座標は -w から w の範囲内になります (その外側にあるものはすべてクリップされます)。OpenGL では、最終的な頂点シェーダー出力の可視座標が -1.0 ~ 1.0 の範囲にある必要があるため、クリップ スペース座標にパースペクティブ分割が適用されます。
ここに画像の説明を挿入

頂点座標の各成分は w 成分で除算され、頂点が観察者から遠くなるほど、頂点座標は小さくなります。これは、透視投影に役立つため、w コンポーネントが重要であるもう 1 つの理由です。結果の座標は正規化されたデバイス空間内にあります。正投影行列と透視投影行列が実際にどのように計算されるかに興味がある場合 (そして数学はあまり怖くない場合)、Songho によるこの素晴らしい記事をお勧めします。

透視投影行列は、次のように GLM で作成できます。

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);

glm::perspective が行うことは、可視空間を定義する錐台を再度作成することです。錐台の外側にあるものはクリップ スペース ボリュームに収まらないため、クリップされます。透視錐台は、ボックス内の各座標がクリップ空間内の点にマッピングされる不均一な形状のボックスと考えることができます。透視錐台のイメージは次のようになります。

ここに画像の説明を挿入

最初のパラメータは fov 値を定義します。これは視野を表し、表示スペースの大きさを設定します。現実的なビューの場合、これは通常 45 度に設定されますが、よりドゥーム風の結果を得るには、より高い値に設定できます。2 番目のパラメータは、ビューポートの幅を高さで割ることによって計算されるアスペクト比を設定します。3 番目と 4 番目のパラメータは、錐台の近い面と遠い面を設定します。通常、近距離を 0.1 に、遠距離を 100.0 に設定します。近い平面と遠い平面の間、および表示錐台内のすべての頂点がレンダリングされます。

遠近行列の近い値の設定が高すぎると (10.0 など)、OpenGL はカメラに近いすべての座標 (0.0 から 10.0 の間) をクリップします。これにより、ビデオ ゲームで以前に見たような視覚的な結果が得られます。つまり、不当に近づくと特定のオブジェクトが貫通される可能性があります。

正射投影を使用する場合、各頂点座標は、派手な透視分割を行わずにクリップ空間に直接マッピングされます (透視分割は行われますが、w コンポーネントは操作されない (1 のままである) ため、効果はありません)。

正投影では透視投影が使用されないため、遠くにあるオブジェクトが小さく見えず、奇妙な視覚出力が生成される可能性があります。したがって、正投影は主に 2D レンダリングや、遠近法によって頂点が歪まないようにする一部の建築または工学アプリケーションに使用されます。3D モデリング用の Blender などのアプリケーションでは、各オブジェクトの寸法をより正確に表現できるため、モデリングに正投影を使用することがあります。以下に、Blender の 2 つの投影方法の比較を示します。
ここに画像の説明を挿入

透視投影では、遠くにある頂点がはるかに小さく見えるのに対し、正投影では各頂点がユーザーから同じ距離にあることがわかります。

8. 統合する

上記の各ステップで、モデル行列、ビュー行列、投影行列といった変換行列を作成します。次に、次のように頂点座標をクリップ座標に変換します。
ここに画像の説明を挿入

行列の乗算の順序が逆になることに注意してください (行列の乗算を右から左に読む必要があることに注意してください)。結果として得られる頂点は、頂点シェーダーの gl_Position に割り当てられる必要があります。これにより、OpenGL がパースペクティブの分割とクリッピングを自動的に実行します。

頂点シェーダーの出力では、座標がクリップ空間内にある必要があります。これは、変換マトリックスで行ったばかりのことです。次に、OpenGL はクリップ空間座標に対して透視分割を実行し、それらを正規化されたデバイス座標に変換します。OpenGL は、glViewPort のパラメータを使用して、正規化されたデバイス座標を画面座標にマップします。各座標は画面 (この場合は 800x600 画面) 上の点に対応します。このプロセスはビューポート変換と呼ばれます。

これは理解するのが難しいテーマなので、各スペースの用途がまだよくわからなくても心配する必要はありません。以下では、これらの座標空間を実際に最大限に活用する方法を説明します。次の章で十分な例を示します。

9. 3D への移行

3D 座標を 2D 座標に変換する方法がわかったので、これまでに示した粗末な 2D 平面の代わりに実際の 3D オブジェクトのレンダリングを開始できます。

3D 描画を開始するには、まずモデル マトリックスを作成します。モデル行列は、すべてのオブジェクトの頂点をグローバル ワールド空間に変換するために適用する、変換、スケール、および/または回転で構成されます。平面を X 軸上で回転させて少し変形させて、床の上に横たわっているように見せてみましょう。モデルのマトリックスは次のようになります。

glm::mat4 model = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f)); 

頂点座標にこのモデル行列を乗算することにより、頂点座標をワールド座標に変換します。

次に、ビュー マトリックスを作成する必要があります。シーン内を少し後方に移動して、オブジェクトが見えるようにしたいとします (ワールド空間の原点 (0,0,0) にあるとき)。シーン内を移動するには、次の点を考慮してください。

カメラを後方に移動することは、シーン全体を前方に移動することと同じです。

これはまさにビュー マトリックスが行うことです。カメラを移動させたい場所にシーン全体を逆に移動します。
後方に移動したいため、また OpenGL は右手系システムであるため、正の Z 軸に沿って移動する必要があります。これを行うには、シーンを負の Z 軸に向かって移動します。これは私たちが後退しているような印象を与えます。

ここに画像の説明を挿入

右手系

慣例により、OpenGL は右手系システムです。これは基本的に、正の X 軸が右側にあり、正の Y 軸が上にあり、正の Z 軸が後ろにあることを示しています。画面が 3 軸の中心であると仮定すると、正の Z 軸は画面を通過して手前に向きます。座標軸は上図のように描画されます。

なぜ右巻きと呼ばれるかを理解するには、次の手順を実行します。

  • 手を上にして右腕を Y 軸の正の方向に伸ばします。
  • 親指を右に向けます。
  • 人差し指を上に向けます。
  • 次に、中指を下に90度曲げます。

正しく行われた場合、親指は x 軸の正の方向を指し、人差し指は y 軸の正の方向を指し、中指は z 軸の正の方向を指すはずです。左腕でこれを行うと、Z 軸が逆になるのがわかります。これは左手系と呼ばれ、通常は DirectX で使用されます。正規化されたデバイス座標では、OpenGL は実際には左手系を使用することに注意してください (射影行列は左手系を切り替えます)。

シーン内を移動する方法については、次の章で詳しく説明します。これで、ビュー マトリックスは次のようになります。

glm::mat4 view = glm::mat4(1.0f);
// note that we're translating the scene in the reverse direction of where we want to move
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); 

最後に定義する必要があるのは、射影行列です。シーンで透視投影を使用したいので、次のように投影行列を宣言します。

glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);

変換マトリックスを作成したので、それをシェーダーに渡す必要があります。まず、頂点シェーダーで変換行列をユニフォームとして宣言し、頂点座標を乗算します。

#version 330 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    // note that we read the multiplication from right to left
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    ...
}

マトリックスもシェーダーに送信する必要があります (変換マトリックスは大きく変化する傾向があるため、これは通常フレームごとに行われます)。

int modelLoc = glGetUniformLocation(ourShader.ID, "model");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
... // same for View Matrix and Projection Matrix

頂点座標がモデル、ビュー、投影行列を通じて変換されたので、最終的なオブジェクトは次のようになります。

  • 床に寄りかかります。
  • 私たちから離れてください。
  • 遠近法で表示されます (頂点が遠くにあるほど、頂点は小さくなります)。

結果が実際にこれらの要件を満たしていることを確認してみましょう。
ここに画像の説明を挿入

この飛行機は、架空の床の上に置かれた 3D 飛行機のように見えます。同じ結果が得られない場合は、コードを完全なソース コードと比較してください。

10. さらなる 3D

これまでは、3D 空間であっても 2D 平面を使用して作業してきました。それでは、冒険的なルートを選択して、2D 平面を 3D 立方体に拡張してみましょう。立方体をレンダリングするには、合計 36 個の頂点 (頂点ごとに 6 つの面 * 2 つの三角形 * 3 つ) が必要です。36 個の頂点を合計すると大量になるため、ここから頂点を取得できます。

楽しみのために、立方体を時間の経過とともに回転させてみましょう。

model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));  

次に、glDrawArrays を使用して立方体を描画します (インデックスを指定しなかったため)。今回は 36 個の頂点を使用します。

glDrawArrays(GL_TRIANGLES, 0, 36);

次のような内容が表示されるはずです。ここをクリックしてビデオを再生してください。

ここに画像の説明を挿入

少し立方体のように見えますが、何かが間違っています。立方体の一部の面は、立方体の他の面上に描画されます。これは、OpenGL が立方体の三角形を三角形ごと、フラグメントごとに描画するときに、以前に描画された可能性のあるピクセルの色が上書きされるために発生します。OpenGL は (同じ描画呼び出し内で) レンダリングされる三角形の順序を保証しないため、1 つの三角形が他の三角形より明らかに前にあるはずであっても、いくつかの三角形は互いに重なって描画されます。

幸いなことに、OpenGL は深度情報を Z バッファーと呼ばれるバッファーに保存します。これにより、OpenGL は、いつピクセルに描画するか、いつ描画しないかを決定できます。Z バッファを使用すると、深度テスト用に OpenGL を構成できます。

11. Zバッファ

OpenGL は、すべての深度情報を Z バッファー (深度バッファーとも呼ばれます) に保存します。GLFW は、そのようなバッファーを自動的に作成します (出力画像の色を保存するカラー バッファーがあるのと同じです)。深度は各フラグメントに (フラグメントの Z 値として) 保存され、フラグメントがその色を出力したいときはいつでも、OpenGL はその深度値を Z バッファーと比較します。現在のフラグメントが別のフラグメントの背後にある場合、そのフラグメントは破棄され、そうでない場合は上書きされます。このプロセスは深度テストと呼ばれ、OpenGL によって自動的に実行されます。

ただし、OpenGL が深度テストを実行していることを確認したい場合は、まず OpenGL に深度テストを有効にするように指示する必要があります (デフォルトでは無効になっています)。glEnable を使用して深度テストを有効にできます。glEnable 関数と glDisable 関数を使用すると、OpenGL の特定の機能を有効/無効にすることができます。その後、この機能を無効または有効にするための別の呼び出しが行われるまで、この機能は有効または無効になります。次に、GL_DEPTH_TEST を有効にして深度テストを有効にします。

glEnable(GL_DEPTH_TEST);  

深度バッファーを使用するため、各レンダリング反復の前に深度バッファーもクリアする必要があります (そうしないと、前のフレームの深度情報がバッファーに残ります)。カラー バッファをクリアするのと同じように、glClear 関数で DEPTH_BUFFER_BIT ビットを指定することで深度バッファをクリアできます。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

プログラムを再実行して、OpenGL が深度テストを実行するかどうかを確認してみましょう。ビデオ:
ここに画像の説明を挿入

始めましょう!完全にテクスチャ化された立方体、適切な深度テストが行​​われ、時間の経過とともに回転します。ここでソースコードを確認してください。

12. キューブが増えました!

画面上に 10 個の立方体を表示したいとします。各立方体は同じように見えますが、唯一の違いはワールド内での位置であり、各立方体の回転が異なります。キューブのグラフィック レイアウトはすでに定義されているため、さらに多くのオブジェクトをレンダリングするときにバッファーや属性配列を変更する必要はありません。各オブジェクトについて変更する必要があるのは、立方体を世界に変換するモデル マトリックスだけです。

まず、各立方体の平行移動ベクトルを定義し、ワールド空間での位置を指定します。glm::vec3 配列で 10 個の立方体位置を定義します。

glm::vec3 cubePositions[] = {
    glm::vec3( 0.0f,  0.0f,  0.0f), 
    glm::vec3( 2.0f,  5.0f, -15.0f), 
    glm::vec3(-1.5f, -2.2f, -2.5f),  
    glm::vec3(-3.8f, -2.0f, -12.3f),  
    glm::vec3( 2.4f, -0.4f, -3.5f),  
    glm::vec3(-1.7f,  3.0f, -7.5f),  
    glm::vec3( 1.3f, -2.0f, -2.5f),  
    glm::vec3( 1.5f,  2.0f, -2.5f), 
    glm::vec3( 1.5f,  0.2f, -1.5f), 
    glm::vec3(-1.3f,  1.0f, -1.5f)  
};

ここで、レンダー ループで glDrawArrays を 10 回呼び出していますが、今回は描画呼び出しを発行する前に別のモデル マトリックスを頂点シェーダーに送信します。レンダリング ループ内に小さなループを作成し、毎回異なるモデル マトリックスを使用してオブジェクトを 10 回レンダリングします。各コンテナに小さな固有の回転も追加したことに注意してください。

glBindVertexArray(VAO);
for(unsigned int i = 0; i < 10; i++)
{
    glm::mat4 model = glm::mat4(1.0f);
    model = glm::translate(model, cubePositions[i]);
    float angle = 20.0f * i; 
    model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
    ourShader.setMat4("model", model);

    glDrawArrays(GL_TRIANGLES, 0, 36);
}

このコードは、新しい立方体が描画されるたびにモデル行列を合計 10 回更新します。これで、奇妙に回転する 10 個の立方体で満たされた世界が表示されるはずです。
ここに画像の説明を挿入

完全!私たちのコンテナは志を同じくする友人を見つけたようです。行き詰まった場合は、コードをソース コードと比較できるかどうかを確認してください。


元のリンク: OpenGL の 5 つの座標系 - BimAnt

おすすめ

転載: blog.csdn.net/shebao3333/article/details/131863539