見出し画像

三十二、環境光 (WebGL)

■サンプルページはコチラ↓ (タッチパネル非対応です)

.oOo..oOo..oOo.
第三十一回の時は、拡散光のみ → 陰になる面は真っ黒。(下図)

陰になる部分が真っ黒になっている図。

今回。陰になる部分でも、環境光によって面が見えるようになった。

環境光を追加。

■このプログラムの解説は、次のExcelファイルをご覧下さい。

↑ 今回は、前回のプログラムに数行追加しただけなので、JavaScript の部分のみ掲載しています。

.oOo..oOo..oOo.
↓ HTMLのコードです。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>拡散光 + 環境光</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Canvasをサポートしているブラウザを使用してください。
    </canvas>

<p>キーボードの矢印キーを使って視点を左右に移動できます。</p>
<ul>
 <li>右矢印キー: 視点を右に移動</li>
 <li>左矢印キー: 視点を左に移動</li>
</ul>

 <!-- 説明図の挿入 -->
<br>
    <p>※このページの”視点の移動”とは下図の様な動きのことを指します。</p>
    <img src="setsumei.png" alt="説明図" width="400" />

<script src="webgl-utils.js"></script>
<script src="webgl-debug.js"></script>
<script src="sano-func.js"></script>
<script src="gyouretsu-8-1-6.js"></script>
    <script src="Ambient_Light.js"></script>
  </body>
</html>

↓ JavaScriptのコードです。


// 頂点シェーダのプログラム
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec4 a_Color;\n' +
  'attribute vec4 a_Normal;\n' +       // 法線
  'uniform mat4 u_MvpMatrix;\n' +
  'uniform vec3 u_DiffuseLight;\n' +   // 拡散光の色
  'uniform vec3 u_LightDirection;\n' + // 拡散光の向き(ワールド座標、正規化されている)
  'uniform vec3 u_AmbientLight;\n' +   // 環境光の色
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_Position = u_MvpMatrix * a_Position;\n' +
     // 法線の長さを1.0に
  '  vec3 normal = normalize(a_Normal.xyz);\n' +

     // 光の向きと法線と内積
  '  float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' +
     // 拡散反射による面の色を計算
  '  vec3 diffuse = u_DiffuseLight * a_Color.rgb * nDotL;\n' +
     // 環境反射による面の色を計算
  '  vec3 ambient = u_AmbientLight * a_Color.rgb;\n' +
     // 拡散反射による面の色と環境光反射よる面の色を加算
  '  v_Color = vec4(diffuse + ambient, a_Color.a);\n' + 
  '}\n';

// フラグメントシェーダのプログラム
var FSHADER_SOURCE =
  '#ifdef GL_ES\n' +
  'precision mediump float;\n' +
  '#endif\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_FragColor = v_Color;\n' +
  '}\n';

function main() {
  // Canvas要素を取得する
  var canvas = document.getElementById('webgl');

  // WebGL描画用のコンテキストを取得する
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('WebGLコンテキストの取得に失敗');
    return;
  }

  // シェーダを初期化する
  if (!initShaders2(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('シェーダの初期化に失敗');
    return;
  }

  // 頂点座標と色を設定する
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('頂点属性の設定に失敗');
    return;
  }

  // クリアカラーを設定し、デプステストを有効にする
  gl.clearColor(0, 0, 0, 1);
  gl.enable(gl.DEPTH_TEST);

  // uniform変数などの格納場所を取得する
  var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  var u_DiffuseLight = gl.getUniformLocation(gl.program, 'u_DiffuseLight');
  var u_LightDirection = gl.getUniformLocation(gl.program, 'u_LightDirection');
  var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
  if (!u_MvpMatrix || !u_DiffuseLight || !u_LightDirection || !u_AmbientLight) { 
    console.log('uniform変数の格納場所の取得に失敗');
    return;
  }

  // 光源の色(白)を設定する
  gl.uniform3f(u_DiffuseLight, 1.0, 1.0, 1.0);
  // 光源の方向(ワールド座標系)を設定する
  var lightDirection = new Vector3([1.0, 3.0, 4.0]);
  lightDirection.normalize();     // 正規化する
  gl.uniform3fv(u_LightDirection, lightDirection.elements);
  // 環境光を設定する
  gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);

  // ビュー投影行列を計算する
  var mvpMatrix = new Matrix4();   // モデルビュー投影行列

  var modelMatrix = new Matrix4(); // モデル行列
  var viewMatrix = new Matrix4();// ビュー行列
  var projMatrix = new Matrix4();  // 投影行列

window.onkeydown = function(ev){ keydown(ev, gl, n, modelMatrix, viewMatrix, projMatrix,mvpMatrix, u_MvpMatrix, canvas); };

  draw(gl, n, modelMatrix, viewMatrix, projMatrix, mvpMatrix, u_MvpMatrix, canvas);   // 描画する

}

function initVertexBuffers(gl) {
  // 立方体を生成する
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3
  // 座標
  var vertices = new Float32Array([
     1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0, // v0-v1-v2-v3 前
     1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0, // v0-v3-v4-v5 右
     1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0, // v0-v5-v6-v1 上
    -1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0, // v1-v6-v7-v2 左
    -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0, // v7-v4-v3-v2 下
     1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0  // v4-v7-v6-v5 奥
  ]);

  // 色
  var colors = new Float32Array([
    0.1, 0.8, 0.4,   0.1, 0.8, 0.4,   0.1, 0.8, 0.4,  0.1, 0.8, 0.4,     // v0-v1-v2-v3 前
    0.1, 0.8, 0.4,   0.1, 0.8, 0.4,   0.1, 0.8, 0.4,  0.1, 0.8, 0.4,     // v0-v3-v4-v5 右
    0.1, 0.8, 0.4,   0.1, 0.8, 0.4,   0.1, 0.8, 0.4,  0.1, 0.8, 0.4,     // v0-v5-v6-v1 上
    0.1, 0.8, 0.4,   0.1, 0.8, 0.4,   0.1, 0.8, 0.4,  0.1, 0.8, 0.4,     // v1-v6-v7-v2 左
    0.1, 0.8, 0.4,   0.1, 0.8, 0.4,   0.1, 0.8, 0.4,  0.1, 0.8, 0.4,     // v7-v4-v3-v2 下
    0.1, 0.8, 0.4,   0.1, 0.8, 0.4,   0.1, 0.8, 0.4,  0.1, 0.8, 0.4,     // v4-v7-v6-v5 奥
 ]);

  // 法線
  var normals = new Float32Array([
    0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 手前
    1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 右
    0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 上
   -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 左
    0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 下
    0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 奥
  ]);

  // インデックス
  var indices = new Uint8Array([
     0, 1, 2,   0, 2, 3,    // 前
     4, 5, 6,   4, 6, 7,    // 右
     8, 9,10,   8,10,11,    // 上
    12,13,14,  12,14,15,    // 左
    16,17,18,  16,18,19,    // 下
    20,21,22,  20,22,23     // 奥
 ]);

  // 頂点属性(座標、色、法線)をバッファに書き込む
  if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT)) return -1;
  if (!initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT)) return -1;
  if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;




  // インデックスをバッファオブジェクトに転送する
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log('バッファオブジェクトの作成に失敗');
    return false;
  }
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  return indices.length;
}

function initArrayBuffer (gl, attribute, data, num, type) {
  // バッファオブジェクトを作成する
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log('バッファオブジェクトの作成に失敗');
    return false;
  }
  // バッファオブジェクトにデータを書き込む
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
// attribute変数にバッファオブジェクトを割り当てる
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log(attribute + 'の格納場所の取得に失敗');
    return false;
  }
  gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
  // attribute変数へのバッファオブジェクトの割り当てを有効にする
  gl.enableVertexAttribArray(a_attribute);

  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  return true;
}

var g_EyeX = 2.0, g_EyeY = 2.0, g_EyeZ = 10.0; // 視点の位置

function keydown(ev, gl, n, modelMatrix, viewMatrix, projMatrix, mvpMatrix, u_MvpMatrix, canvas) {
    if(ev.keyCode == 39) { // 右矢印キーが押された
      g_EyeX += 0.1;
    } else 
    if (ev.keyCode == 37) { // 左矢印キーが押された
      g_EyeX -= 0.1;
    } else { return; } // 余分な描画を行わないようにする
    draw(gl, n, modelMatrix, viewMatrix, projMatrix, mvpMatrix, u_MvpMatrix, canvas);    
}


function draw(gl, n, modelMatrix, viewMatrix, projMatrix, mvpMatrix, u_MvpMatrix, canvas) {
  // ビュー行列、投影行列を計算する
modelMatrix.setTranslate(0, 0, 0);
viewMatrix.setLookAt(g_EyeX, g_EyeY, g_EyeZ, 0, 0, 0, 0, 1, 0);
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
  // モデルビュー投影行列を計算する
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);

  // モデルビュー投影行列をu_MvpMatrix変数に設定する
  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

//gl.clear(gl.COLOR_BUFFER_BIT);   // Canvasをクリアする  // カラーバッファとデプスバッファをクリアする
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  // 立方体を描画する
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

}

■参考文献:WebGL+HTML5 3DCGプログラミング入門
(この本は良書ですが、現在は絶版となっています。中古で購入する際は、付属のCD-ROMが欠品していないかご確認ください。CD-ROMにはサンプルコードが収録されており、それがないと学習の難易度が大幅に上がります)
.oOo..oOo..oOo.
■以下は私が検索する時に使うヤツです。(先程のExcelファイルに載っているので、読まなくて大丈夫です)

三十一 に対して、u_AmbientLight を追加。

環境光 * 面の色

最終的な面の色。ambient を単純に足しているだけ。(立方体全体が少し明るく見える)
→環境光がすべての方向から均一に立方体に当たるという現象を表現しています。

↓三十一の時 = 拡散光のみ → 陰になる面は真っ黒。

↓今回。陰になる部分でも、環境光によって面が見えるようになった。

環境光の色 (0.2, 0.2, 0.2) を代入。(0.2, 0.2, 0.2) では、かなり暗い灰色になるが、真っ暗ではないので、少し面が見えるようになる感じ。
.oOo..oOo..oOo.

今回は、ここまでです。

いいなと思ったら応援しよう!

佐野毅_プロダクトデザイナー+機械系エンジニア_SANO_Takeshi
頂戴したチップ(サポート)は、レンタルサーバーの費用に充てさせて頂きます🙇 心より感謝いたします❤️