如何在WebGL中使用多个纹理? [英] How to use multiple textures in WebGL?

查看:674
本文介绍了如何在WebGL中使用多个纹理?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在立方体上使用6种不同的纹理,每边一个,但找不到错误。这是我目前的代码:

I wanted to use 6 different textures on a cube, one per side, but can't find the mistake. Here's my current code:

   var texturen = new Array();
function initTexture(sFilename,texturen)  
{
  var anz = texturen.length;
  texturen[anz] = gl.createTexture();
  texturen[anz].image = new Image();
  texturen[anz].image.onload = function()    
  {
  gl.bindTexture(gl.TEXTURE_2D, texturen[anz]);
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
  gl.texImage2D
  (gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texturen[anz].image);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.bindTexture(gl.TEXTURE_2D, null);
  }
  texturen[anz].image.src = sFilename;
} 


var mvMatrix = mat4.create();
var mvMatrixStack = [];
var pMatrix = mat4.create();

function mvPushMatrix() {
    var copy = mat4.create();
    mat4.set(mvMatrix, copy);
    mvMatrixStack.push(copy);
}

function mvPopMatrix() {
    if (mvMatrixStack.length == 0) {
        throw "Invalid popMatrix!";
    }
    mvMatrix = mvMatrixStack.pop();
}


function setMatrixUniforms() {
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}


function degToRad(degrees) {
    return degrees * Math.PI / 180;
}

var cubeVertexPositionBuffer;
var cubeVertexTextureCoordBuffer;
var cubeVertexIndexBuffer;
var cubeVertexPositionBuffer1;
var cubeVertexTextureCoordBuffer1;
var cubeVertexIndexBuffer1;


function initBuffers() {
    cubeVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
    vertices = [
        // Front face
        -1.0, -1.0,  1.0,
         1.0, -1.0,  1.0,
         1.0,  1.0,  1.0,
        -1.0,  1.0,  1.0,

        // Back face
        -1.0, -1.0, -1.0,
        -1.0,  1.0, -1.0,
         1.0,  1.0, -1.0,
         1.0, -1.0, -1.0,

        // Top face
        -1.0,  1.0, -1.0,
        -1.0,  1.0,  1.0,
         1.0,  1.0,  1.0,
         1.0,  1.0, -1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    cubeVertexPositionBuffer.itemSize = 3;
    cubeVertexPositionBuffer.numItems = 12;

cubeVertexPositionBuffer1 = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer1);
    vertices = [
       // Bottom face
        -1.0, -1.0, -1.0,
         1.0, -1.0, -1.0,
         1.0, -1.0,  1.0,
        -1.0, -1.0,  1.0,

        // Right face
         1.0, -1.0, -1.0,
         1.0,  1.0, -1.0,
         1.0,  1.0,  1.0,
         1.0, -1.0,  1.0,

        // Left face
        -1.0, -1.0, -1.0,
        -1.0, -1.0,  1.0,
        -1.0,  1.0,  1.0,
        -1.0,  1.0, -1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    cubeVertexPositionBuffer1.itemSize = 3;
    cubeVertexPositionBuffer1.numItems = 12;


    cubeVertexTextureCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
    var textureCoords = [
      // Front face
      0.0, 0.0,
      1.0, 0.0,
      1.0, 1.0,
      0.0, 1.0,

      // Back face
      1.0, 0.0,
      1.0, 1.0,
      0.0, 1.0,
      0.0, 0.0,

      // Top face
      0.0, 1.0,
      0.0, 0.0,
      1.0, 0.0,
      1.0, 1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
    cubeVertexTextureCoordBuffer.itemSize = 2;
    cubeVertexTextureCoordBuffer.numItems = 12;



cubeVertexTextureCoordBuffer1 = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer1);
    var textureCoords = [
   // Bottom face
      1.0, 1.0,
      0.0, 1.0,
      0.0, 0.0,
      1.0, 0.0,

      // Right face
      1.0, 0.0,
      1.0, 1.0,
      0.0, 1.0,
      0.0, 0.0,

      // Left face
      0.0, 0.0,
      1.0, 0.0,
      1.0, 1.0,
      0.0, 1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
    cubeVertexTextureCoordBuffer1.itemSize = 2;
    cubeVertexTextureCoordBuffer1.numItems = 12;

cubeVertexIndexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
    var cubeVertexIndices = [
        0, 1, 2,      0, 2, 3,    // Front face
        4, 5, 6,      4, 6, 7,    // Back face
        8, 9, 10,     8, 10, 11,  // Top face
    ];
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
    cubeVertexIndexBuffer.itemSize = 1;
    cubeVertexIndexBuffer.numItems = 18;

cubeVertexIndexBuffer1 = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer1);
    var cubeVertexIndices = [
        12, 13, 14,   12, 14, 15, // Bottom face
        16, 17, 18,   16, 18, 19, // Right face
        20, 21, 22,   20, 22, 23  // Left face
    ];
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
    cubeVertexIndexBuffer1.itemSize = 1;
    cubeVertexIndexBuffer1.numItems = 18;



}


var xRot = 0;
var yRot = 0;
var zRot = 0;

function drawScene() {
    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

    mat4.identity(mvMatrix);

    mat4.translate(mvMatrix, [0.0, 0.0, -5.0]);

    mat4.rotate(mvMatrix, degToRad(xRot), [1, 0, 0]);
    mat4.rotate(mvMatrix, degToRad(yRot), [0, 1, 0]);
    mat4.rotate(mvMatrix, degToRad(zRot), [0, 0, 0]);
    setMatrixUniforms();

 gl.activeTexture(gl.TEXTURE0);
 gl.bindTexture(gl.TEXTURE_2D, texturen[0]); 
 gl.vertexAttribPointer
 (textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize,  gl.FLOAT, false, 0,  0);
   gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
  gl.vertexAttribPointer
  (shaderProgram.vertexPositionAttribute,cubeVertexPositionBuffer.itemSize, 
  gl.FLOAT, false, 0, 0);

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);

    gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);


 gl.activeTexture(gl.TEXTURE1);
 gl.bindTexture(gl.TEXTURE_2D, texturen[1]); 
 gl.vertexAttribPointer
 (textureCoordAttribute, cubeVertexTextureCoordBuffer1.itemSize,  gl.FLOAT, false, 0, 0);
 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer1);
 gl.vertexAttribPointer
 (shaderProgram.vertexPositionAttribute,cubeVertexPositionBuffer1.itemSize, 
  gl.FLOAT, false, 0, 0);


    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer1);

    gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer1.numItems, gl.UNSIGNED_SHORT, 0);


}

我把它分成两部分,试图用2种不同的照片进行测试。什么是cubeVertexIndexBuffers呢?

i just divided it into two parts, trying to make a test with 2 different pics. And what are the cubeVertexIndexBuffers for exactly?

推荐答案

首先,简短回答:用你的代码替换最后10行代码以下,我认为它应该有效。

First off, the short answer: Replace the last 10 lines of your code with the following and I think it should work.

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texturen[1]); 
gl.vertexAttribPointer(textureCoordAttribute, cubeVertexTextureCoordBuffer1.itemSize,  gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer1);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,cubeVertexPositionBuffer1.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

主要变化是:


  • activeTexture 仍然是 TEXTURE0 (你这里一次只使用一个纹理)

  • 使用 cubeVertexIndexBuffer 而不是 cubeVertexIndexBuffer1 ,其中包含超出范围的指标

  • activeTexture is still TEXTURE0 (you're only using one texture at a time here)
  • Using cubeVertexIndexBuffer instead of cubeVertexIndexBuffer1, which contained out-of-range indicies

为了更好地解释实际使用的索引,我将引用这个问题因为我不想在这里重复所有这些。

For a better explanation of what the indices are actually used for I'll refer you to this SO question as I'd rather not repeat all that here.

现在,回到更一般的答案。

Now, on to the more general answer.

有两种基本方法可以解决在不同面上使用不同纹理的问题。第一个也是更简单的方法就是完成你在这里做的事情:将对象渲染成碎片,为每个碎片绑定不同的纹理。虽然不是严格来说是实现效果的最有效方式,但它仍然是在游戏等高性能应用中处理它的最常见方式,因为它提供了很大的灵活性,特别是当您的材料比简单的漫反射纹理更复杂时。

There are two basic ways to handle the issue of using different textures on different faces. The first and simpler method is to do exactly what you are doing here: Render the object in pieces, binding a different texture for each piece. Though not strictly the most efficient way to accomplish the effect it is nevertheless the most common way to handle it in high-performance applications such as games simply because it offers a lot of flexibility, especially when your materials are more complex than a simple diffuse texture.

尽管如此,有一种简单的方法可以改善像你这样的代码的性能。顶点/索引不必分解为每个纹理的单独缓冲区。您可以将它们全部组合到一个缓冲区中,并使用不同的偏移量渲染它们,如下所示:

There is a simple way that you can improve the performance of code like yours for a case like this, though. The vertices/indices don't have to be broken into separate buffers per-texture. You could instead combine them all into a single buffer and render them with different offsets like so:

// On Init
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
var vertices = [
    // Vertex values for all 6 faces
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
var indices = [
    // Index values for all 6 faces
];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

// On Draw
// Setup the shader and uniforms and all that jazz

gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.vertexAttribPointer(// Blah blah blah...);

// Draw face 0
gl.bindTexture(gl.TEXTURE_2D, texture[0]);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

// Draw face 1
gl.bindTexture(gl.TEXTURE_2D, texture[1]);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 12);

// Draw face 2
gl.bindTexture(gl.TEXTURE_2D, texture[2]);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 24);

// .. And so on to face 5
// Draw face 2
gl.bindTexture(gl.TEXTURE_2D, texture[5]);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 60);

这里发生的是每次拨打 drawElements 仅绘制2个三角形(6个顶点,调用的第二个参数),但每个调用都会偏移到索引缓冲区中,以便它在不同的面上启动。 (调用的第四个参数,表示字节偏移量。每个索引都是Uint16,每个索引2个字节.12 ==索引[6]开始)这样所有绑定和设置只发生一次,并且每个绘制调用只需要改变实际需要的状态(纹理)。

What's going on here is that each call to drawElements only draws 2 triangles (6 vertices, the second parameter of the call), but each call is offset into the index buffer so that it starts on a different face. (fourth parameter of the call, which indicates a byte offset. Each index is a Uint16, so 2 bytes per index. 12 == "start on index[6]") This way all of the binding and setup only happens once, and each draw call only has to change the state that is actually necessary (the texture).

处理这个问题的另一种方法是更快但更难推广,将纹理数组绑定到着色器均匀,并使用另一个顶点属性索引到着色器中的数组。我不打算详细介绍这种方法的代码,但是一旦熟悉了WebGL着色器,它应该相对容易设置。如果您对此特定方法有进一步的疑问,最好在不同的SO问题中提出问题,以免超出此问题。

The other way to handle this, which is faster but harder to generalize, is to bind an array of textures to a shader uniform and use another vertex attribute to index into the array within the shader. I'm not going to detail the code for this approach but it should be relatively easy to set up once you're comfortable with WebGL shader use. If you have further questions about this specific approach it would be best to ask in a different SO question so as not to overload this one.

这篇关于如何在WebGL中使用多个纹理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆