和 WebGLBuffer 类似,不过 WebGLTexture 是存储传输纹理数据的,纹理数据通常来自一个 Image 对象。
一、写入数据
往着色器中传递纹理数据我们需要使用 WebGLTexture 这个对象,通过texImage2D这个方法可以把 Javascript 里的图像数据存到 WebGLTexture中,会自动存储放到片元着色器的第0个纹理单元。
大致步骤如下:
- 通过
createTexture创建 WebGLTexture; - 激活纹理单元(默认是第0和纹理单元,可以省略这步);
- 通过
bindTexture绑定 WebGLTexture; - 通过
texParameteri设置纹理裁剪和采样属性; - 通过
texImage2D指定二维图像,写入纹理;
在这里,gl.TEXTURE_2D和ARRAY_BUFFER有点类似,可以看作是一个临时的数据桥梁🌉或者数据转运卡车🚚一样,把Javascript环境下的的Image转达给WebGL的WebGLTexture,不同的是,在转运前要设置一些裁剪和采样属性。
WebGLTexture大多数情况存放的是图像数据(来自Image或者数组),其本质上是存放的是一个数据序列,因此也可以随意存放除了颜色数据以外的其它数据。纹理往往需要搭配纹理坐标来使用(需要注意纹理坐标系和顶点坐标系不一致),纹理坐标一般是顶点着色器插值生成传给片元着色器的,通过纹理坐标获取对应片元的颜色。
// 创建纹理对象
let texture = gl.createTexture();
// 绑定纹理,会默认绑定到WebGL的纹理单元0,在片元着色器中定义的sampler2D默认也是纹理单元0
// gl.activeTexture(gl.TEXTURE6); // 激活纹理到单元
gl.bindTexture(gl.TEXTURE_2D, texture);
// 设置纹理裁剪规则,有重复、镜像、按最大值、最小值处理
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// 将图像上传到纹理(image是JS中的Image对象,也可以是颜色数组)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
二、读取数据
当二维图像被传入WebGLTexture后,不需要像Buffer读取数据那样手动指定读取规则,纹理会数据会默认读取并存到到 WebGL 的第0个纹理单元在片元着色器中通过sampler2D类型的uniform变量就可以直接获取。同时,可以在着色程序运行中随意读取其中的数据。
WebGL 环境中,在片元着色器中至少有8个纹理单元,顶点着色器中可以是0个。
如果使用了超过8个纹理单元就应该调用gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)查看单元个数;
或者调用gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS)查看顶点着色器中可以用几个纹理单元;
超过 99% 的 GPU 硬件在顶点着色器中至少有4个纹理单元。
let fragmentShaderSource = `
precision mediump float; // 中等精度
uniform sampler2D u_image; // 其默认值是0,表示第0个纹理单元,会直接从webGLTexture中取数据
varying vec2 v_texCoord; // 从顶点着色器传入的纹理坐标
void main() {
gl_FragColor = texture2D(u_image, v_texCoord);
}
`;
这是WebGL 系列的入门文章,免费订阅,如有帮助请点赞收藏,纰漏之处欢迎指正!
也欢迎关注公众号交流知识哇😄