webgl纹理贴图的坑

636 阅读1分钟

最近在按照网上教程学习webgl,当做到纹理贴图的时候,发现在图片应该显示的区域上没有显示图片,取而代之的一片黑色,如下图:

屏幕截图 2022-10-10 234612.png

(这里我把背景设置为蓝色,便于让异常区域与画布背景区分)

我当时纹理部分的代码如下:

function initTexture() {
    gl.activeTexture(gl.TEXTURE0);
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);

    //纹理图片上下反转,必须在gl.texImage2D之前执行
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

    //image 对象
    const image = new Image();
    image.onload = function () {
      showMap();
    };
    image.src = "../../images/test.jpg";

    //贴图
    function showMap() {
      //配置纹理图像
      gl.texImage2D(
        gl.TEXTURE_2D,
        0,
        gl.RGB,
        gl.RGB,
        gl.UNSIGNED_BYTE,
        image
      )

      //配置纹理参数
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

      const u_Sampler = gl.getUniformLocation(gl.program, "u_Sampler");
      gl.uniform1i(u_Sampler, 0);

      //渲染
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, sourceSize);
    }
}

这也是网上大部分教程的做法。

在折磨一下午后,我终于发现问题出在纹理参数的设置上。webgl默认图像源的尺寸为2的n次幂,比如宽为256、高为256的图像可以正常显示,但宽为256、高为300的图像不能正常显示。为此,需要在原来的基础上添加如下两句:

//配置纹理参数
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

改完后渲染结果:

屏幕截图 2022-10-10 235541.png

附上demo代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
    <!-- glMatrix-0.9.6.min.js -->
    <script src="https://searchcode.com/codesearch/raw/11610755"></script>
    <script>
      let gl;
      const vertexString = `
			attribute vec4 a_Position;
    	attribute vec2 a_Pin;
    	varying vec2 v_Pin;
    	void main(){
      	gl_Position = a_Position;
      	v_Pin = a_Pin;
    	}
        `;
      const fragmentString = `
			precision mediump float;
			uniform sampler2D u_Sampler;
			varying vec2 v_Pin;
			void main(){
				gl_FragColor = texture2D(u_Sampler, v_Pin);
			}
        `;

      //数据源
      const source = 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,
      ]);
      const FSIZE = source.BYTES_PER_ELEMENT;
      //元素字节数
      const elementBytes = source.BYTES_PER_ELEMENT;
      //系列尺寸
      const posSize = 2;
      const pinSize = 2;
      //类目尺寸
      const categorySize = posSize + pinSize;
      //类目字节数
      const categoryBytes = categorySize * elementBytes;
      //系列字节索引位置
      const posByteIndex = 0;
      const pinByteIndex = posSize * elementBytes;
      //顶点总数
      const sourceSize = source.length / categorySize;

      function init() {
        initgl();
        initShader();
        initBuffer();
        initTexture();
      }

      function initgl() {
        let glCanvas = document.getElementById("gl");
        gl = glCanvas.getContext("webgl");
        gl.viewport(0, 0, glCanvas.clientWidth, glCanvas.clientHeight);
      }

      function initShader() {
        let vsShader = gl.createShader(gl.VERTEX_SHADER);
        let fsShader = gl.createShader(gl.FRAGMENT_SHADER);

        gl.shaderSource(vsShader, vertexString);
        gl.shaderSource(fsShader, fragmentString);

        gl.compileShader(vsShader);
        gl.compileShader(fsShader);

        let program = gl.createProgram();
        gl.attachShader(program, vsShader);
        gl.attachShader(program, fsShader);

        gl.linkProgram(program);
        gl.useProgram(program);

        gl.program = program;
      }

      function initBuffer() {
        const sourceBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, sourceBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, source, gl.STATIC_DRAW);

        const a_Position = gl.getAttribLocation(gl.program, "a_Position");
        gl.vertexAttribPointer(
          a_Position,
          posSize,
          gl.FLOAT,
          false,
          categoryBytes,
          posByteIndex
        );
        gl.enableVertexAttribArray(a_Position);

        const a_Pin = gl.getAttribLocation(gl.program, "a_Pin");
        gl.vertexAttribPointer(
          a_Pin,
          pinSize,
          gl.FLOAT,
          false,
          categoryBytes,
          pinByteIndex
        );
        gl.enableVertexAttribArray(a_Pin);
      }

      function initTexture() {
        //纹理单元
        gl.activeTexture(gl.TEXTURE0);

        //纹理对象
        const texture = gl.createTexture();

        //把纹理对象装进纹理单元里
        gl.bindTexture(gl.TEXTURE_2D, texture);

        //纹理图片上下反转,必须在gl.texImage2D之前执行
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

        //image 对象
        const image = new Image();
        image.onload = function () {
          showMap();
        };
        // 用自己的图替换,注意图片的格式要是jpg
        image.src = "../../images/test.jpg";

        //贴图
        function showMap() {
          //配置纹理图像
          gl.texImage2D(
            gl.TEXTURE_2D,
            0,
            gl.RGB,
            gl.RGB,
            gl.UNSIGNED_BYTE,
            image
          )

          //配置纹理参数
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

           

          //获取u_Sampler
          const u_Sampler = gl.getUniformLocation(gl.program, "u_Sampler");
          //将0号纹理分配给着色器,0 是纹理单元编号
          gl.uniform1i(u_Sampler, 0);

          //渲染
          render();
        }

        function render() {
          // gl.clearColor(0, 0, 0, 1.0)
          gl.clear(gl.COLOR_BUFFER_BIT);
          gl.drawArrays(gl.TRIANGLE_STRIP, 0, sourceSize);
        }
      }
    </script>
  </head>

  <body onload="init()">
    <canvas
      id="gl"
      width="500"
      height="500"
      style="background-color: blue"
    ></canvas>
  </body>
</html>