Skip to content

纹理映射详解

纹理映射是3D图形渲染中的一项关键技术,它通过将二维图像(纹理)映射到三维物体表面,使物体看起来更加真实和详细。本文将详细介绍纹理映射的基本概念、如何在WebGL中加载纹理,以及如何将纹理坐标传递给着色器,最后通过一个完整的实例展示如何实现纹理映射。

一、纹理基础

(一)纹理的概念

纹理(Texture)是一张二维图像,通常用于覆盖在3D模型的表面上,以增加细节和真实感。纹理可以是任何图像,例如照片、图案或程序生成的图像。纹理映射的过程就是将纹理图像的像素(称为纹理元素或texel)映射到3D模型的表面上。

(二)纹理的作用

  1. 增加细节:纹理可以为3D模型添加细节,如砖墙的纹理、木材的纹理等。
  2. 提高真实感:通过纹理映射,可以使3D模型看起来更加真实,增强视觉效果。
  3. 优化性能:相比于使用复杂的几何模型,纹理映射可以在不增加几何复杂度的情况下增加视觉细节。

二、加载纹理

在WebGL中,加载纹理并将其应用到3D物体表面需要以下几个步骤:

(一)创建纹理对象

首先,需要创建一个纹理对象并获取其ID。

javascript
const texture = gl.createTexture();

(二)绑定纹理

将纹理绑定到指定的纹理目标(如GL_TEXTURE_2D)。

javascript
gl.bindTexture(gl.TEXTURE_2D, texture);

(三)设置纹理参数

配置纹理的过滤和环绕方式。这些参数决定了纹理在放大、缩小或超出边界时的行为。

javascript
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

(四)加载纹理数据

使用texImage2D将图像数据加载到纹理中。通常,纹理数据来自一个HTML <img> 元素。

javascript
const image = new Image();
image.onload = function() {
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    gl.generateMipmap(gl.TEXTURE_2D);
};
image.src = '/files/site/text.png';

三、纹理坐标

纹理坐标(Texture Coordinates)用于在3D模型上正确映射2D纹理图像。它们定义了纹理图片的哪一部分应该映射到一个3D物体的某个顶点上。

(一)纹理坐标的表示

纹理坐标通常使用归一化坐标系(Normalized Coordinates),即(s, t)或(u, v),取值范围通常是[0,1]。

  • (0,0)表示纹理的左下角。
  • (1,0)表示纹理的右下角。
  • (0,1)表示纹理的左上角。
  • (1,1)表示纹理的右上角。

(二)纹理坐标与顶点关联

在定义3D模型的顶点数据时,需要同时定义每个顶点的纹理坐标。例如,一个四边形的顶点和纹理坐标可以定义如下:

javascript
const vertices = [
    -0.5, -0.5, 0.0, 0.0, 0.0,  // 左下角
     0.5, -0.5, 0.0, 1.0, 0.0,  // 右下角
     0.5,  0.5, 0.0, 1.0, 1.0,  // 右上角
    -0.5,  0.5, 0.0, 0.0, 1.0   // 左上角
];

(三)将纹理坐标传递给着色器

在顶点着色器中,将纹理坐标传递给片段着色器:

glsl
attribute vec2 a_texCoord;
varying vec2 v_texCoord;

void main() {
    v_texCoord = a_texCoord;
    gl_Position = ...; // 其他顶点变换
}

在片段着色器中,使用纹理坐标从纹理中采样颜色:

glsl
precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_texture;

void main() {
    gl_FragColor = texture2D(u_texture, v_texCoord);
}

四、完整的WebGL纹理映射实例

以下是一个完整的WebGL示例,展示如何加载纹理并将其映射到一个矩形上:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebGL Texture Mapping Example</title>
</head>
<body>
    <canvas id="webglCanvas" width="800" height="600"></canvas>
    <script>
        const canvas = document.getElementById('webglCanvas');
        const gl = canvas.getContext('webgl');

        if (!gl) {
            alert('Your browser does not support WebGL');
        }

        // 顶点着色器代码
        const vertexShaderSource = `
            attribute vec4 a_position;
            attribute vec2 a_texCoord;
            varying vec2 v_texCoord;
            uniform mat4 u_modelViewProjectionMatrix;
            void main() {
                v_texCoord = a_texCoord;
                gl_Position = u_modelViewProjectionMatrix * a_position;
            }
        `;

        // 片段着色器代码
        const fragmentShaderSource = `
            precision mediump float;
            varying vec2 v_texCoord;
            uniform sampler2D u_texture;
            void main() {
                gl_FragColor = texture2D(u_texture, v_texCoord);
            }
        `;

        // 创建顶点着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vertexShaderSource);
        gl.compileShader(vertexShader);

        // 创建片元着色器
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, fragmentShaderSource);
        gl.compileShader(fragmentShader);

        // 创建程序
        const program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);

        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
        }

        // 使用程序
        gl.useProgram(program);

        // 定义顶点数据
        const vertices = [
            -0.5, -0.5, 0.0, 0.0, 0.0,  // 左下角
             0.5, -0.5, 0.0, 1.0, 0.0,  // 右下角
             0.5,  0.5, 0.0, 1.0, 1.0,  // 右上角
            -0.5,  0.5, 0.0, 0.0, 1.0   // 左上角
        ];

        // 创建顶点缓冲区对象
        const vertexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

        // 获取attribute变量的位置
        const positionLocation = gl.getAttribLocation(program, 'a_position');
        const texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');

        // 绑定缓冲区到attribute变量
        gl.enableVertexAttribArray(positionLocation);
        gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 20, 0);

        gl.enableVertexAttribArray(texCoordLocation);
        gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 20, 12);

        // 创建纹理对象
        const texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);

        // 设置纹理参数
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

        // 加载纹理数据
        const image = new Image();
        image.onload = function() {
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            gl.generateMipmap(gl.TEXTURE_2D);

            // 清空画布
            gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置背景颜色为黑色
            gl.clear(gl.COLOR_BUFFER_BIT);

            // 绘制矩形
            gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
        };
        image.src = '/files/site/text.png'; // 确保路径正确
    </script>
</body>
</html>

代码解析

  1. 顶点数据:定义了矩形的四个顶点及其纹理坐标。
  2. 顶点着色器:将顶点位置和纹理坐标传递到片段着色器。
  3. 片段着色器:使用纹理坐标从纹理中采样颜色。
  4. 纹理加载:通过Image对象加载纹理图像,并将其绑定到纹理对象。
  5. 绘制命令:使用gl.drawArrays绘制矩形。

通过这些步骤,你可以将纹理正确映射到3D物体表面,从而实现更丰富的视觉效果。

Theme by threelab