見出し画像

十九、テクスチャを重ね合わせる。


↑この画像と、
↑この画像を重ねます。

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

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


■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>

    <script src="webgl-utils.js"></script>
    <script src="webgl-debug.js"></script>
    <script src="sano-func.js"></script>

    <script src="dual-tex.js"></script>
<BR>
  </body>
</html>


■JavaScriptのコードです。↓

// 頂点シェーダのプログラム
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec2 a_TexCoord;\n' +
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  v_TexCoord = a_TexCoord;\n' +
  '}\n';


// フラグメントシェーダのプログラム
var FSHADER_SOURCE =
  '#ifdef GL_ES\n' +
  'precision mediump float;\n' +
  '#endif\n' +
  'uniform sampler2D u_Sampler0;\n' +
  'uniform sampler2D u_Sampler1;\n' +
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  vec4 color0 = texture2D(u_Sampler0, v_TexCoord);\n' +
  '  vec4 color1 = texture2D(u_Sampler1, v_TexCoord);\n' +
  '  gl_FragColor = color0 * color1;\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.0, 0.0, 1.0);

  // テクスチャを設定する
  if (!initTextures(gl, n)) {
    console.log('テクスチャの設定に失敗');
    return;
  }
}

function initVertexBuffers(gl) {
  var verticesTexCoords = new Float32Array([
    // 頂点座標、テクスチャ座標
    -0.5,  0.5,   0.0, 1.0,
    -0.5, -0.5,   0.0, 0.0,
     0.5,  0.5,   1.0, 1.0,
     0.5, -0.5,   1.0, 0.0,
  ]);
  var n = 4; // 頂点数

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

  // 頂点座標を設定する
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

  var FSIZE = verticesTexCoords.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, 2, gl.FLOAT, false, FSIZE * 4, 0);

  gl.enableVertexAttribArray(a_Position);  // バッファオブジェクトの割り当ての有効化

  // テクスチャ座標をa_TexCoordに割り当て、有効化する
  var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
  if (a_TexCoord < 0) {
    console.log('a_TexCoordの格納場所の取得に失敗');
    return -1;
  }
  gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);




  gl.enableVertexAttribArray(a_TexCoord);  // バッファオブジェクトの割り当ての有効化

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

  return n;
}

function initTextures(gl, n) {
  // テクスチャオブジェクトを作成する
  var texture0 = gl.createTexture(); 
  var texture1 = gl.createTexture();
  if (!texture0 || !texture1) {
    console.log('テクスチャオブジェクトの作成に失敗');
    return false;
  }

  // u_Sampler1とu_Sampler2の格納場所を取得する
  var u_Sampler0 = gl.getUniformLocation(gl.program, 'u_Sampler0');
  var u_Sampler1 = gl.getUniformLocation(gl.program, 'u_Sampler1');
  if (!u_Sampler0 || !u_Sampler1) {
    console.log('u_Samplerの格納場所の取得に失敗');
    return false;
  }

  // 画像オブジェクトを作成する
  var image0 = new Image();
  var image1 = new Image();
  if (!image0 || !image1) {
    console.log('画像オブジェクトの作成に失敗');
    return false;
  }
  // 画像の読み込み完了時のイベントハンドラを設定する
  image0.onload = function(){ loadTexture(gl, n, texture0, u_Sampler0, image0, 0); };
  image1.onload = function(){ loadTexture(gl, n, texture1, u_Sampler1, image1, 1); };
  // 画像を読み込む(ブラウザにお任せ)
  image0.src = 'kani.jpg';
  image1.src = 'kasaneru.gif';

  return true;
}

var g_texUnit0 = false, g_texUnit1 = false; // テクスチャユニットの準備が完了したかどうか?
function loadTexture(gl, n, texture, u_Sampler, image, texUnit) {
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);  // 画像のY軸を反転する
  // テクスチャユニットをアクティブにする
  if (texUnit == 0) {
    gl.activeTexture(gl.TEXTURE0);
    g_texUnit0 = true;
  } else {
    gl.activeTexture(gl.TEXTURE1);
    g_texUnit1 = true;
  }

  gl.bindTexture(gl.TEXTURE_2D, texture);   // テクスチャオブジェクトをバインドする

  // テクスチャパラメータを設定する
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  // テクスチャ画像を設定する
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  
  gl.uniform1i(u_Sampler, texUnit);   // サンプラにテクスチャユニットを設定する
  
  // Canvasをクリアする
  gl.clear(gl.COLOR_BUFFER_BIT);

  if (g_texUnit0 && g_texUnit1) {
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);   // 四角形を描画する
  }
}

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

■以下は私が検索する時に使うヤツです。(先程のExcelファイルに載っているので、読まなくて大丈夫です)

a_TexCoord は89行目に出てくる。

サンプラー(sampler)は、シェーダープログラム内で、テクスチャデータにアクセスするための特殊なユニフォーム変数です。
異なるテクスチャを、それぞれ別のサンプラーにバインドする為、変数を2つ用意する。

2つのテクスチャのピクセルカラーを取り出し、それらを掛け算して最終的なフラグメントカラーを決定しています。
color0のRとcolor1のRを掛け算する。(同様にG、B、Aも)
この掛け算をブレンディングというらしい。
2つの画像を掛け合わせた色や透明度を得る為に用いる。

今回は二つの画像を使う為、テクスチャオブジェクトも二つ用意する。
テクスチャオブジェクト→絵を入れる額縁のような物。

テクスチャユニットの準備が完了したかどうか?のフラグ。準備完了すると両方がtrueになる。
g_texUnit0 と g_texUnit1 を main 関数の外に出した理由は、これらのフラグが複数の関数で共有される必要があるためです。
具体的には、loadTexture 関数の内部でこれらのフラグが更新されますが、loadTexture 関数は main 関数から呼び出される initTextures 関数によって呼び出されます。もしこれらのフラグを main 関数の内部に定義すると、loadTexture 関数からアクセスできなくなります。

texUnit はこの関数の引数。
.oOo..oOo..oOo.

以上です。

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