Search code examples
webglwebgl2

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


I am trying to map a texture at a cube, but I came across two problems and one question

here is the code for initialize the cube

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};
}

and the code for the texture, I referenced this tutorial while I making this code

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 = "";
  }
}

....

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??

These are the pictures for above problem..

Before changing the angle

After changing the angle by pushing the arrow keys...

After changing the angle

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

By changing the texture coordinates I can see the 1, 2, 3, 4 on the cube but no matter how I editted the order of coordinates I still cannot see 5, 6 on the right face of the cube and the bottom face of the cube ... here is the texture image

The right face of the cube The bottom face of the cube

And the last question is that If I want to change the faces to be mapped, do I have to modify the texture coordinates arrays or indices array or vertices array?


Solution

  • 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??

    Textures are loaded asynchonously so you have about 3 choices

    1. Wait for the texture to load before starting

    2. Create a renderable texture, render continuously using a requestAnimationFrame loop, replace the texture contents it with the image when the image has loaded.

      since your render loop would have things rendering contiunously problem solved.

    3. Create a renderable texture, replace the texture contents it with the image when the image has loaded and render again

    It looks like you're currently doing #3 but you're not rendering again after the texture loads. Inside your loadTexture function call your render/draw function.

    If you want to do #1 you need to create the Image and set its onload outside the rest of your code. Something like

    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..

    Just checking first you turned on depth test and face culling

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

    But otherwise the texture coordinates don't match the vertex positions. There are 4 vertex positions per cube face but the code has 6 texture coordinates per cube face. They need to match, just 4 texture coordinates per face. Attributes in WebGL represent parallel arrays