七、纹理
1.纹理坐标
纹理坐标也称为st坐标。如下所示

在webgl⾥需要通过纹理坐标和图形顶点坐标的映射关系来确定贴图

2.添加图片步骤

2.1 定义sampler2D数据
在片元着色器中定义sampler2D类型的数据,并且是uniform变量,赋值给gl_FragColor
- 注意要设置精度
glsl
precision lowp float;
uniform sampler2D uSampler;
varying vec2 vTex;
void main() {
gl_FragColor = texture2D(uSampler,vTex);
}vec4 texture2D(sampler2D sampler, vec2 coord)
sampler:纹理单元编号coord:纹理坐标
2.2 获取sampler2D数据
js
const uSampler = gl.getUniformLocation(program, "uSampler");2.3 创建Image对象
创建Image对象,并指定图像路径
js
const img = new Image();
img.src = "./assets/kpl.png";2.4 处理纹理对象
处理纹理对象需要在image对象的onload`事件里
1.创建纹理对象
js
const texture = gl.createTexture();- 另外可以通过
gl.deleteTexture(textrue)来删除纹理对象。
2.翻转图片的y轴
js
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);3.开启一个纹理单元
- 纹理单元的格式为
gl.TEXTURE数字
js
gl.activeTexture(gl.TEXTURE0);4.绑定纹理对象
js
gl.bindTexture(type, texture);type参数有以下两种:gl.TEXTURE_2D: 二维纹理gl.TEXTURE_CUBE_MAP: 立方体纹理
texture:纹理对象
5.处理放大缩小的逻辑以及横向纵向的平铺方式
js
gl.texParameteri(type, pname, param);- type:纹理类型
gl.TEXTURE_2D: 二维纹理gl.TEXTURE_CUBE_MAP: 立方体纹理
- pname:
gl.TEXTURE_MAG_FILTER:放⼤gl.TEXTURE_MIN_FILTER:缩小gl.TEXTURE_WRAP_S:横向(水平填充)gl.TEXTURE_WRAP_T:纵向(垂直填充)
- param:
- 赋值给
gl.TEXTURE_MAG_FILTER(放⼤ )和gl.TEXTURE_MIN_FILTER(缩小)gl.NEAREST:使用像素颜色值gl.LINEAR:使用四周的加权平均值
- 赋值给
gl.TEXTURE_WRAP_S(水平填充)和gl.TEXTURE_WRAP_T(垂直填充)gl.REPEAT:平铺重复gl.MIRRORED_REPEAT:镜像对称gl.CLAMP_TO_EDGE:边缘延伸
- 赋值给
6.配置纹理图像
js
gl.texImage2D(type, level, internalformat, format,dataType, image);- type:纹理类型
gl.TEXTURE_2D: 二维纹理gl.TEXTURE_CUBE_MAP: 立方体纹理
- level:为0即可
- internalformat:图像的内部格式
gl.RGBgl.RGBAgl.ALPHAgl.LUMINANCE:使用物体表面的 红绿蓝 分量的加权平均值来计算gl.LUMINANCE_ALPHA
- format:纹理的内部格式,必须和 internalformat 相同
- dataType:纹理数据的数据类型
gl.UNSIGNED_BYTEgl.UNSIGNED_SHORT_5_6_5gl.UNSIGNED_SHORT_4_4_4_4gl.UNSIGNED_SHORT_5_5_5_1
- image:图片对象
7.给uniform类型的sampler2D传递数据
js
gl.uniform1i(uSampler, 0);9.绘制
js
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);2.5 案例
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aTex;
varying vec2 vTex;
void main() {
gl_Position = aPosition;
vTex = vec2(aTex.x,aTex.y);
}
`;
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
uniform sampler2D uSampler;
varying vec2 vTex;
void main() {
gl_FragColor = texture2D(uSampler,vTex);
}
`;
const program = initShader(gl,VERTEX_SHADER_SOURCE,FRAGMENT_SHADER_SOURCE);
const aPosition = gl.getAttribLocation(program, "aPosition");
const aTex = gl.getAttribLocation(program, "aTex");
const uSampler = gl.getUniformLocation(program, "uSampler");
// prettier-ignore
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();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
const BYTES = points.BYTES_PER_ELEMENT;
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.src = "./assets/kpl.png";
img.onload = () => {
// 创建纹理对象
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);
};3.添加多重背景图
将上述方法封装成函数,并且通过Promise.all()绘制多个背景图
3.1 封装函数
js
/**
*
* @param {string} url 图片地址
* @param {location} location sampler2D纹理存储位置
* @param {number} index 纹理单元
* @returns
*/
const getImage = (url, location, index) => {
return new Promise((reslove, reject) => {
const img = new Image();
// prettier-ignore
img.onload = () => {
// 创建纹理对象
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);
reslove()
};
img.src = url;
});
};
Promise.all([
getImage("./assets/kpl.png", uSampler, 0),
getImage("./assets/lufi.png", uSampler1, 1),
]).then(() => {
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
});3.2 案例
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aTex;
varying vec2 vTex;
void main() {
gl_Position = aPosition;
vTex = vec2(aTex.x,aTex.y);
}
`;
const FRAGMENT_SHADER_SOURCE = `
precision highp float;
uniform sampler2D uSampler;
uniform sampler2D uSampler1;
varying vec2 vTex;
void main() {
vec4 c1=texture2D(uSampler,vTex);
vec4 c2=texture2D(uSampler1,vTex);
gl_FragColor = c1*c2;
}
`;
const program = initShader(gl,VERTEX_SHADER_SOURCE,FRAGMENT_SHADER_SOURCE);
const aPosition = gl.getAttribLocation(program, "aPosition");
const aTex = gl.getAttribLocation(program, "aTex");
const uSampler = gl.getUniformLocation(program, "uSampler");
const uSampler1 = gl.getUniformLocation(program, "uSampler1");
// prettier-ignore
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();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
const BYTES = points.BYTES_PER_ELEMENT;
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 getImage = (url, location, index) => {
return new Promise((reslove, reject) => {
const img = new Image();
// prettier-ignore
img.onload = () => {
// 创建纹理对象
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);
reslove()
};
img.src = url;
});
};
Promise.all([
getImage("./assets/kpl.png", uSampler, 0),
getImage("./assets/lufi.png", uSampler1, 1),
]).then(() => {
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
});