十九、テクスチャを重ね合わせる。
■サンプルページはコチラ↓
■このプログラムの解説は、次の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.
以上です。
頂戴したサポートは、レンタルサーバーの費用に充てさせて頂きます🙇 心より感謝いたします❤️