WebGL は複数のテクスチャを同時に使用します

目次

序文

編集

サンプルコード

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 つのテクスチャ レイヤーを描画します。 

おすすめ

転載: blog.csdn.net/dabaooooq/article/details/132776421