返回

贴图和 Mipmap:打造您的 3D 世界

前端

前言

欢迎来到 WebGL 教程的第 27 课!在这一课中,我们将踏上激动人心的 3D 图形之旅,探索贴图和 Mipmap 的奥秘。贴图能够赋予 3D 模型逼真的细节和纹理,而 Mipmap 则是提高图像质量和性能的关键技术。准备好进入贴图和 Mipmap 的奇妙世界了吗?

贴图:给 3D 世界增添细节和真实感

贴图是将图像或纹理应用于 3D 模型表面的技术。它可以极大地增强模型的细节和真实感,让虚拟世界变得更加栩栩如生。贴图的种类繁多,从简单的颜色贴图到复杂的纹理贴图,应有尽有。

Mipmap:优化图像质量和提高性能

Mipmap 是一种图像优化技术,可以有效提高纹理贴图的质量和性能。它通过创建一系列逐渐缩小的图像来实现这一目的,每个较小的图像都称为 Mipmap 等级。当物体离相机较远时,WebGL 会自动使用较低级别的 Mipmap,从而减少内存占用和提高渲染速度。

贴图与 Mipmap 的原理

贴图和 Mipmap 的原理都与纹理映射密切相关。纹理映射是将纹理贴图应用于 3D 模型的过程,它通过计算纹理坐标并将这些坐标映射到纹理图像上来实现。Mipmap 则是在纹理映射的基础上,通过创建不同级别的 Mipmap 来实现图像优化的。

实践应用:让您的 3D 世界栩栩如生

为了更好地理解贴图和 Mipmap,让我们通过一个示例代码来实践一下。在这个示例中,我们将创建一个带有纹理贴图的立方体,并使用 Mipmap 来优化图像质量和性能。

// 创建 WebGL 上下文
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

// 创建顶点着色器和片段着色器
const vertexShaderSource = `
    attribute vec3 a_position;
    attribute vec2 a_texCoord;

    uniform mat4 u_modelViewMatrix;
    uniform mat4 u_projectionMatrix;

    varying vec2 v_texCoord;

    void main() {
        v_texCoord = a_texCoord;
        gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(a_position, 1.0);
    }
`;

const fragmentShaderSource = `
    precision mediump float;

    uniform sampler2D u_texture;

    varying vec2 v_texCoord;

    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);

// 获取顶点属性和统一变量的位置
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const texCoordAttributeLocation = gl.getAttribLocation(program, 'a_texCoord');

const modelViewMatrixUniformLocation = gl.getUniformLocation(program, 'u_modelViewMatrix');
const projectionMatrixUniformLocation = gl.getUniformLocation(program, 'u_projectionMatrix');
const textureUniformLocation = gl.getUniformLocation(program, 'u_texture');

// 创建缓冲区对象
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

const positions = [
    -1.0, -1.0, 1.0,
    1.0, -1.0, 1.0,
    1.0, 1.0, 1.0,
    -1.0, 1.0, 1.0,

    -1.0, -1.0, -1.0,
    -1.0, 1.0, -1.0,
    1.0, 1.0, -1.0,
    1.0, -1.0, -1.0,

    -1.0, 1.0, -1.0,
    -1.0, 1.0, 1.0,
    1.0, 1.0, 1.0,
    1.0, 1.0, -1.0,

    -1.0, -1.0, -1.0,
    1.0, -1.0, -1.0,
    1.0, -1.0, 1.0,
    -1.0, -1.0, 1.0,

    1.0, -1.0, -1.0,
    1.0, 1.0, -1.0,
    1.0, 1.0, 1.0,
    1.0, -1.0, 1.0,

    -1.0, -1.0, -1.0,
    -1.0, -1.0, 1.0,
    -1.0, 1.0, 1.0,
    -1.0, 1.0, -1.0
];

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);

const texCoords = [
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0
];

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);

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

const image = new Image();
image.onload = () => {
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

    gl.generateMipmap(gl.TEXTURE_2D);

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

    // 启用深度测试
    gl.enable(gl.DEPTH_TEST);

    // 清空颜色缓冲区和深度缓冲区
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clearDepth(1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // 设置视口
    gl.viewport(0, 0, canvas.width, canvas.height);

    // 设置模型