4. 三角形へのレンダリングとライティング パート1

此のチュートリアルシリーズを2025年7月22日午前2時から午前7時の間に始めた方へお知らせです。HomebrewがmacOSにデフォルトでインストールされていない事に気づき、インストールが非常に難しい場合がある為、libディレクトリにglfw.dylibを追加し、Xcodeプロジェクト設定を/usr/local/libのグローバルディレクトリではなくローカルディレクトリにリンクする様に変更しました。其の為、Macユーザー向けのGLFWインストール手順をGLSL 概要ページから削除しました。

Unixユーザーの場合、build_unix.shファイルに-lmフラグをリンカーに追加し、数学関数が動作する様に更新しました。

其の他の方には、includeディレクトリにvector.hファイルを追加しました。此れにより、色値を9つの異なるfloatで定義する代わりに、3つのVector3を作成出来る様になり、見た目がずっと良くなりました。其の為、グラデーションページも其れに合わせて調整しました。最後に、ページの最後で完全なソースコードを表示する代わりに、.diffファイルを共有し、その直下にプレビューを表示する様にしました。これにより、スクロール量が大幅に削減されます。

ZIP及びTARファイルも更新しました。こちらで入手出来ます。


4.1 三角形へのレンダリング

現在、シェーダーを画面全体にレンダリングしています。此れは見た目は良いですが、画面上に他に何をレンダリング出来るかという点で非常に制限があります。先ず其の問題を解決し、シェーダーを三角形にレンダリングしてみましょう。

  // 頂点データ(フルスクリーンクアッド)を設定
  float vertices[] = {
    -0.5f, -0.5f, 0.0f, 0.0f, // 左下(位置、UV)
     0.5f, -0.5f, 1.0f, 0.0f, // 右下
     0.0f,  0.5f, 0.5f, 1.0f
  };
  GLuint indices[] = { 0, 1, 2 };
  ...
      glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);

結果:

シェーダー1

4.2 ライティングの説明

次に、ライティングについて説明します。重要でない様に思えるかもしんが、複雑なモデルを使用する場合、ライティングは非常に重要な概念です。一般的な光の種類は6つあり、此の章では其の内の最初の2つを、簡単なものから難しい物の順に説明します。

アンビエントライト

アンビエントライトは理解と実装が最も簡単です。方向性を持たない均一な光で、方向や距離などの複雑な計算は必要ありません。簡単に言えば、「アンビエントライト」は「背景光」とも呼ばれます。

ディレクショナルライト

ディレクショナルライトは、光の方向と表面の法線という、3Dライティングの基本的な概念を導入します。此れも比較的簡単です。太陽光に例える事が出来ます。

ポイントライト

ポイントライトは、ディレクショナルライトに位置と減衰を追加し、距離による光の減衰の概念を導入します。電球に例える事が出来ます。

光の方向の計算:(光の位置 - フラグメントの位置)

減衰を適用するには:1 / (定数 + 線形 * d + 二次体 * d²)

スポットライト

スポットライトは、ポイントライトに方向性と円錐角度を追加した物です。懐中電灯に例える事が出来ます。

ヘミスフィアライト

ヘミスフィアライトは、アンビエントライトの特殊な形態で、方向性(空と地面)の変化を追加します。エリアライトよりは複雑ではありませんが、環境に基づくライティングの概念を導入します。屋外のライティングをシミュレートする為に使用出来ます。

エリアライト

エリアライトは最も複雑で、表面全体の積分や近似(複数のポイントライトなど)が必要です。エリアライトは、窓やパネルの様な現実世界の表面をシミュレートします。

4.3 アンビエントライト

シェーダーディレクトリに新しいファイルfrag_ambient.glslを作成します。

#version 450 core

in vec2 uVu;
out vec4 out_color;

uniform vec3 materialColor;
uniform float ambientIntensity;

void main() {
  vec3 ambient = materialColor * ambientIntensity;
  out_color = vec4(ambient, 1.0);
}

簡単そうですね?Cコードではもっと多くの作業が必要です。

  // シェーダーファイルを読み込む
#ifdef __APPLE__
  char *vertexShaderSource = readShaderFile("shader/vertex.mac.glsl");
  char *fragmentShaderSource = readShaderFile("shader/fragment.mac.glsl");
  char *fragmentAmbientSource = readShaderFile("shader/frag_ambient.mac.glsl");
#else
  char *vertexShaderSource = readShaderFile("shader/vertex.glsl");
  char *fragmentShaderSource = readShaderFile("shader/fragment.glsl");
  char *fragmentAmbientSource = readShaderFile("shader/frag_ambient.glsl");
#endif
  if (!vertexShaderSource || !fragmentShaderSource || !fragmentAmbientSource) {
    puts("シェーダーファイルの読み込みに失敗しました");
    if (vertexShaderSource) free(vertexShaderSource);
    if (fragmentShaderSource) free(fragmentShaderSource);
    if (fragmentAmbientSource) free(fragmentAmbientSource);
    glfwDestroyWindow(window);
    glfwTerminate();
    return -1;
  }

  // シェーダーをコンパイル
  GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertexShader, 1, (const char **)&vertexShaderSource, NULL);
  glCompileShader(vertexShader);
  checkShaderCompile(vertexShader);

  GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentShader, 1, (const char **)&fragmentShaderSource, NULL);
  glCompileShader(fragmentShader);
  checkShaderCompile(fragmentShader);

  GLuint fragmentAmbientShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentAmbientShader, 1, (const char **)&fragmentAmbientSource, NULL);
  glCompileShader(fragmentAmbientShader);
  checkShaderCompile(fragmentAmbientShader);

  // 三角形のシェーダープログラムをリンク
  GLuint shaderProgram = glCreateProgram();
  glAttachShader(shaderProgram, vertexShader);
  glAttachShader(shaderProgram, fragmentShader);
  glLinkProgram(shaderProgram);
  checkProgramLink(shaderProgram);

  // アンビエントシェーダープログラムをリンク
  GLuint ambientShaderProgram = glCreateProgram();
  glAttachShader(ambientShaderProgram, vertexShader);
  glAttachShader(ambientShaderProgram, fragmentAmbientShader);
  glLinkProgram(ambientShaderProgram);
  checkProgramLink(ambientShaderProgram);

  glDeleteShader(vertexShader);
  glDeleteShader(fragmentShader);
  glDeleteShader(fragmentAmbientShader);
  free(vertexShaderSource);
  free(fragmentShaderSource);
  free(fragmentAmbientSource);

  // 頂点データ(三角形)を設定:三角形用(左にオフセット)
  float vertices[] = {
    -0.8f, -0.5f, 0.0f, 0.0f, // 左下(位置、UV)
    -0.3f, -0.5f, 1.0f, 0.0f, // 右下
    -0.55f, 0.5f, 0.5f, 1.0f  // 上
  };
  GLuint indices[] = { 0, 1, 2 };

  // 頂点データ(三角形)を設定:アンビエント用(右にオフセット)
  float verticesAmbient[] = {
     0.3f, -0.5f, 0.0f, 0.0f, // 左下(位置、UV)
     0.8f, -0.5f, 1.0f, 0.0f, // 右下
     0.55f, 0.5f, 0.5f, 1.0f  // 上
  };

  // VAO、VBO、EBOを三角形用に設定
  GLuint VAO, VBO, EBO;
  glGenVertexArrays(1, &VAO);
  glGenBuffers(1, &VBO);
  glGenBuffers(1, &EBO);

  glBindVertexArray(VAO);
  glBindBuffer(GL_ARRAY_BUFFER, VBO);
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

  // 頂点属性:位置(location 0)、UV(location 1)
  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
  glEnableVertexAttribArray(0);
  size_t offset = 2 * sizeof(float);
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)offset);
  glEnableVertexAttribArray(1);

  // VAO、VBO、EBOをアンビエント用に設定
  GLuint VAOAmbient, VBOAmbient, EBOAmbient;
  glGenVertexArrays(1, &VAOAmbient);
  glGenBuffers(1, &VBOAmbient);
  glGenBuffers(1, &EBOAmbient);

  glBindVertexArray(VAOAmbient);
  glBindBuffer(GL_ARRAY_BUFFER, VBOAmbient);
  glBufferData(GL_ARRAY_BUFFER, sizeof(verticesAmbient), verticesAmbient, GL_STATIC_DRAW);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOAmbient);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)offset);
  glEnableVertexAttribArray(1);

  // メインレンダリングループ
  while (!glfwWindowShouldClose(window)) {
    // 画面をクリア
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // シェーダープログラムを使用
    glUseProgram(shaderProgram);

    // 時間に基づいて色をアニメーション
    float time = (float)glfwGetTime();
    float speed = 1.0f; // アニメーションの速度
    float phase = 2.0f * 3.14159f / 3.0f; // // 120度の位相差(3色用)

    // uColorA: 赤 -> 緑 -> 青
    Vector3 colorA;
    colorA.r = sin(time * speed + 0.0f) * 0.5f + 0.5f; // 0.0 to 1.0
    colorA.g = sin(time * speed + phase) * 0.5f + 0.5f;
    colorA.b = sin(time * speed + 2.0f * phase) * 0.5f + 0.5f;

    // uColorB: 緑 -> 青 -> 赤
    Vector3 colorB;
    colorB.r = sin(time * speed + 2.0f * phase) * 0.5f + 0.5f;
    colorB.g = sin(time * speed + 0.0f) * 0.5f + 0.5f;
    colorB.b = sin(time * speed + phase) * 0.5f + 0.5f;

    // uColorC: 青 -> 赤 -> 緑
    Vector3 colorC;
    colorC.r = sin(time * speed + phase) * 0.5f + 0.5f;
    colorC.g = sin(time * speed + 2.0f * phase) * 0.5f + 0.5f;
    colorC.b = sin(time * speed + 0.0f) * 0.5f + 0.5f;

    // ユニフォームを設定
    glUniform3f(glGetUniformLocation(shaderProgram, "uColorA"), colorA.r, colorA.g, colorA.b); // 赤
    glUniform3f(glGetUniformLocation(shaderProgram, "uColorB"), colorB.r, colorB.g, colorB.b); // 緑
    glUniform3f(glGetUniformLocation(shaderProgram, "uColorC"), colorC.r, colorC.g, colorC.b); // 青

    // クアッドを描画
    glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);

    // アンビエントシェーダープログラムを使用(右の三角形)
    glUseProgram(ambientShaderProgram);

    // ユニフォームを設定(アンビエントシェーダー)
    Vector3 materialColor = { 0.8f, 0.2f, 0.3f }; // 赤みがかった色
    float ambientIntensity = 0.6f; // アンビエント光の強さ(0.0~1.0)

    glUniform3f(glGetUniformLocation(ambientShaderProgram, "materialColor"), materialColor.r, materialColor.g, materialColor.b);
    glUniform1f(glGetUniformLocation(ambientShaderProgram, "ambientIntensity"), ambientIntensity);

    // 右の三角形を描画(アンビエントシェーダー)
    glBindVertexArray(VAOAmbient);
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);

    // バッファをスワップし、イベントを処理
    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  // クリーンアップ
  glDeleteVertexArrays(1, &VAO);
  glDeleteBuffers(1, &VBO);
  glDeleteBuffers(1, &EBO);
  glDeleteVertexArrays(1, &VAOAmbient);
  glDeleteBuffers(1, &VBOAmbient);
  glDeleteBuffers(1, &EBOAmbient);
  glDeleteProgram(shaderProgram);
  glDeleteProgram(ambientShaderProgram);
  glfwDestroyWindow(window);
  glfwTerminate();

結果は次の様になります:

シェーダー2

4.4 ディレクショナルライト

此のチュートリアルではディレクショナルライトは似た様に見えるかもしんが、実際の複雑な環境では全く異なる物です。同じ様に見える理由は、影を落とす3Dオブジェクトがない為です。

新しいファイルvertex_directional.glslを作成し、以下を挿入します:

#version 450 core

layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aUv;
layout(location = 2) in vec3 aNormal;

out vec2 vUv;
out vec3 vNormal;

void main() {
  vUv = aUv;
  vNormal = aNormal;
  gl_Position = vec4(aPosition, 0.0, 1.0);
}

layout(location = 2) in vec3 aNormal; は、頂点の法線のための3Dベクトル入力を定義し、属性2に割り当てます。法線は頂点が「向いている」方向を示し、光が各頂点にどの様に当たるかを決定します。vNormal = aNormal; で此の値を変更せずにフラグメントシェーダーに渡します。

新しいファイルfrag_directional.glslを作成し、以下を挿入します:

#version 450 core

in vec2 vUv;
out vec4 out_color;

uniform vec3 materialColor;
uniform vec3 lightDirection;
uniform float lightIntensity;

void main() {
  vec3 normal = vec3(0.0, 0.0, 1.0);

  float diffuseFactor = max(dot(normal, -normalize(lightDirection)), 0.0);
  vec3 diffuse = materialColor * diffuseFactor * lightIntensity;
  out_color = vec4(diffuse, 1.0);
}

in vec3 vNormal;は、頂点シェーダーから受け取った法線ベクトルを追加します。uniform vec3 materialColor;は、三角形の基本色(RGB)を保持します。此れはuniformであり、Cプログラムから設定され、全てのピクセルで同じままです。uniform vec3 lightDirection;は、光が来る方向の座標(XYZ)を保持します。此れは、太陽の様に直線的な光の方向をシェーダーに伝えます。uniform float lightIntensity;は、光の明るさを保持し、0.0が最も暗く(光なし)、1.0が最も明るいです。

vec3 normal = normalize(vNormal);は、補間された法線(vNormal)を取り、長さを1にスケールします(normalize)。此れにより、正確なライティング計算が保証されます。normalize(lightDirection)は、lightDirectionベクトルを長さ1にスケールし、一貫した計算を保証します。-normalize(lightDirection)は、光の方向を反転させ、表面に向かって光が来る様にします。dot(normal, -normalize(lightDirections))は、法線と反転した光の方向の間の角度のコサインを計算します。max(..., 0.0)は、結果が負にならない様にし(負の値は表面の裏から来る光を意味し、それを無視します)。

main.cで:

  // 頂点データ(三角形)を設定:ディレクショナル用(右にオフセット)
  float verticesDirectional[] = {
     0.3f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.707f, // 左下(位置、UV、形動ノーマル)
     0.8f, -0.5f, 1.0f, 0.0f,  0.5f, -0.5f, 0.707f, // 右下
     0.55f, 0.5f, 0.5f, 1.0f,  0.0f,  0.5f, 0.707f  // 上
  };
  ...
    // VAO、VBO、EBOをディレクショナル用に設定
  GLuint VAODirectional, VBODirectional, EBODirectional;
  glGenVertexArrays(1, &VAODirectional);
  glGenBuffers(1, &VBODirectional);
  glGenBuffers(1, &EBODirectional);

  glBindVertexArray(VAODirectional);
  glBindBuffer(GL_ARRAY_BUFFER, VBODirectional);
  glBufferData(GL_ARRAY_BUFFER, sizeof(verticesDirectional), verticesDirectional, GL_STATIC_DRAW);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBODirectional);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void *)0);
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void *)(2 * sizeof(float)));
  glEnableVertexAttribArray(1);
  glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void *)(4 * sizeof(float)));
  glEnableVertexAttribArray(2);
  ...
    // ディレクショナルシェーダープログラムを使用(右の三角形)
    glUseProgram(directionalShaderProgram);

    // ユニフォームを設定(アンビエントシェーダー)
    Vector3 materialColor = { 0.8f, 0.2f, 0.3f }; // 赤みがかった色
    Vector3 lightDirection = { 0.0f, 0.0f, -1.0f }; // ライトの方向(正規化不要、シェーダーで処理)
    float lightIntensity = 1.0f; // ライトの強さ(0.0~1.0)

    glUniform3f(glGetUniformLocation(directionalShaderProgram, "materialColor"), materialColor.r, materialColor.g, materialColor.b);
    glUniform3f(glGetUniformLocation(directionalShaderProgram, "lightDirection"), lightDirection.x, lightDirection.y, lightDirection.z);
    glUniform1f(glGetUniformLocation(directionalShaderProgram, "lightIntensity"), lightIntensity);

シェーダー3

最終的な変更: DIFFをダウンロード

ファイル: a/main-ambient.c

151 // シェーダーファイルを読み込む151 // シェーダーファイルを読み込む
152#ifdef __APPLE__152#ifdef __APPLE__
153 char *vertexShaderSource = readShaderFile("shader/vertex.mac.glsl");153 char *vertexShaderSource = readShaderFile("shader/vertex.mac.glsl");
154 char *fragmentShaderSource = readShaderFile("shader/fragment.mac.glsl");154 char *fragmentShaderSource = readShaderFile("shader/fragment.mac.glsl");
155 155 char *fragmentAmbientSource = readShaderFile("shader/frag_ambient.mac.glsl");
156#else156#else
157 char *vertexShaderSource = readShaderFile("shader/vertex.glsl");157 char *vertexShaderSource = readShaderFile("shader/vertex.glsl");
158 char *fragmentShaderSource = readShaderFile("shader/fragment.glsl");158 char *fragmentShaderSource = readShaderFile("shader/fragment.glsl");
159 159 char *fragmentAmbientSource = readShaderFile("shader/frag_ambient.glsl");
160#endif160#endif
161 if (!vertexShaderSource || !fragmentShaderSource) {161 if (!vertexShaderSource || !vertexDirectionalSource || !fragmentShaderSource || !fragmentAmbientSource) {
162 puts("シェーダーファイルの読み込みに失敗しました");162 puts("シェーダーファイルの読み込みに失敗しました");
163 if (vertexShaderSource) free(vertexShaderSource);163 if (vertexShaderSource) free(vertexShaderSource);
164 if (fragmentShaderSource) free(fragmentShaderSource);164 if (fragmentShaderSource) free(fragmentShaderSource);
165 165 if (fragmentAmbientSource) free(fragmentAmbientSource);
166 glfwDestroyWindow(window);166 glfwDestroyWindow(window);
167 glfwTerminate();167 glfwTerminate();
168 return -1;168 return -1;
171 181 glCompileShader(vertexShader);
172 182 checkShaderCompile(vertexShader);
173 183 
174 184 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
175 185 glShaderSource(fragmentShader, 1, (const char **)&fragmentShaderSource, NULL);
176 186 glCompileShader(fragmentShader);
177 187 checkShaderCompile(fragmentShader);
178 188 
179 189 GLuint fragmentAmbientShader = glCreateShader(GL_FRAGMENT_SHADER);
180 190 glShaderSource(fragmentAmbientShader, 1, (const char **)&fragmentAmbientSource, NULL);
181 glCompileShader(vertexShader);191 glCompileShader(fragmentAmbientShader);
182 checkShaderCompile(vertexShader);192 checkShaderCompile(fragmentAmbientShader);
183 193 
184 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);194 // 三角形のシェーダープログラムをリンク
185 glShaderSource(fragmentShader, 1, (const char **)&fragmentShaderSource, NULL);195 GLuint shaderProgram = glCreateProgram();
186 glCompileShader(fragmentShader);196 glAttachShader(shaderProgram, vertexShader);
187 checkShaderCompile(fragmentShader);197 glAttachShader(shaderProgram, fragmentShader);
188 198 glLinkProgram(shaderProgram);
189 // シェーダープログラムをリンク199 checkProgramLink(shaderProgram);
190 200 
191 201 // アンビエントシェーダープログラムをリンク
192 202 GLuint ambientShaderProgram = glCreateProgram();
193 203 glAttachShader(ambientShaderProgram, vertexShader);
194 204 glAttachShader(ambientShaderProgram, fragmentAmbientShader);
195 GLuint shaderProgram = glCreateProgram();205 glLinkProgram(ambientShaderProgram);
196 glAttachShader(shaderProgram, vertexShader);206 checkProgramLink(ambientShaderProgram);
197 glAttachShader(shaderProgram, fragmentShader);207 
198 glLinkProgram(shaderProgram);208 glDeleteShader(vertexShader);
199 checkProgramLink(shaderProgram);209 glDeleteShader(fragmentShader);
200 210 glDeleteShader(fragmentAmbientShader);
201 211 free(vertexShaderSource);
202 212 free(vertexDirectionalSource);
203 213 free(fragmentShaderSource);
204 214 free(fragmentAmbientSource);
205 215 
206 216 // 頂点データ(三角形)を設定:三角形用(左にオフセット)
207 217 float vertices[] = {
208 glDeleteShader(vertexShader);218 -0.8f, -0.5f, 0.0f, 0.0f, // 左下(位置、UV)
209 glDeleteShader(fragmentShader);219 -0.3f, -0.5f, 1.0f, 0.0f, // 右下
210 220 -0.55f, 0.5f, 0.5f, 1.0f // 上
211 free(vertexShaderSource);221 };
212 222 GLuint indices[] = { 0, 1, 2 };
213 free(fragmentShaderSource);223 
214 224 // 頂点データ(三角形)を設定:アンビエント用(右にオフセット)
215 225 float verticesAmbient[] = {
216 // 頂点データ(フルスクリーンクアッド)を設定226 0.3f, -0.5f, 0.0f, 0.0f, // 左下(位置、UV)
217 float vertices[] = {227 0.8f, -0.5f, 1.0f, 0.0f, // 右下
218 -1.0f, -1.0f, 0.0f, 0.0f, // 左下(位置、UV)228 0.55f, 0.5f, 0.5f, 1.0f // 上
219 1.0f, -1.0f, 1.0f, 0.0f, // 右下229 };
220 1.0f, 1.0f, 1.0f, 1.0f, // 右上230 
221 -1.0f, 1.0f, 0.0f, 1.0f // 左上231 // VAO、VBO、EBOを三角形用に設定
222 GLuint indices[] = { 0, 1, 2, 2, 3, 0 };232 GLuint VAO, VBO, EBO;
223 233 glGenVertexArrays(1, &VAO);
224 234 glGenBuffers(1, &VBO);
225 267 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
226 268 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
227 269 
228 270 
229 271 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
230 272 glEnableVertexAttribArray(0);
231 273 size_t offset = 2 * sizeof(float);
232 GLuint VAO, VBO, EBO;274 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)offset);
233 glGenVertexArrays(1, &VAO);275 glEnableVertexAttribArray(1);
234 glGenBuffers(1, &VBO);276 
208 277 // VAO、VBO、EBOをアンビエント用に設定
209 278 GLuint VAOAmbient, VBOAmbient, EBOAmbient;
210 279 glGenVertexArrays(1, &VAOAmbient);
211 280 glGenBuffers(1, &VBOAmbient);
212 281 glGenBuffers(1, &EBOAmbient);
213 282 
214 283 glBindVertexArray(VAOAmbient);
215 284 glBindBuffer(GL_ARRAY_BUFFER, VBOAmbient);
216 285 glBufferData(GL_ARRAY_BUFFER, sizeof(verticesAmbient), verticesAmbient, GL_STATIC_DRAW);
217 286 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOAmbient);
218 287 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
219 288 
220 289 // メインレンダリングループ
221 290 while (!glfwWindowShouldClose(window)) {
222 291 // 画面をクリア
223 344 
224 345 // クアッドを描画
225 346 glBindVertexArray(VAO);
226 347 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
227 348 
228 349 // アンビエントシェーダープログラムを使用(右の三角形)
229 350 glUseProgram(ambientShaderProgram);
230 351 
231 352 // ユニフォームを設定(アンビエントシェーダー)
232 353 Vector3 materialColor = { 0.8f, 0.2f, 0.3f }; // 赤みがかった色
233 354 Vector3 lightDirection = { -0.5f, -0.5f, -0.707f }; // ライトの方向(正規化不要、シェーダーで処理)
234 355 float ambientIntensity = 0.6f; // アンビエント光の強さ(0.0~1.0)
235 356 
236 357 glUniform3f(glGetUniformLocation(directionalShaderProgram, "materialColor"), materialColor.r, materialColor.g, materialColor.b);
237 358 glUniform3f(glGetUniformLocation(directionalShaderProgram, "lightDirection"), lightDirection.x, lightDirection.y, lightDirection.z);
238 359 glUniform1f(glGetUniformLocation(ambientShaderProgram, "ambientIntensity"), ambientIntensity);
239 360 
240 361 // 右の三角形を描画(アンビエントシェーダー)
241 362 glBindVertexArray(VAOAmbient);
242 363 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
243 364 
244 365 // バッファをスワップし、イベントを処理
245 366 glfwSwapBuffers(window);
246 377 glDeleteVertexArrays(1, &VAO);
247 378 glDeleteBuffers(1, &VBO);
248 379 glDeleteBuffers(1, &EBO);
249 380 glDeleteVertexArrays(1, &VAOAmbient);
250 381 glDeleteBuffers(1, &VBOAmbient);
251 382 glDeleteBuffers(1, &EBOAmbient);
252 383 glDeleteProgram(shaderProgram);
253 384 glDeleteProgram(ambientShaderProgram);
254 385 glfwDestroyWindow(window);
255 386 glfwTerminate();
256 387 
257 388 return 0;
258 389}
259   
260   
261   
262   
263   
264   
265   
266   
267 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);  
268 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);  
269   
270 頂点属性:位置(location 0)、UV(location 1)  
271 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);  
272 glEnableVertexAttribArray(0);  
273 size_t offset = 2 * sizeof(float);  
274 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)offset);  
275 glEnableVertexAttribArray(1);  
276   
277   
278   
279   
280   
281   
282   
283   
284   
285   
286   
287   
288   
289 // メインレンダリングループ  
290 while (!glfwWindowShouldClose(window)) {  
291 // 画面をクリア  
254   
255   
256   
257   
258   
259   
260   
261   
262   
263   
264   
265   
266   
267   
268   
269   
270   
271   
272   
273   
274   
275   
276   
277   
278   
279   
280   
281   
282   
283   
284   
285   
286   
287   
288   
289   
290   
291   
292   
293   
294   
295   
296   
297   
298   
299   
300   
301   
302   
303   
304   
305   
306   
307   
308   
309   
310   
311   
312   
313   
314   
315   
316   
317   
318   
319   
320   
321   
322   
323   
324   
325   
326   
327   
328   
329   
330   
331   
332   
333   
334   
335   
336   
337   
338   
339   
340   
341   
342   
343   
344   
345 // クアッドを描画  
346 glBindVertexArray(VAO);  
347 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);  
348   
349   
350   
351   
352   
353   
354   
355   
356   
357   
358   
359   
360   
361   
362   
363   
364   
365 // バッファをスワップし、イベントを処理  
366 glfwSwapBuffers(window);  
265   
266   
267   
268   
269   
270   
271   
272   
273   
274   
275   
276   
277   
278   
279   
280   
281   
282   
283   
284   
285   
286   
287   
288   
289   
290   
291   
292   
293   
294   
295   
296   
297   
298   
299   
300   
301   
302   
303   
304   
305   
306   
307   
308   
309   
310   
311   
312   
313   
314   
315   
316   
317   
318   
319   
320   
321   
322   
323   
324   
325   
326   
327   
328   
329   
330   
331   
332   
333   
334   
335   
336   
337   
338   
339   
340   
341   
342   
343   
344   
345   
346   
347   
348   
349   
350   
351   
352   
353   
354   
355   
356   
357   
358   
359   
360   
361   
362   
363   
364   
365   
366   
367   
368   
369   
370   
371   
372   
373   
374   
375   
376   
377 glDeleteVertexArrays(1, &VAO);  
378 glDeleteBuffers(1, &VBO);  
379 glDeleteBuffers(1, &EBO);  
380   
381   
382   
383 glDeleteProgram(shaderProgram);  
384   
385 glfwDestroyWindow(window);  
386 glfwTerminate();  
387   
388 return 0;  
389}  

ファイル: a/main-directional.c

151 // シェーダーファイルを読み込む151 // シェーダーファイルを読み込む
152#ifdef __APPLE__152#ifdef __APPLE__
153 char *vertexShaderSource = readShaderFile("shader/vertex.mac.glsl");153 char *vertexShaderSource = readShaderFile("shader/vertex.mac.glsl");
154 154 char *vertexDirectionalSource = readShaderFile("shader/vertex_directional.mac.glsl");
155 char *fragmentShaderSource = readShaderFile("shader/fragment.mac.glsl");155 char *fragmentShaderSource = readShaderFile("shader/fragment.mac.glsl");
156 156 char *fragmentDirectionalSource = readShaderFile("shader/frag_directional.mac.glsl");
157#else157#else
158 char *vertexShaderSource = readShaderFile("shader/vertex.glsl");158 char *vertexShaderSource = readShaderFile("shader/vertex.glsl");
159 159 char *vertexDirectionalSource = readShaderFile("shader/vertex_directional.glsl");
160 char *fragmentShaderSource = readShaderFile("shader/fragment.glsl");160 char *fragmentShaderSource = readShaderFile("shader/fragment.glsl");
161 161 char *fragmentDirectionalSource = readShaderFile("shader/frag_directional.glsl");
162#endif162#endif
163 if (!vertexShaderSource || !fragmentShaderSource) {163 if (!vertexShaderSource || !vertexDirectionalSource || !fragmentShaderSource || !fragmentDirectionalSource) {
164 puts("シェーダーファイルの読み込みに失敗しました");164 puts("シェーダーファイルの読み込みに失敗しました");
165 if (vertexShaderSource) free(vertexShaderSource);165 if (vertexShaderSource) free(vertexShaderSource);
166 166 if (vertexDirectionalSource) free(vertexDirectionalSource);
167 if (fragmentShaderSource) free(fragmentShaderSource);167 if (fragmentShaderSource) free(fragmentShaderSource);
168 168 if (fragmentDirectionalSource) free(fragmentDirectionalSource);
169 glfwDestroyWindow(window);169 glfwDestroyWindow(window);
170 glfwTerminate();170 glfwTerminate();
171 return -1;171 return -1;
171 181 glCompileShader(vertexShader);
172 182 checkShaderCompile(vertexShader);
173 183 
174 184 GLuint vertexDirectionalShader = glCreateShader(GL_VERTEX_SHADER);
175 185 glShaderSource(vertexDirectionalShader, 1, (const char **)&vertexDirectionalSource, NULL);
176 186 glCompileShader(vertexDirectionalShader);
177 187 checkShaderCompile(vertexDirectionalShader);
178 188 
179 189 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
180 190 glShaderSource(fragmentShader, 1, (const char **)&fragmentShaderSource, NULL);
181 glCompileShader(vertexShader);191 glCompileShader(fragmentShader);
182 checkShaderCompile(vertexShader);192 checkShaderCompile(fragmentShader);
183 193 
184 194 GLuint fragmentDirectionalShader = glCreateShader(GL_FRAGMENT_SHADER);
185 195 glShaderSource(fragmentDirectionalShader, 1, (const char **)&fragmentDirectionalSource, NULL);
186 196 glCompileShader(fragmentDirectionalShader);
187 197 checkShaderCompile(fragmentDirectionalShader);
188 198 
189 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);199 // 三角形のシェーダープログラムをリンク
190 glShaderSource(fragmentShader, 1, (const char **)&fragmentShaderSource, NULL);200 GLuint shaderProgram = glCreateProgram();
191 glCompileShader(fragmentShader);201 glAttachShader(shaderProgram, vertexShader);
192 checkShaderCompile(fragmentShader);202 glAttachShader(shaderProgram, fragmentShader);
193 203 glLinkProgram(shaderProgram);
194 204 checkProgramLink(shaderProgram);
195 205 
196 206 // ディレクショナルシェーダープログラムをリンク
197 207 GLuint directionalShaderProgram = glCreateProgram();
198 208 glAttachShader(directionalShaderProgram, vertexDirectionalShader);
199 209 glAttachShader(directionalShaderProgram, fragmentDirectionalShader);
200 GLuint shaderProgram = glCreateProgram();210 glLinkProgram(directionalShaderProgram);
201 glAttachShader(shaderProgram, vertexShader);211 checkProgramLink(directionalShaderProgram);
202 glAttachShader(shaderProgram, fragmentShader);212 
203 glLinkProgram(shaderProgram);213 glDeleteShader(vertexShader);
204 checkProgramLink(shaderProgram);214 glDeleteShader(vertexDirectionalShader);
205 215 glDeleteShader(fragmentShader);
206 216 glDeleteShader(fragmentDirectionalShader);
207 217 free(vertexShaderSource);
208 218 free(vertexDirectionalSource);
209 219 free(fragmentShaderSource);
210 220 free(fragmentDirectionalSource);
211 221 
212 222 // 頂点データ(三角形)を設定:三角形用(左にオフセット)
213 glDeleteShader(vertexShader);223 float vertices[] = {
214 224 -0.8f, -0.5f, 0.0f, 0.0f, // 左下(位置、UV)
215 glDeleteShader(fragmentShader);225 -0.3f, -0.5f, 1.0f, 0.0f, // 右下
216 226 -0.55f, 0.5f, 0.5f, 1.0f // 上
217 free(vertexShaderSource);227 };
218 228 GLuint indices[] = { 0, 1, 2 };
219 free(fragmentShaderSource);229 
220 230 // 頂点データ(三角形)を設定:ディレクショナル用(右にオフセット)
221 231 float verticesDirectional[] = {
222 // 頂点データ(フルスクリーンクアッド)を設定232 0.3f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.707f, // 左下(位置、UV、形動ノーマル)
223 float vertices[] = {233 0.8f, -0.5f, 1.0f, 0.0f, 0.5f, -0.5f, 0.707f, // 右下
224 -1.0f, -1.0f, 0.0f, 0.0f, // 左下(位置、UV)234 0.55f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.707f // 上
225 1.0f, -1.0f, 1.0f, 0.0f, // 右下235 };
226 1.0f, 1.0f, 1.0f, 1.0f, // 右上236 
227 -1.0f, 1.0f, 0.0f, 1.0f // 左上237 
228 238 // VAO、VBO、EBOを三角形用に設定
229 239 GLuint VAO, VBO, EBO;
230 240 glGenVertexArrays(1, &VAO);
231 241 glGenBuffers(1, &VBO);
232 267 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
233 268 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
234 269 
235 };270 // 頂点属性:位置(location 0)、UV(location 1)、ノーマル (2)
236 GLuint indices[] = { 0, 1, 2, 2, 3, 0 };271 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
237 272 glEnableVertexAttribArray(0);
238 273 size_t offset = 2 * sizeof(float);
239 GLuint VAO, VBO, EBO;274 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)offset);
240 glGenVertexArrays(1, &VAO);275 glEnableVertexAttribArray(1);
241 glGenBuffers(1, &VBO);276 
208 277 // VAO、VBO、EBOをディレクショナル用に設定
209 278 GLuint VAODirectional, VBODirectional, EBODirectional;
210 279 glGenVertexArrays(1, &VAODirectional);
211 280 glGenBuffers(1, &VBODirectional);
212 281 glGenBuffers(1, &EBODirectional);
213 282 
214 283 glBindVertexArray(VAODirectional);
215 284 glBindBuffer(GL_ARRAY_BUFFER, VBODirectional);
216 285 glBufferData(GL_ARRAY_BUFFER, sizeof(verticesDirectional), verticesDirectional, GL_STATIC_DRAW);
217 286 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBODirectional);
218 287 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
219 288 
220 289 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void *)0);
221 290 glEnableVertexAttribArray(0);
222 291 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void *)(2 * sizeof(float)));
223 292 glEnableVertexAttribArray(1);
224 293 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void *)(4 * sizeof(float)));
225 294 glEnableVertexAttribArray(2);
226 295 
227 296 // メインレンダリングループ
228 297 while (!glfwWindowShouldClose(window)) {
229 298 // 画面をクリア
230 344 
231 345 // クアッドを描画
232 346 glBindVertexArray(VAO);
233 347 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
234 348 
235 349 // ディレクショナルシェーダープログラムを使用(右の三角形)
236 350 glUseProgram(directionalShaderProgram);
237 351 
238 352 // ユニフォームを設定(アンビエントシェーダー)
239 353 Vector3 materialColor = { 0.8f, 0.2f, 0.3f }; // 赤みがかった色
240 354 Vector3 lightDirection = { -0.5f, -0.5f, -0.707f }; // ライトの方向(正規化不要、シェーダーで処理)
241 355 float lightIntensity = 1.0f; // ライトの強さ(0.0~1.0)
242 356 
243 357 glUniform3f(glGetUniformLocation(directionalShaderProgram, "materialColor"), materialColor.r, materialColor.g, materialColor.b);
244 358 glUniform3f(glGetUniformLocation(directionalShaderProgram, "lightDirection"), lightDirection.x, lightDirection.y, lightDirection.z);
245 359 glUniform1f(glGetUniformLocation(directionalShaderProgram, "lightIntensity"), lightIntensity);
246 360 
247 361 // 右の三角形を描画(ディレクショナルシェーダー)
248 362 glBindVertexArray(VAODirectional);
249 363 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
250 364 
251 365 // バッファをスワップし、イベントを処理
252 366 glfwSwapBuffers(window);
253 377 glDeleteVertexArrays(1, &VAO);
254 378 glDeleteBuffers(1, &VBO);
255 379 glDeleteBuffers(1, &EBO);
256 380 glDeleteVertexArrays(1, &VAODirectional);
257 381 glDeleteBuffers(1, &VBODirectional);
258 382 glDeleteBuffers(1, &EBODirectional);
259 383 glDeleteProgram(shaderProgram);
260 384 glDeleteProgram(directionalShaderProgram);
261 385 glfwDestroyWindow(window);
262 386 glfwTerminate();
263 387 
264 388 return 0;
265 389}
266   
267 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);  
268 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);  
269   
270 // 頂点属性:位置(location 0)、UV(location 1)  
271 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);  
272 glEnableVertexAttribArray(0);  
273 size_t offset = 2 * sizeof(float);  
274 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)offset);  
275 glEnableVertexAttribArray(1);  
276   
277   
278   
279   
280   
281   
282   
283   
284   
285   
286   
287   
288   
289   
290   
291   
292   
293   
294   
295   
296 // メインレンダリングループ  
297 while (!glfwWindowShouldClose(window)) {  
298 // 画面をクリア  
254   
255   
256   
257   
258   
259   
260   
261   
262   
263   
264   
265   
266   
267   
268   
269   
270   
271   
272   
273   
274   
275   
276   
277   
278   
279   
280   
281   
282   
283   
284   
285   
286   
287   
288   
289   
290   
291   
292   
293   
294   
295   
296   
297   
298   
299   
300   
301   
302   
303   
304   
305   
306   
307   
308   
309   
310   
311   
312   
313   
314   
315   
316   
317   
318   
319   
320   
321   
322   
323   
324   
325   
326   
327   
328   
329   
330   
331   
332   
333   
334   
335   
336   
337   
338   
339   
340   
341   
342   
343   
344   
345 // クアッドを描画  
346 glBindVertexArray(VAO);  
347 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);  
348   
349   
350   
351   
352   
353   
354   
355   
356   
357   
358   
359   
360   
361   
362   
363   
364   
365 // バッファをスワップし、イベントを処理  
366 glfwSwapBuffers(window);  
265   
266   
267   
268   
269   
270   
271   
272   
273   
274   
275   
276   
277   
278   
279   
280   
281   
282   
283   
284   
285   
286   
287   
288   
289   
290   
291   
292   
293   
294   
295   
296   
297   
298   
299   
300   
301   
302   
303   
304   
305   
306   
307   
308   
309   
310   
311   
312   
313   
314   
315   
316   
317   
318   
319   
320   
321   
322   
323   
324   
325   
326   
327   
328   
329   
330   
331   
332   
333   
334   
335   
336   
337   
338   
339   
340   
341   
342   
343   
344   
345   
346   
347   
348   
349   
350   
351   
352   
353   
354   
355   
356   
357   
358   
359   
360   
361   
362   
363   
364   
365   
366   
367   
368   
369   
370   
371   
372   
373   
374   
375   
376   
377 glDeleteVertexArrays(1, &VAO);  
378 glDeleteBuffers(1, &VBO);  
379 glDeleteBuffers(1, &EBO);  
380   
381   
382   
383 glDeleteProgram(shaderProgram);  
384   
385 glfwDestroyWindow(window);  
386 glfwTerminate();  
387   
388 return 0;  
389}  

ファイル: a/shader/frag_ambient.glsl

  1#version 450 core
  2 
  3in vec2 uVu;
  4out vec4 out_color;
  5 
  6uniform vec3 materialColor;
  7uniform float ambientIntensity;
  8 
  9void main() {
  10 vec3 ambient = materialColor * ambientIntensity;
  11 out_color = vec4(ambient, 1.0);
  12}

ファイル: a/shader/frag_directional.glsl

  1#version 450 core
  2 
  3in vec2 vUv;
  4in vec3 vNormal;
  5out vec4 out_color;
  6 
  7uniform vec3 materialColor;
  8uniform vec3 lightDirection;
  9uniform float lightIntensity;
  10 
  11void main() {
  12 vec3 normal = normalize(vNormal);
  13 
  14 float diffuseFactor = max(dot(normal, -normalize(lightDirection)), 0.0);
  15 vec3 diffuse = materialColor * diffuseFactor * lightIntensity;
  16 out_color = vec4(diffuse, 1.0);
  17}

ファイル: a/shader/vertex_directional.glsl

  1#version 450 core
  2 
  3layout(location = 0) in vec2 aPosition;
  4layout(location = 1) in vec2 aUv;
  5layout(location = 2) in vec3 aNormal;
  6 
  7out vec2 vUv;
  8out vec3 vNormal;
  9 
  10void main() {
  11 vUv = aUv;
  12 vNormal = aNormal;
  13 gl_Position = vec4(aPosition, 0.0, 1.0);
  14}