十八、テクスチャを繰り返し使う。
■サンプルページはコチラ↓
■このプログラムの解説は、次の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="tex-repeat.js"></script>
<BR>
↑この技法は、昔のドラクエの中のお城の壁のレンガなど、同じ画像を繰り返し表示することができます。<BR>
が、このサンプル作成時にレンガの様な画像の手持ちが無く、蟹味噌を使用しています。m(_ _)m
</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_Sampler;\n' +
'varying vec2 v_TexCoord;\n' +
'void main() {\n' +
' gl_FragColor = texture2D(u_Sampler, v_TexCoord);\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.3, 1.7,
-0.5, -0.5, -0.3, -0.2,
0.5, 0.5, 1.7, 1.7,
0.5, -0.5, 1.7, -0.2
]);
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;
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);
// テクスチャ座標を設定する
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 texture = gl.createTexture();
if (!texture) {
console.log('テクスチャオブジェクトの作成に失敗');
return false;
}
// u_Samplerの格納場所を取得する
var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
if (!u_Sampler) {
console.log('u_Samplerの格納場所の取得に失敗');
return false;
}
// 画像オブジェクトを作成する
var image = new Image();
if (!image) {
console.log('画像オブジェクトの作成に失敗');
return false;
}
// 画像の読み込み完了時のイベントハンドラを設定する
image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); };
// 画像を読み込む(ブラウザにお任せ)
image.src = 'kani.jpg';
return true;
}
function loadTexture(gl, n, texture, u_Sampler, image) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 画像のY軸を反転する
// テクスチャユニット0を有効にする
gl.activeTexture(gl.TEXTURE0);
// テクスチャオブジェクトをバインドする
gl.bindTexture(gl.TEXTURE_2D, texture);
// テクスチャパラメータを設定する
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// テクスチャ画像を設定する
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
// サンプラにテクスチャユニット0を設定する
gl.uniform1i(u_Sampler, 0);
// Canvasをクリアする
gl.clear(gl.COLOR_BUFFER_BIT);
// 四角形を描画する
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
}
■参考文献:WebGL+HTML5 3DCGプログラミング入門 (良い本ですが、絶版です。中古で購入される場合はCD-ROMがちゃんと付いているか確認して下さい)
■以下は私が検索する時に使うヤツです。(先程のExcelファイルに載っているので、読まなくて大丈夫です)
a_TexCoord は89行目に出てくる。
gl_FragColor は、フラグメントシェーダ内で定義される変数で、各ピクセルの色を決定します。
フラグメントシェーダの最後に、この変数に色を設定することによって、描画される最終的なピクセルの色を指定します。
texture2D は、GLSL ESの組み込み関数で vec4。
↑テクスチャのサンプリング関数で、指定したテクスチャの特定の座標から色を取得します。
サンプリング関数の動作↓
・シェーダにバインドされているテクスチャオブジェクトを参照します。
・テクスチャ内の座標を、引数として、サンプリング関数が受け取ります。
・上記の座標から色情報(RGBA)を取得し、vec4型のデータとして返します。
なお、サンプリングとは信号処理の用語で、連続信号から離散的なデータポイント(サンプル)を抽出するプロセスを指します。
u_Sampler は、シェーダに送られるサンプラ変数で、使用するテクスチャを示します。
サンプラ変数の役目↓
u_Sampler はサンプラ変数で、sampler2D型のユニフォーム変数です。
サンプラ変数は、シェーダープログラムにバインドされたテクスチャオブジェクト(データが入った容器)を参照します。
そして、テクスチャデータへのアクセスを提供します。
v_TexCoord は、テクスチャ座標を示す変数で、テクスチャ内のどの部分をサンプリングするかを指定します。
テクスチャが主役なので、背景に余計な色を付けない様にしよう。(メモ)
103行目
↑紫色の枠は、WebGLの座標。黒色の枠はテクスチャの座標。
テクスチャの座標の、(0, 0) から (1.0, 1.0) の範囲をはみ出した分が繰り返される感じ。
vertexTexCoordBuffer の内容を、バッファオブジェクトに書きこむ
75行目で書き込んだ情報を読み取る方法を指定している。
ここで読み取っているのは、テクスチャ座標ではなく、頂点座標。
75行目で書き込んだ情報を読み取る方法を指定している。
ここではテクスチャ座標を読み取っている。
読み取った情報を a_TexCoord へ格納。
9行目の、 v_TexCoord = a_TexCoord; でシェーダに渡される。
gl.createTexture()は、WebGLのAPIの一部で、事前に定義されたメソッドです。
new Image()はJavaScriptの組み込み関数で、HTML DOM APIの一部です。
この関数は、HTMLドキュメント内で使用する画像オブジェクトを作成します。
Imageオブジェクトは、画像を読み込んだり、設定したりするための便利なオブジェクトです。
.onloadはImageオブジェクトのプロパティ。
画像が読み込まれた時に呼び出される関数を指定します。
loadTexture()の前に、無名関数function()を置くのは、loadTexture()を即時実行させたくないため。
無名関数function()を置くことで、画像を完全に読み込んでから処理が進むようにしている。
JPGなどの画像フォーマットの原点は、左上であることが多いです。
しかし、WebGLのテクスチャの原点は、左下です。
なので、上下を反転させます。
gl.pixelStorei の最後の i の文字はINTEGER の i 。
gl.pixelStorei の第一引数は他に3種類ほどあるが、説明省略。
gl.UNPACK_FLIP_Y_WEBGL は、Y軸を反転させるかどうか。 (1だと反転、0だとそのまま)
通常はTEXTURE7まであるらしい。画像を重ねる場合などに使う。8層あれば十分でしょう、ということらしい。
”テクスチャユニット”はステッカーを貼る枠のような物。
gl.TEXTURE0は初期状態で”有効”になっている為、gl.TEXTURE0のみの場合はこの文は無くてもよい。
第一引数は、gl.TEXTURE_2D か gl.TEXTURE_CUBE_MAP 。松田先生の書籍ではgl.TEXTURE_2D (=2次元のテクスチャ)しか使わない。
第二引数は、テクスチャオブジェクト。104行目で生成。
テクスチャ座標が [0, 1] の範囲外である -0.3 や 1.7 などになっているため、テクスチャが自動的に繰り返されます。(繰り返しを明示しなくてよい)
書籍にはこのことが書かれていなかったので、理解するのにかなりの時間を要した。
シェーダープログラムのサンプラーユニフォーム変数に、値を設定する。
サンプラー(sampler)は、シェーダープログラム内で、テクスチャデータにアクセスするための特殊なユニフォーム変数です。
今は u_Sampler がサンプラーユニフォーム変数。17行目と111行目で定義。
0はテクスチャユニットの番号。テクスチャユニットは133行目。
このコマンドの役割は、シェーダー内のu_Sampler変数をテクスチャユニット0にバインドすることです。
これにより、シェーダープログラムがu_Samplerを参照する際に、テクスチャユニット0にバインドされているテクスチャが使用されます。
以上です。
頂戴したサポートは、レンタルサーバーの費用に充てさせて頂きます🙇 心より感謝いたします❤️