目次
2 つのテクセルの最終フラグメント カラーを計算するためのカラー ベクトルのコンポーネント乗算
イベント応答関数を登録します:loadTexture()。最後のパラメータはテクスチャ ユニット番号です。
テクスチャを設定します:loadTexture()関数。この関数のコアコードは次のとおりです。
序文
WebGL は複数のテクスチャを同時に処理でき、テクスチャ ユニットはこの目的のために設計されています。このプログラム例では、2 つのテクスチャ画像を長方形上に重ねて貼り付けます。以下の図は、この例の実行効果を示しており、長方形上で 2 つのテクスチャ画像をブレンドする効果は次のとおりです。
以下の 2 つの画像は、サンプル プログラムで使用される 2 つのテクスチャ イメージを示しています。さまざまなテクスチャ イメージ形式を処理する WebGL の機能を示すために、この例では意図的に 2 つの異なるイメージ形式 (左側が jpg、右側が gif) を使用しています。
サンプルコード
// 顶点着色器
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() {
var canvas = document.getElementById('webgl');
var gl = getWebGLContext(canvas);
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
var n = initVertexBuffers(gl);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 配置纹理
if (!initTextures(gl, n)) {
console.log('Failed to intialize the texture.');
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();
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');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
gl.enableVertexAttribArray(a_Position); // Enable the assignment of the buffer object
var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
gl.enableVertexAttribArray(a_TexCoord); // Enable the buffer assignment
return n;
}
function initTextures(gl, n) {
// 创建纹理缓冲区对象
var texture0 = gl.createTexture();
var texture1 = gl.createTexture();
// 获取u_Sampler1和u_Sampler2的存储位置
var u_Sampler0 = gl.getUniformLocation(gl.program, 'u_Sampler0');
var u_Sampler1 = gl.getUniformLocation(gl.program, 'u_Sampler1');
var image0 = new Image();
var image1 = new Image();
// 注册事件响应函数,在图像加载完成后调用
image0.onload = function(){ loadTexture(gl, n, texture0, u_Sampler0, image0, 0); };
image1.onload = function(){ loadTexture(gl, n, texture1, u_Sampler1, image1, 1); };
// 告诉浏览器开始加载图像
image0.src = '../resources/sky.jpg';
image1.src = '../resources/circle.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);// 沿反转图像
// 激活纹理单元
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);
gl.clear(gl.COLOR_BUFFER_BIT);
// 图像加载是异步的,我们无法预测哪一张纹理被加载完成,只有两幅都加载好,程序才开始绘制
if (g_texUnit0 && g_texUnit1) {
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
}
}
まず、フラグメント シェーダを見てみましょう。この例では 2 つのテクスチャが使用されているため、2 つの均一変数を定義する必要があります。
次に、フラグメント シェーダーの main() 関数で、2 つのテクスチャからカラーを取得し、変数 color0 と color1 にそれぞれ保存します。
2 つのテクセルの最終フラグメント カラーを計算するためのカラー ベクトルのコンポーネント乗算
2 つのテクセルを使用して最終的なフラグメントの色 (gl_FragColor) を計算するには、いくつかの方法があります。サンプル プログラムでは、カラー ベクトルのコンポーネント乗算を使用します。以下に示すように、2 つのベクトルの対応するコンポーネントが新しいベクトルのコンポーネントとして乗算されます。これはわかりやすいですね。GLSL ES では、2 つの vec4 変数を単純に乗算するだけで目的を達成できます。
55行目と56行目では2つのテクスチャバッファオブジェクトを作成しており、変数名の末尾(テクスチャ0の「0」、テクスチャ1の「1」)がテクスチャユニット番号(テクスチャユニット0、テクスチャユニット1)に対応しています。 (行 68、69) と画像オブジェクト (行 70、71) も同様の命名方法を使用します。
イベント応答関数を登録します:loadTexture()。最後のパラメータはテクスチャ ユニット番号です。
ブラウザに画像をロードするようにリクエストします。
テクスチャを設定します:loadTexture()関数。この関数のコアコードは次のとおりです。
認識しなければならないのは
loadTexture() 関数では、読み込みプロセスが非同期であるため、どのテクスチャ イメージが最初に読み込まれるかを予測できません。プログラムは、両方のテクスチャ イメージの読み込みが完了した場合にのみ描画を開始します。この目的のために、2 つのグローバル変数 g_texUnit0 と g_texUnit1 を定義して、対応するテクスチャがロードされているかどうかを示します (81 行目)。
これらの変数は false に初期化されます(81 行目)。テクスチャがロードされると、onload イベントがトリガーされ、応答関数loadTexture () が呼び出されます。この関数はまず、テクスチャ ユニット番号 0 または 1 に基づいて g_texUnit0 または g_texUnit1 を true に割り当てます(85 行目)。つまり、この onload イベントをトリガーしたテクスチャ番号が 0 の場合、テクスチャ ユニット 0 番がアクティブになり、g_texUnit0 が true に設定され、1 の場合、テクスチャ ユニット 1 番がアクティブになり、g_texUnit0 が true に設定されます。 true に設定します。
次に、uniform変数にテクスチャユニット番号texUnitを代入します(99行目)。texUnit は gl.uniform1i() メソッドを通じてシェーダーに渡されることに注意してください。両方のテクスチャ イメージがロードされた後の WebGL システムの内部ステータスは次の図のようになります。
loadTexture() 関数の最後で、g_texUnit0 変数と g_texUnit1 変数がチェックされ、両方のイメージがロードされているかどうかが判断されます (103 行目)。その場合は、頂点シェーダーの実行を開始し、この記事の最初の図に示すように、グラフ上に重なる 2 つのテクスチャ レイヤーを描画します。