WebGL入门教程(八)纹理映射

发表于2017-12-12
评论0 4.6k浏览

下面给大家介绍的可以说是这个系列最重要的部分,纹理映射。当传入几个顶点及其颜色后,虽然会对剩余的顶点进行插值,绘制出渐变的图像,但同要绘制的图像更加复杂时,就需要用到另一种方式——纹理映射,来确定每个点的颜色。

当需要绘制一个点的颜色时,不在是通过计算得到,而是去一张事先保存好的图像中,对应的坐标取纹理的颜色。纹理的坐标st(或者叫uv)范围是0-1。

示例:

/**
 * 纹理映射
 * xu.lidong@qq.com
 * */
var vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
void main(){
    gl_Position = a_Position;
    v_TexCoord = a_TexCoord;
}`;
var fragmentShaderSrc = `
precision mediump float;// 必须声明浮点数精度,否则会出错
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main(){
    gl_FragColor = texture2D(u_Sampler, v_TexCoord);
}`;
function main() {
    var canvas = document.getElementById("container");
    var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    initShader(gl);
    var n = initVertexBuffers(gl);
    initTextures(gl, n);
}
function initShader(gl) {
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vertexShaderSrc);
    gl.compileShader(vertexShader);
    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fragmentShaderSrc);
    gl.compileShader(fragmentShader);
    var shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);
    gl.useProgram(shaderProgram);
    gl.program = shaderProgram;
}
function initVertexBuffers(gl) {
    var verticesTexCoords = new Float32Array([
        // 顶点坐标 对应的纹理坐标
        -1.0, 1.0,  0.0, 1.0,
        -1.0, -1.0, 0.0, 0.0,
        1.0, 1.0,   1.0, 1.0,
        1.0, -1.0, 1.0, 0.0
    ]);
    var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
    var vertexColorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);
    var a_Position = gl.getAttribLocation(gl.program, "a_Position");
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
    gl.enableVertexAttribArray(a_Position);
    var a_TexCoord = gl.getAttribLocation(gl.program, "a_TexCoord");
    gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
    gl.enableVertexAttribArray(a_TexCoord);
    return verticesTexCoords.length/4;
}
function initTextures (gl, n) {
    var u_Sampler = gl.getUniformLocation(gl.program, "u_Sampler");
    var image = new Image();
    image.onload = function () {
        loadTexture(gl, n, u_Sampler, image);
    };
    image.src = "./sky.jpg";
}
function loadTexture(gl, n, u_Sampler, image) {
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);// 图像坐标与纹理坐标Y轴方向相反,需进行Y轴反转
    var texture = gl.createTexture();// 创建纹理
    gl.activeTexture(gl.TEXTURE0);// 激活0号纹理单元(0号是默认激活的纹理单元)
    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, image);// 配置纹理图像
    gl.uniform1i(u_Sampler, 0);// 将0号纹理传递给着色器
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
}

如图:

加载图片的方法由浏览器提供,是异步的,需要在加载完成之后的回调中进行绘制。

/**
 * 纹理叠加
 * xu.lidong@qq.com
 * */
var vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
void main(){
    gl_Position = a_Position;
    v_TexCoord = a_TexCoord;
}`;
var fragmentShaderSrc = `
precision mediump float;// 必须声明浮点数精度,否则会出错
uniform sampler2D u_Sampler;
uniform sampler2D u_Sampler1;
varying vec2 v_TexCoord;
void main(){
    vec4 color = texture2D(u_Sampler, v_TexCoord);
    vec4 color1 = texture2D(u_Sampler1, v_TexCoord);
    if (color1.a > 0.0) {
        gl_FragColor.r = color1.r;
        gl_FragColor.g = color1.g;
        gl_FragColor.b = color1.b;
        gl_FragColor.a = color1.a;
    } else {
        gl_FragColor.r = color.r;
        gl_FragColor.g = color.g;
        gl_FragColor.b = color.b;
        gl_FragColor.a = color.a;
    }
}`;
var g_LoadImage = false;
var g_LoadImage1 = false;
function main() {
    var canvas = document.getElementById("container");
    var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    initShader(gl);
    var n = initVertexBuffers(gl);
    initTextures(gl, n);
}
function initShader(gl) {
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vertexShaderSrc);
    gl.compileShader(vertexShader);
    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fragmentShaderSrc);
    gl.compileShader(fragmentShader);
    var shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);
    gl.useProgram(shaderProgram);
    gl.program = shaderProgram;
}
function initVertexBuffers(gl) {
    var verticesTexCoords = new Float32Array([
        // 顶点坐标 对应的纹理坐标
        -1.0, 1.0,  0.0, 1.0,
        -1.0, -1.0, 0.0, 0.0,
        1.0, 1.0,   1.0, 1.0,
        1.0, -1.0, 1.0, 0.0
    ]);
    var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
    var vertexColorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);
    var a_Position = gl.getAttribLocation(gl.program, "a_Position");
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
    gl.enableVertexAttribArray(a_Position);
    var a_TexCoord = gl.getAttribLocation(gl.program, "a_TexCoord");
    gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
    gl.enableVertexAttribArray(a_TexCoord);
    return verticesTexCoords.length/4;
}
function initTextures (gl, n) {
    var u_Sampler = gl.getUniformLocation(gl.program, "u_Sampler");
    var u_Sampler1 = gl.getUniformLocation(gl.program, "u_Sampler1");
    var image = new Image();
    var image1 = new Image();
    image.onload = function () {
        loadTexture(gl, n, u_Sampler, image, 0);
    };
    image1.onload = function () {
        loadTexture(gl, n, u_Sampler1, image1, 1);
    };
    image.src = "./sky.jpg";
    image1.src = "./girl.png";
}
function loadTexture(gl, n, u_Sampler, image, texUnit) {
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);// 图像坐标与纹理坐标Y轴方向相反,需进行Y轴反转
    var texture = gl.createTexture();// 创建纹理
    if (texUnit === 0) {
        gl.activeTexture(gl.TEXTURE0);// 激活0号纹理单元(0号是默认激活的纹理单元)
        g_LoadImage = true;
    } else {
        gl.activeTexture(gl.TEXTURE1);// 激活0号纹理单元(0号是默认激活的纹理单元)
        g_LoadImage1 = true;
    }
    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);// 纹理垂直填充方式
    var color = (texUnit === 0) ? gl.RGB: gl.RGBA;
    gl.texImage2D(gl.TEXTURE_2D, 0, color, color, gl.UNSIGNED_BYTE, image);// 配置纹理图像
    gl.uniform1i(u_Sampler, texUnit);
    if (g_LoadImage && g_LoadImage1) {
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
    }
}

如图:

背景是jpg格式,任务为png格式,在两个纹理颜色叠加时,优先使用人物的颜色。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引