纹理映射问题以及如何在 WebGL 中更改纹理映射位置

Texture mapping problems and How to cahnge the texture mapping location in WebGL

我正在尝试在立方体上映射纹理,但我遇到了两个问题和一个问题

这里是初始化立方体的代码

function init_cube(gl, n)
  {
      const vertices = new Float32Array([   // Vertex coordinates
        1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0,  
        // v0-v1-v2-v3 front, Pass
        1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0,  
        // v0-v3-v4-v5 right, No
        1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0,  
        // v0-v5-v6-v1 up, Pass
       -1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0,  
       // v1-v6-v7-v2 left, Pass
       -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0,  
       // v7-v4-v3-v2 down, No
        1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0
      // v4-v7-v6-v5 back, Pass
     ]);

    const indices = new Uint8Array([       // Indices of the vertices
      0, 1, 2,   0, 2, 3,    // front
      4, 5, 6,   4, 6, 7,    // right
      8, 9,10,   8,10,11,    // up
     12,13,14,  12,14,15,    // left
     16,17,18,  16,18,19,    // down
     20,21,22,  20,22,23     // back
   ]);


    const vao = gl.createVertexArray();
    gl.bindVertexArray(vao);
    const indexBuffer = gl.createBuffer();

    if(!indexBuffer)
    {
      console.log("Failed to create an index buffer");
      return;
    }

    if(!initArrayBuffer(gl, vertices, 3, gl.FLOAT, loc_aPosition))
    {
      console.log("Failed to initialize an array buffer for the position");
      return;
    }


    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);


    init_texture(gl);

    gl.bindVertexArray(null);

    return {vao, n:indices.length};
}

和纹理的代码,我在制作这段代码时参考了这个tutorial

function init_texture(gl)
{  
    const UVCoord = new Float32Array
    (
      [
        //First Pass
        0, 0.75,
        0.25, 0.75,
        0.25, 0.5,
        0, 0.5,
        0, 0.75,
        0.25, 0.5,
        //Second Pass
        0.25, 0.75,
        0.5, 0.5,
        0.5, 0.75,
        0.25, 0.75,
        0.25, 0.5,
        0.5, 0.5,
        //Third Pass
        0.5, 0.75,
        0.75, 0.75,
        0.75, 0.5,
        0.5, 0.5,
        0.5, 0.75,
        0.75, 0.5,
        //Fourth Pass
        1, 0.5,
        0.75, 0.75,
        1, 0.75,
        1, 0.5,
        0.75, 0.5,
        0.75, 0.75,
        //Sixth No
        0.75, 0.25,
        0.5, 0.5,
        0.5, 0.25,
        0.75, 0.25,
        0.75, 0.5,
        0.5, 0.5,
        //Fifth No 
        0.75, 0.75,
        0.5, 1,
        0.5, 0.75,
        0.75, 0.75,
        0.75, 1,
        0.5, 1
      ]
    );

    let UVCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, UVCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, UVCoord, gl.STATIC_DRAW);


    gl.vertexAttribPointer(loc_UVCoord, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(loc_UVCoord);

    let texture = gl.createTexture();
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
      new Uint8Array([0, 0, 255, 255]));
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);


    let image = new Image();
    let url = "https://live.staticflickr.com/65535/49093406911_7d185dba0e_b.jpg";
    requestCORSIfNotSameOrigin(image, url);
    image.src = url;
    image.onload = function() 
    {
      loadTexture(gl, texture, image);
    }
}

function loadTexture(gl, texture, image)
{
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  gl.generateMipmap(gl.TEXTURE_2D);
}

....

function requestCORSIfNotSameOrigin(img, url) //https://webgl2fundamentals.org/webgl/lessons/webgl-cors-permission.html
{
  if ((new URL(url)).origin !== window.location.origin) 
  {
    img.crossOrigin = "";
  }
}

....

第一个问题是main函数没有加载纹理这是因为loadTexure函数是在main函数完成后才开始的,所以有没有第一次加载纹理的方法绘制立方体??

这些是上述问题的图片..

通过按箭头键更改角度后...

第二个问题是立方体右侧和底部的纹理应用不正确..

通过更改纹理坐标,我可以看到立方体上的 1、2、3、4,但是无论我如何编辑坐标顺序,我仍然看不到立方体右面上的 5、6 和立方体的底面...这是纹理 image

最后一个问题是,如果我想改变要映射的面,我是否必须修改纹理坐标数组或索引数组或顶点数组?

The First problem is that the texture is not loaded at the main function this is because the loadTexure function get started after the main function is finished so, is there anyway to load the texture at the first time of drawing the cube??

纹理是异步加载的,因此您有大约 3 个选择

  1. 等待纹理加载后再开始

  2. 创建可渲染的纹理,使用 requestAnimationFrame 循环连续渲染,加载图像后用图像替换纹理内容。

    因为你的渲染循环可以解决连续渲染的问题。

  3. 创建一个可渲染的纹理,当图像加载并再次渲染时用图像替换纹理内容

看起来您目前正在执行#3,但您没有在加载纹理后再次渲染。在你的 loadTexture 函数中调用你的 render/draw 函数。

如果您想执行#1,您需要创建 Image 并将其 onload 设置在其余代码之外。像

const img = new Image();
img.onload = initRestOfCode;
img.src = urlForImage;

function initRestOfCode() { ...

The second problem is that the texture is not applied properly at the right side of cube and the bottom..

先检查一下你是否开启了深度测试和面部剔除

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);

但是纹理坐标与顶点位置不匹配。每个立方体面有 4 个顶点位置,但代码每个立方体面有 6 个纹理坐标。他们需要匹配,每个面只有 4 个纹理坐标。 WebGL 中的属性表示并行数组