十三、在矩形表面贴上图像

142 阅读2分钟

一、介绍

1、定义

纹理映射:
将一张图像映射到一个几何图形的表面

作用:
根据纹理图像,为之前光栅化后的每个片元涂上合适的颜色;

纹素:
组成纹理图像的像素,每一个纹素的颜色都使用RGB或RGBA格式编码

2、纹理坐标

纹理图像上的坐标,二维
原点坐标为(0,0),右上为(1.0, 1.0)

二、关键代码

#顶点着色器
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +
    'attribute vec2 a_TexCoord;\n' +
    'varying vec2 v_TexCoord;\n' +
    'void main() {\n' +
    '  gl_Position = a_Position;\n' +
    '  v_TexCoord = a_TexCoord;\n' +
    '}\n';
    
#片元着色器
var FSHADER_SOURCE =
    '#ifdef GL_ES\n' +
    'precision mediump float;\n' +
    '#endif\n' +
    #专用于纹理对象的数据类型,包括sampler2D、samplerCube
    #sampler2D:绑定到gl.TEXTURE_2D上的纹理数据类型
    #samplerCube:绑定到gl.TEXTURE_CUBE_MAP上的纹理数据类型
    'uniform sampler2D u_Sampler;\n' +
    'varying vec2 v_TexCoord;\n' +
    'void main() {\n' +
    #从纹理图像上获取味素颜色
    #u_Sampler:纹理单元编号
    #v_TexCoord:纹理坐标
    '  gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
    '}\n';

function main() {
    ···
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
    }
    var n = initVertexBuffers(gl);
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    #配置纹理
    if (!initTextures(gl, n)) {
        console.log('Failed to intialize the texture.');
        return;
    }
}

function initVertexBuffers(gl) {
    #存储顶点坐标及纹理坐标
    var verticesTexCoords = 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,
    ]);
    var n = 4; 

    #(1)创建缓冲区对象
    var vertexTexCoordBuffer = gl.createBuffer();
    #(2)将缓冲区绑定到目标
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
    #(3)向缓冲区对象中写入数据
    gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

    var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    #(4)将缓冲区对象分配给a_Position变量
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
    #(5)连接a_Position变量与分配给它的缓冲区对象
    gl.enableVertexAttribArray(a_Position); 

    var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
    #(4)将缓冲区对象分配给a_TexCoord变量
    gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
    #(5)连接a_TexCoord变量与分配给它的缓冲区对象
    gl.enableVertexAttribArray(a_TexCoord); 

    return n;
}
#配置和加载纹理
function initTextures(gl, n) {
    # 创建纹理对象,用于管理WebGL纹理,以存储纹理图像
    var texture = gl.createTexture();  

    # 获取u_Sampler的存储位置
    # u_Sampler为取样器,用于接收纹理图像
    var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');

    var image = new Image();  
    if (!image) {
        console.log('Failed to create the image object');
        return false;
    }
    #出于安全,WebGL不允许使用跨域纹理图像
    image.crossOrigin = 'Anonymous';
    image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); };
    image.src = '../resources/sky.jpg';

    return true;
}
#配置纹理供WebGL使用
function loadTexture(gl, n, texture, u_Sampler, image) {
    #(1)对纹理图像进行Y轴反转
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); 
    #(2)开启0号纹理单元
    gl.activeTexture(gl.TEXTURE0);
    #(3)向target绑定纹理对象
    # 包括开启纹理对象及绑定纹理和纹理单元
    gl.bindTexture(gl.TEXTURE_2D, texture);
    #(4)配置纹理参数
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    #(5)配置纹理图像
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
    #(6)将0号纹理传递给着色器中的取样器变量
    gl.uniform1i(u_Sampler, 0);

    gl.clear(gl.COLOR_BUFFER_BIT);   
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // Draw the rectangle
}


注:

1、顶点着色器和片元着色器在图像加载完成后执行;
在顶点着色器中为每个顶点指定纹理坐标
然后在片元着色器中根据每个片元的纹理坐标从纹理图像中抽取纹素颜色

内置方法说明

1、 gl.createBuffer()

用于创建缓冲区对象

gl.deleteBuffer(buffer)
删除缓冲区对象