WebGL uses multiple textures simultaneously

Table of contents

Preface

Edit

Sample code

Component multiplication of color vectors to calculate the final fragment color of two texels

Register event response function: loadTexture(), the last parameter is the texture unit number.

Request the browser to load an image:

Configure texture: loadTexture() function. The core code of this function is as follows: 

have to be aware of is


Preface

WebGL can handle multiple textures at the same time, and the texture unit is designed for this purpose. This example program overlaps and pastes two texture images on a rectangle. The figure below shows the running effect of this example. The blending effect of two texture images on a rectangle is as follows.

The two images below show two texture images used in the sample program. In order to illustrate WebGL's ability to handle different texture image formats, this example deliberately uses two different formats of images (jpg on the left, gif on the right). 

Sample code

// 顶点着色器
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);  
  }
}

First, let's take a look at the fragment shader. In this example, two textures are used, so two uniform variables are needed to be defined.

Then, in the main() function of the fragment shader, we take the colors from the two textures and store them in the variables color0 and color1 respectively.

Component multiplication of color vectors to calculate the final fragment color of two texels

There are several possible ways to calculate the final fragment color (gl_FragColor) using two texels. The example program uses component multiplication of color vectors - corresponding components in two vectors are multiplied as components of a new vector , as shown below. This is easy to understand. In GLSL ES, you only need to simply multiply two vec4 variables to achieve the goal. 

Lines 55 and 56 create two texture buffer objects. The suffixes of the variable names ("0" in texture0 and "1" in texture1) correspond to the texture unit numbers (texture unit 0 and texture unit 1). In addition, uniform variables (lines 68, 69) and image objects (lines 70, 71) also use similar naming methods.

Register event response function: loadTexture(), the last parameter is the texture unit number.

Request the browser to load an image:

Configure texture: loadTexture() function. The core code of this function is as follows: 

have to be aware of is

In the loadTexture() function, we cannot predict which texture image will be loaded first because the loading process is asynchronous. The program will only start drawing when both texture images have finished loading. To this end, we define two global variables g_texUnit0 and g_texUnit1 to indicate whether the corresponding texture is loaded (line 81). 

These variables are initialized to false (line 81). When any texture is loaded, the onload event is triggered and the response function loadTexture () is called. The function first assigns g_texUnit0 or g_texUnit1 to true based on texture unit number 0 or 1 (line 85). In other words, if the texture number that triggers this onload event is 0, then texture unit No. 0 is activated and g_texUnit0 is set to true; if it is 1, then texture unit No. 1 is activated and g_texUnit0 is set to true. Set to true .

Next, the texture unit number texUnit is assigned to the uniform variable (line 99). Note that texUnit is passed into the shader through the gl.uniform1i() method. After both texture images are loaded, the internal status of the WebGL system is as shown in the figure below.

At the end of the loadTexture() function, the g_texUnit0 and g_texUnit1 variables are checked to determine whether both images have been loaded (line 103). If so, start executing the vertex shader and draw two layers of textures overlapping on the graph, as shown in the first picture of this article. 

Guess you like

Origin blog.csdn.net/dabaooooq/article/details/132776421
Recommended