Skip to content

七、纹理

1.纹理坐标

纹理坐标也称为st坐标。如下所示

image-20231008113026314

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

image-20231008113123705

2.添加图片步骤

image-20231008120131254

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.RGB
    • gl.RGBA
    • gl.ALPHA
    • gl.LUMINANCE:使用物体表面的 红绿蓝 分量的加权平均值来计算
    • gl.LUMINANCE_ALPHA
  • format:纹理的内部格式,必须和 internalformat 相同
  • dataType:纹理数据的数据类型
    • gl.UNSIGNED_BYTE
    • gl.UNSIGNED_SHORT_5_6_5
    • gl.UNSIGNED_SHORT_4_4_4_4
    • gl.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);
});

MIT Licensed