1. 纹理坐标(st坐标)简介
ST纹理坐标(也称为UV坐标)是一种二维坐标系统,用于在三维模型的表面上精确地定位二维纹理图像。这种坐标系统通常将纹理的左下角映射到(0,0),而右上角映射到(1,1)。
- S坐标(U坐标):通常对应纹理图像的水平方向,即纹理的宽度。
- T坐标(V坐标):通常对应纹理图像的垂直方向,即纹理的高度。
1.1 纹理坐标的工作原理
- 纹理坐标的范围通常是从0.0到1.0,其中(0.0, 0.0)代表纹理的左下角,而(1.0, 1.0)代表右上角。
- 在WebGL中,纹理坐标系统的t轴(垂直轴)与传统图像文件的y轴方向相反,这意味着当你在WebGL中使用纹理时,通常需要翻转图像的Y轴以确保正确的映射。
- 这可以通过设置gl.UNPACK_FLIP_Y_WEBGL为1来实现。
2. 基本步骤
2.1 创建纹理对象
- 使用gl.createTexture()创建一个新的纹理对象。
- 可以通过 gl.deleteTexture(textrue)来删除纹理对象。
const texture = gl.createTexture();
2.2 翻转图片Y轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
2.3 开启纹理单元
gl.activeTexture(gl.TEXTURE0)
2.4绑定纹理对象
使用gl.bindTexture(type, texture)将纹理对象绑定到纹理单元上。type 参数有以下两种:
- gI.TEXTURE_2D:二维纹理
- gI.TEXTURE_CUBE_MAP:立方体纹理
gl.bindTexture(gl.TEXTURE_2D, texture);
2.5 配置纹理参数
设置纹理的过滤方式(gl.TEXTURE_MIN_FILTER和gl.TEXTURE_MAG_FILTER)、包裹方式(gl.TEXTURE_WRAP_S和gl.TEXTURE_WRAP_T)等参数。
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);
2.6 上传纹理图像数据
使用gl.texImage2D(type, level,internalformat, format,dataType, image)将图像数据上传到纹理对象中。
2.6.1 type参数:
- gI.TEXTURE_2D:二维纹理
- gI.TEXTURE_CUBE_MAP:立方体纹理
2.6.2 level参数:
- 默认为0
2.6.3 internalformat参数
- gI.RGB
- gI.RGBA
- gI.ALPHA
- gI.LUMINANCE 使用物体表面的红绿蓝 分量的加权平均值来计算
- gI.LUMINANCE ALPHA
2.6.4 format参数
- format 纹理的内部格式,必须和internalformat 相同
2.6.5 dataType参数
- 9I.UNSIGNED_BYTE
- gI.UNSIGNED_SHORT_5_6_5
- gI.UNSIGNED_SHORT_4_4_4_4
- gI.UNSIGNED_SHORT_5_5_5_1
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
3. 实例代码
以下是一个简单的WebGL示例,展示了如何给一个简单的四边形添加背景图:
/** @type {HTMLCanvasElement} */const ctx = document.getElementById('canvas')const gl = ctx.getContext('webgl')// 顶点着色器源码const vertexShaderSource = `attribute vec4 aPosition;attribute vec4 aTex;varying vec2 vTex;void main() {gl_Position = aPosition;vTex = vec2(aTex.x, aTex.y);}`// 片源着色器源码const fragmentShaderSource = `precision lowp float;uniform sampler2D uSampler;varying vec2 vTex;void main() {gl_FragColor = texture2D(uSampler, vTex);}`const program = initShader(gl, vertexShaderSource, fragmentShaderSource);const aPosition = gl.getAttribLocation(program, 'aPosition');const aTex = gl.getAttribLocation(program, 'aTex');const uSampler = gl.getUniformLocation(program, 'uSampler');//赋值const points = 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 buffer = gl.createBuffer();const BYTES = points.BYTES_PER_ELEMENT; // 偏移字节gl.bindBuffer(gl.ARRAY_BUFFER, buffer);gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 4, 0);gl.enableVertexAttribArray(aPosition);gl.vertexAttribPointer(aTex, 2, gl.FLOAT, false, BYTES * 4, BYTES * 2);gl.enableVertexAttribArray(aTex);const img = new Image();img.onload = function () {// 创建纹理对象并加载图片const texture = gl.createTexture();// 翻转图片Y轴gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);// 开启一个单元纹理gl.activeTexture(gl.TEXTURE0)// 绑定纹理对象gl.bindTexture(gl.TEXTURE_2D, texture);// 配置纹理参数gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);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.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);gl.uniform1i(uSampler, 0);gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);}img.src = './images/img2.png';
4.效果如下
5. 添加多张背景图
- 添加几张图,就定义几个sampler2D
// 片源着色器源码
const fragmentShaderSource = `precision lowp float;uniform sampler2D uSampler;uniform sampler2D uSampler1;varying vec2 vTex;void main() {vec4 c1 = texture2D(uSampler, vTex);vec4 c2 = texture2D(uSampler1, vTex);texture2D(uSampler1, vTex);gl_FragColor = c1 * c2;}`
- 将创建纹理的过程封装方法
// 封装函数function getImage(location, url, index) {// Promisereturn new Promise(resolve => {const img = new Image();img.onload = function () {// 创建纹理对象并加载图片const texture = gl.createTexture();// 翻转图片Y轴gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);// 开启一个单元纹理gl.activeTexture(gl[`TEXTURE${index}`])// 绑定纹理对象gl.bindTexture(gl.TEXTURE_2D, texture);// 配置纹理参数gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);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.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);gl.uniform1i(location, index);resolve();}img.src = url;});}Promise.all([getImage(uSampler, "./images/img.png", 0), getImage(uSampler1, "./images/img2.png", 1)]).then(() => {gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);})
效果如下