计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 05.纹理贴图

发布于:2025-05-11 ⋅ 阅读:(6) ⋅ 点赞:(0)

1. 纹理贴图

纹理贴图是 OpenGL 中用于将图像数据映射到几何体表面的技术。通过纹理贴图,可以为 3D 模型添加细节和真实感,例如颜色、光照、凹凸效果等。

1.1. 纹理坐标

每个顶点(x,y,z)都对应一个纹理坐标(u,v),纹理坐标用于确定纹理图像在顶点上的映射位置。纹理坐标通常是一个二维向量,其中 u 和 v 分别表示纹理图像的 U 和 V 坐标。U 坐标表示纹理图像在水平方向上的位置,V 坐标表示纹理图像在垂直方向上的位置。

上图是纹理坐标的示意图,其中 (0,0) 表示纹理图像的左下角,(1,1) 表示纹理图像的右上角。


上图是纹理坐标在 3D 模型上的应用示例,其中顶点 (0,0,0) 对应纹理坐标 (0,0)

注意:

  • 由于模型面的宽高比与纹理图像的宽高比可能存在不一致的情况,这就为涉及到图像的变形。
  • 模型面的大小与纹理图像的大小也可能存在不一致的情况,这就为涉及到图像的拉伸或缩放。
  • 纹理坐标通常使用 0 到 1 之间的浮点数表示,而不是使用像素坐标。

1.2. 纹理贴图步骤

  1. 加载纹理图像
  2. 创建纹理对象
  3. 将纹理对象绑定到目标
  4. 将纹理图像传递给纹理对象
  5. 使用纹理坐标将纹理映射到几何体表面

1

1.2.1. 加载纹理图像

 // 生成纹理对象
    texture = util.loadTexture("./texture/brick1.jpg"); // 加载纹理
    if (texture <= 0)
    {
        cout << "Error loading texture" << endl;
       // exit(1);
    } // 检查纹理加载是否成功

    glActiveTexture(GL_TEXTURE0);                                     // 激活纹理单元 0
    glBindTexture(GL_TEXTURE_2D, texture);                            // 绑定纹理对象
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 设置纹理过滤参数:最小过滤

纹理图像加载成功且绑定到GL_TEXTURE0后,在片段着色器中,纹理图像会自动传递给纹理单元 0
即片段着色器中采用如下代码拿到纹理图像

layout (binding=0) uniform sampler2D tex0;

layout(binding = 0): 这个语法告诉图形驱动程序,这个特定的资源(例如一个纹理采样器)应该在绑定点 0 上被访问。绑定点是一个抽象的概念,用于在着色器和应用程序之间映射资源。

可用纹理单元的数量取新决于GPU上提供的数量 ,据据OpenGL 规范,要求每个着色器至少有 16 个纹理单元。

1.2.2. 纹理坐标定义

纹理坐标定义有两种方式:

  1. 在定义顶点坐标时,同时定义纹理坐标,即每个顶点除了有(x,y,z)坐标外,还有(u,v)纹理坐标 。这种方式参见第4章 定义顶点坐标同时定义顶点颜色的示例
  2. 顶点坐标和纹理坐标分开定义,此种方式容易理解,在配置时也比较灵活,本节代码使用此种方式
float pyramidPositions[54] =
        {
            -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f,    // front
            1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f,    // right
            1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f,  // back
            -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f,  // left
            -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, // LF
            1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f  // RR
        };
    float textureCoordinates[36] =
        {0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
         0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
         0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
         0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
         0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f};

    glGenVertexArrays(1, vao);  // 生成 VAO
    glBindVertexArray(vao[0]);  // 绑定 VAO
    glGenBuffers(numVBOs, vbo); // 生成 VBO

    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);                                                     // 绑定 VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidPositions), pyramidPositions, GL_STATIC_DRAW); // 将顶点数据传递到 VBO

    // glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); // 绑定 VBO
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, (void *)0); // 设置顶点属性指针:坐标
    glEnableVertexAttribArray(0);                               // 启用顶点属性

    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);                                                         // 绑定 VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoordinates), textureCoordinates, GL_STATIC_DRAW); // 将纹理坐标数据传递到 VBO

    glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, (void *)0); // 设置顶点属性指针:坐标
    glEnableVertexAttribArray(1);                               // 启用顶点属性

由于我们已经启用了顶点属性,纹理坐标数据会自动传递到顶点着色器中

1.2.3. 顶点着色器

#version 430
// 指定 GLSL 的版本为 4.30

layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord; // 输入变量,表示顶点的颜色,绑定到 location = 1

// 输入变量,表示顶点的三维位置,绑定到 location = 0

uniform mat4 mv_matrix;
// uniform 变量,表示模型-视图矩阵,用于将顶点从模型空间变换到视图空间

uniform mat4 proj_matrix;
// uniform 变量,表示投影矩阵,用于将顶点从视图空间变换到裁剪空间

out vec2 tc;
// 输出变量,表示顶点的颜色,绑定到 location = 0
void main(void)
// 主函数,计算顶点的最终位置
{
    gl_Position = proj_matrix * mv_matrix * vec4(position,1.0);
    // 将顶点位置从模型空间依次变换到视图空间和裁剪空间
    // 最终结果存储在内置变量 gl_Position 中,用于后续的光栅化阶段
    tc = texCoord;
    
}

每个顶点的纹理坐标会自动经过插值传递到片段着色器中

1.2.4. 片段着色器

#version 430
// 指定 GLSL 的版本为 4.30

in vec2 tc;
// 输入变量,表示顶点的颜色(未使用)
out vec4 color;
// 输出变量,表示片段的最终颜色

uniform mat4 mv_matrix;
// uniform 变量,模型-视图矩阵(未使用)

uniform mat4 proj_matrix;
// uniform 变量,投影矩阵(未使用)
//layout (binding=0) uniform sampler2D tex0;
uniform sampler2D tex0;
void main(void)
// 主函数,计算片段的最终颜色
{
    color = texture(tex0, tc);
}

1.3. 多级渐远纹理贴图

多级渐隐纹理映射(Mipmapping),是一种在计算机图形学中用于提高纹理渲染质量和性能的技术。其主要目的是在不同距离和分辨率下提供合适的纹理细节,同时减少渲染过程中的资源消耗和视觉上的瑕疵,如闪烁和摩尔纹。

工作原理

  1. 生成多级纹理:

原始纹理(Level 0)被缩小,生成一系列逐渐缩小的纹理图像,形成一个纹理金字塔,每一级(Level)的纹理大小是前一级的一半。
例如,一个256x256的纹理会有8级(包括原始纹理),尺寸分别为256x256、128x128、64x64、32x32、16x16、8x8、4x4、2x2和1x1。
2. 选择合适的纹理级别:

在渲染过程中,根据物体表面的观察距离和屏幕上像素的大小,动态选择最合适的纹理级别。
靠近观察者的表面使用较高分辨率的纹理(较高级别),而远离观察者的表面使用较低分辨率的纹理(较低级别)。
3. 过滤和混合:

为了在不同级别之间实现平滑过渡,通常会使用线性插值等方法进行混合,以避免出现明显的纹理切换痕迹。
优点

提高渲染质量:通过提供适合当前观察条件的纹理细节,减少了视觉上的瑕疵,提高了图像的整体质量。
提升性能:使用较低分辨率的纹理来渲染远距离物体,减少了显存带宽和处理器的负担,提高了渲染速度。

1.3.1. 函数说明

1.3.1.1. glTexParameteri

在OpenGL中,glTexParameteri函数用于设置纹理参数。其函数原型如下:

void glTexParameteri(GLenum target, GLenum pname, GLint param);

参数说明

  1. target:

指定纹理目标的类型。常见的值包括:

  • GL_TEXTURE_1D:一维纹理。
  • GL_TEXTURE_2D:二维纹理。
  • GL_TEXTURE_3D:三维纹理。
  • GL_TEXTURE_CUBE_MAP:立方体贴图。
  1. pname:

指定要设置的纹理参数名称。常见的值包括:

  • GL_TEXTURE_MIN_FILTER:设置纹理缩小过滤模式。
  • GL_TEXTURE_MAG_FILTER:设置纹理放大过滤模式。
  • GL_TEXTURE_WRAP_S:设置纹理在S(水平)方向的环绕模式。
  • GL_TEXTURE_WRAP_T:设置纹理在T(垂直)方向的环绕模式。
  • GL_TEXTURE_WRAP_R:设置纹理在R(深度)方向的环绕模式(仅适用于3D纹理)。
  1. param:

指定参数的具体值。根据pname的不同,param可以取不同的值。例如:
对于GL_TEXTURE_MIN_FILTER和GL_TEXTURE_MAG_FILTER,常见的值包括:

  • GL_NEAREST:最近邻过滤。
  • GL_LINEAR:线性过滤。
  • GL_NEAREST_MIPMAP_NEAREST:最近邻Mipmap过滤。
  • GL_LINEAR_MIPMAP_NEAREST:线性Mipmap过滤。
  • GL_NEAREST_MIPMAP_LINEAR:最近邻Mipmap线性过滤。
  • GL_LINEAR_MIPMAP_LINEAR:线性Mipmap线性过滤(三线性过滤)。
    对于GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T和GL_TEXTURE_WRAP_R,常见的值包括:
  • GL_REPEAT:重复纹理。
  • GL_MIRRORED_REPEAT:镜像重复纹理。
  • GL_CLAMP_TO_EDGE:将纹理坐标钳制到边缘。
  • GL_CLAMP_TO_BORDER:将纹理坐标钳制到边框颜色。
1.3.1.2. glGenerateMipmap

在OpenGL中,glGenerateMipmap函数用于生成纹理的Mipmap。其函数原型如下:

void glGenerateMipmap(GLenum target);

参数说明
target
指定纹理目标的类型。常见的值包括:

  • GL_TEXTURE_1D:一维纹理。
  • GL_TEXTURE_2D:二维纹理。
  • GL_TEXTURE_3D:三维纹理。
  • GL_TEXTURE_CUBE_MAP:立方体贴图。

1.4. 各向异性过滤

标准的多级渐远纹理映射(Mipmapping)技术通过将纹理缩小到不同级别来提高渲染质量,但同时也会引入一些问题,如摩尔纹和锯齿。为了解决这些问题,引入了各向异性过滤(Anisotropic Filtering)技术。

标准的多级渐远纹理贴图以各种正方形分辨率(如 256 像素×256 像素、 128 像素×128 像素等)对纹理图像进行采样,而各向异性过滤却以多种矩形分辨率对纹理进行采样(如 256 像素×128 像素、 64像素×128 像素等)。这使得从各种角度观看的纹理都保留尽可能多的细节成为可能

1.5. 参考

  1. OpenGLshader开发实战学习笔记:第三章 使用纹理_qopenglwidget 绘制三维坐标系-CSDN博客
  2. 学习笔记完整代码下载

网站公告

今日签到

点亮在社区的每一天
去签到