見出し画像

二十、WebGLのビューの設定。

■サンプルページはコチラ↓

■WebGLの座標系。(三次元)

図1‗WebGLの座標系。XYZ。

↓WebGLの初期状態の視点は、(0, 0, 0)にあり、向きは-z方向。

図2‗WebGLの初期の視点と方向。

↓今回のプログラムでは、視点 (0.25, 0.25, 0.25) から 点 (0, 0, 0) を見ています。

図3‗視点。

.oOo..oOo..oOo.
■前述の視点や方向以外の、点や方向を見ていきましょう。
・WebGLなどの3DCGでは、カメラ(※1)の上方向も指定します。また、カメラがどの点を見ているか(※2)も指定します。(図4)

※1 便宜上、”カメラ"という表現にしました。
※2 ”注視点”というそうです。

図4_カメラの上方向、注視点。

カメラの上方向は、座標ではなく、ベクトルで指定します。

.oOo..oOo..oOo.
■このプログラムで使用した変数の説明図。
・カメラの視点 → (eyeX, eyeY, eyeZ)
・注視点 → (atX, atY, atZ)

図5‗(eyeX, eyeY, eyeZ)、(atX, atY, atZ)

・カメラの大まかな上方向 → (upX, upY, upZ)
↓"カメラの上方向"はベクトルなので、下図のupX, upY, upZ には、WebGLの原点を起点とした方向を示す数値を指定する。
(upX, upY, upZ に入れる数値は、カメラの位置が起点ではない。)

図6‗(upX, upY, upZ)

視点 (eyeX, eyeY, eyeZ)、注視点 (atX, atY, atZ)、上方向 (upX, upY, upZ) の三種類のデータを計算して、一つの行列にまとめてシェーダに渡します。

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

.oOo..oOo..oOo.
■htmlのコードです。↓

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>WebGLのビューの設定</title>
  </head>

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

    <script src="webgl-utils.js"></script>
    <script src="webgl-debug.js"></script>
    <script src="sano-func.js"></script>
    <script src="gyouretsu-7.js"></script>
    <script src="View-Triangle.js"></script>
  </body>
</html>


■JavaScriptのコードです。↓


// 頂点シェーダのプログラム
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec4 a_Color;\n' +
  'uniform mat4 u_ViewMatrix;\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_Position = u_ViewMatrix * a_Position;\n' +
  '  v_Color = a_Color;\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;
  }

  // Canvasをクリアする色を設定する
  gl.clearColor(0, 0, 0, 1);

  // u_ViewMatrix変数の格納場所を取得する
  var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
  if (!u_ViewMatrix) { 
    console.log('u_ViewMatrixの格納場所の取得に失敗');
    return;
  }

  // 視点の位置、注視点、上方向を設定する
  var viewMatrix = new Matrix4();
  viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0);

  // ビュー行列をuniform変数に設定する
  gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);

  // Canvasをクリアする
  gl.clear(gl.COLOR_BUFFER_BIT);

  // 三角形を描画する
  gl.drawArrays(gl.TRIANGLES, 0, n);
}

function initVertexBuffers(gl) {
  var verticesColors = new Float32Array([
    // 頂点座標(X, Y, Z)、 色(RGBA)
-0.41, -0.26,  -0.4,  0.1,  0.1,  0.3, //一番奥。ハンブラビみたいな色。
0.0, 0.67,  -0.4,  0.3,  0.5,  0.9,
0.0, 0.0,  -0.4,  0.1,  0.1,  0.3, 
0.41, -0.26, -0.4, 0.1,  0.1,  0.3,
0.0, 0.67,  -0.4,  0.3,  0.5,  0.9,
0.0, 0.0,  -0.4,  0.1,  0.1,  0.3, 
   
-0.41, -0.26,  -0.2,  0.6,  0.9,  0.7, //真ん中。クィン・マンサみたいな色。
0.0, 0.67,  -0.2,  0.8,  0.7,  0.5,
0.0, 0.0,  -0.2,  0.6,  0.9,  0.7, 
0.41, -0.26,  -0.2, 0.6,  0.9,  0.7,
0.0, 0.67,  -0.2,  0.8,  0.7,  0.5,
0.0, 0.0,  -0.2,  0.6,  0.9,  0.7, 

-0.41, -0.26, 0.0, 0.4,  0.2,  0.3,  //一番手前。シーマ専用機みたいな色。
0.0, 0.67, 0.0,  0.9,  0.9,  0.5,
0.0, 0.0, 0.0,  0.4,  0.2,  0.3, 
0.41, -0.26, 0.0, 0.4,  0.2,  0.3,
0.0, 0.67, 0.0,  0.9,  0.9,  0.5,
0.0, 0.0, 0.0,  0.4,  0.2,  0.3,
  ]);
  var n = 18;

  // バッファオブジェクトを作成する
  var vertexColorbuffer = gl.createBuffer();  
  if (!vertexColorbuffer) {
    console.log('バッファオブジェクトの作成に失敗');
    return -1;
  }

  // 頂点の座標と色をバッファオブジェクトに書き込む
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorbuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

  var FSIZE = verticesColors.BYTES_PER_ELEMENT;
  // a_Positionにバッファオブジェクトを割り当て、有効化する
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if(a_Position < 0) {
    console.log('a_Positionの格納場所の取得に失敗');
    return -1;
  }
  gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
  gl.enableVertexAttribArray(a_Position);
  // a_Colorにバッファオブジェクトを割り当て、有効化する
  var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
  if(a_Color < 0) {
    console.log('a_Colorの格納場所の取得に失敗');
    return -1;
  }
  gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
  gl.enableVertexAttribArray(a_Color);

  // バッファオブジェクトのバインドを解除する
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  return n;
}

■参考文献:WebGL+HTML5 3DCGプログラミング入門
(良い本ですが、絶版です。中古で購入される場合はCD-ROMがちゃんと付いているか確認して下さい)

■以下は私が検索する時に使うヤツです。このまま読むと意味が分からないと思います。この記事の中盤にあるExcelファイルをご覧下さい。

u_ViewMatrix はビュー行列。これによりビューの角度が変わる。

"gyouretsu-7.js"を参照。

viewMatrix.elements は、Matrix4.prototype.setLookAt の中で作られる。

vertexColorbufferという頂点バッファオブジェクトを、現在のバッファとしてバインドすることを意味します。
gl.ARRAY_BUFFERに、verticesColorsの情報を書き込む。

a_Position がシェーダー内のどの「位置」に存在しているかを探し、その位置(インデックス)を返します。
この位置は、後でバッファデータをこの属性変数に関連付けるために必要です。
var a_Positionにはこのインデックスが入る。
そのインデックスを使ってバッファデータをシェーダープログラムにバインドすることができます。
バッファデータは、vertexColorbuffer。vertexColorbufferの中身はverticesColors(頂点座標と色)。

さっきは位置を入力したので、次は色。

書籍では、注視点(atX, atY, atZ) と書かれている。

(注視点) - (視点) 。カメラの視点から注視点への方向(ベクトル)を計算している。
つまり、カメラの向き。

カメラの向きとカメラの上方向の外積を計算することにより、カメラの右方向を計算している。

カメラの右方向とカメラの向きの外積を計算し、カメラの上方向を計算している。
upX, upY, upZ はカメラが上を向いている大まかな方向を示すベクトルであり、カメラの座標系における正確な上方向ベクトル(ux, uy, uz)とは必ずしも一致しません。
upX, upY, upZ は、カメラの「初期上方向」を表す目安でしかなく、これがカメラの視線ベクトルや右方向ベクトルと正確に直交する保証はありません。
そのため、視線ベクトル(f)と右方向ベクトル(s)が確定した後、これらと直交する形でカメラの正確な上方向ベクトル(ux, uy, uz)を再計算する必要があります。

fにマイナスをかけるのは、視線ベクトルは通常「負のZ軸」に沿って定義されることが多いためです。
これは、物体が「負のZ軸」方向にあることが多いためです。

後で、3D座標の行列と掛け算するので、カメラの視点を原点に移動させる。

.oOo..oOo..oOo.

以上です。

頂戴したサポートは、レンタルサーバーの費用に充てさせて頂きます🙇 心より感謝いたします❤️