Catalyst

WebGL 笔记(3)纹理

🎯 目标:贴纹理

在平面上贴纹理。

1. 读取纹理

参考资料:MDN 的 加载纹理

因为只画一次,所以绘图函数写在了加载图像的回调后,保证设置完后再绘制纹理。

function loadTexture(gl, shaderProgram, imageSrc) {
  let texture = gl.createTexture();
  let image = new Image();
  image.src = imageSrc;
  //加载完图像后执行的函数
  image.onload = function () {
    //绑定纹理
    gl.bindTexture(gl.TEXTURE_2D, texture);
    //指定二维纹理贴图,详细级别。颜色组件,数据格式,数据类型,数据源
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    //设置纹理在放大和缩小时候采用的采样方案。
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(
      gl.TEXTURE_2D,
      gl.TEXTURE_MIN_FILTER,
      gl.LINEAR_MIPMAP_NEAREST
    );
    //生成 mipmap
    gl.generateMipmap(gl.TEXTURE_2D);
    //绑定纹理
    gl.activeTexture(gl.TEXTURE0); //TEXTURE0 默认激活,注释掉也行
    //传入纹理数据到着色器
    gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);
    //绘制
    gl.useProgram(shaderProgram);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  };
  return texture;
}

2. 修改着色器

因为是逐像素传数据,所以先用attribute型接受数据,然后传到varying中以传入片段着色器中。 在片段着色器中使用sampler2D类型承载GL_TEXTURE对象。texture2D获取纹理对应坐标上的颜色值。

const vertexSource = `
attribute vec2 aPosition;
attribute vec2 aTexCoord;
varying lowp vec2 vTexCoord;
void main(){
    vTexCoord = aTexCoord;
    gl_Position=vec4(aPosition,0.0,1.0);
}
`;

const fragmentSource = `
varying lowp vec2 vTexCoord;
uniform sampler2D uSampler;
void main(){
      gl_FragColor = texture2D(uSampler,vTexCoord);
  }
`;

3. 纹理映射

指定图形上显示的纹理的坐标,按照点的顺序来。

2020.09.13:这里正方形的点只写xy是因为上面的着色器里已经把z设定了。半年过去自己都忘记了 😅。

//正方形的点
const vertices = [-0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5];
//纹理对应坐标
const tex = [0, 0, 0, 1, 1, 0, 1, 1];

上次写的一次性输入,这次分别绑定缓冲:

function simpleBindBuffer(gl, shaderProgram, name, data, size) {
  const buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
  const index = gl.getAttribLocation(shaderProgram, name);
  gl.vertexAttribPointer(index, size, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(index);
  return buffer;
}

//设置缓冲
simpleBindBuffer(gl, shaderProgram, "aPosition", vertices, 2);
simpleBindBuffer(gl, shaderProgram, "aTexCoord", tex, 2);

这样就能在平面上贴上纹理了。

4. 错误

generateMipmap: The base level of the texture does not have power-of-two dimensions.

webgl 1.0 只能给图片边长为 2 的幂的图片生成 mipmap, webgl 2.0就没有这个问题。

使用 webgl 2.0 需要 const gl = canvas.getContext("webgl2")