OpenGL-纹理

本文深入探讨OpenGL中的纹理技术,包括纹理的基本概念、纹理环绕方式、纹理过滤、多级渐远纹理、纹理加载与创建、纹理生成及应用。此外,还介绍了如何在着色器中使用纹理,以及如何处理多个纹理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

纹理

纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节,
除了图像以外,纹理也可以被用来储存大量的数据,这些数据可以发送到着色器上

纹理环绕方式

纹理坐标的范围通常是从(0, 0)到(1, 1)

环绕方式描述
GL_REPEAT对纹理的默认行为。重复纹理图像
GL_MIRRORED_REPEAT和GL_REPEAT一样,但每次重复图片是镜像放置的
GL_CLAMP_TO_EDGE纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
GL_CLAMP_TO_BORDER超出的坐标为用户指定的边缘颜色

每个选项都可以使用glTexParameter*函数对单独的一个坐标轴设置(s、t(如果是使用3D纹理那么还有一个r)它们和x、y、z是等价的)

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

第一个 参数指定了 纹理目标 ;我们使用的是2D纹理,因此纹理目标是 GL_TEXTURE_2D第二个参数 需要我们指定设置的选项与应用的 纹理轴 。我们打算配置的是WRAP选项,并且指定S和T轴。最后一个参数 需要我们传递一个 环绕方式(Wrapping)

纹理过滤

** GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)**

OpenGL默认的纹理过滤方式。当设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素。

效果
GL_NEAREST产生了颗粒状的图案,我们能够清晰看到组成纹理的像素

GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)

它会基于纹理 坐标附近 的纹理像素,计算出一个 插值 ,近似出这些纹理像素之间的颜色。一个纹理像素的 中心距离纹理坐标越近 ,那么这个纹理像素的颜色对最终的样本颜色的 贡献越大

效果
L_LINEAR能够产生更平滑的图案,很难看出单个的纹理像素

当进行 放大(Magnify)缩小(Minify) 操作的时候可以设置纹理过滤的选项,比如你可以在纹理被 缩小 的时候使用 邻近过滤,被 放大 时使用 线性过滤。我们需要使用glTexParameter*函数为放大和缩小指定过滤方式。这段代码看起来会和纹理环绕方式的设置很相似:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

多级渐远纹理

简单来说就是 一系列的纹理图像,后一个纹理图像是前一个的 二分之一。多级渐远纹理背后的理念很简单:距观察者的距离 超过一定的阈值,OpenGL会 使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。同时,多级渐远纹理另一加分之处是它的 性能非常好

** 过滤方式 **
在这里插入图片描述

加载与创建纹理

加载与创建纹理
使用stb_image.h

int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);

这个函数首先接受一个图像文件的 位置 作为输入。接下来它需要三个int作为它的第二、第三和第四个参数,stb_image.h将会用图像的 宽度、高度和颜色通道 的个数填充这三个变量。我们之后生成纹理的时候会用到的图像的宽度和高度的。

生成纹理

unsigned int texture;
glGenTextures(1, &texture);

绑定纹理

glBindTexture(GL_TEXTURE_2D, texture);

生成纹理

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
// 生成所有需要的多级渐远纹理
glGenerateMipmap(GL_TEXTURE_2D);

在这里插入图片描述
** 过程 **

unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载并生成纹理
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
    std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);

应用纹理

float vertices[] = {
//     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
     0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
     0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
    -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
    -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
};

在这里插入图片描述

** 顶点着色器 **

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
}

** 采样器(Sampler) **
把纹理对象传给片段着色器

** 片段着色器 **

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D ourTexture;

void main()
{
    FragColor = texture(ourTexture, TexCoord);
}

GLSL内建的texture函数来采样纹理的颜色,它第一个参数是纹理采样器``,第二个参数是对应的纹理坐标。texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。

** 调用glDrawElements之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器**

glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

纹理单元

一个 纹理的位置 值通常称为一个纹理单元(Texture Unit)。一个纹理的默认纹理单元是0,它是默认的激活纹理单元

激活纹理单元
纹理单元的主要目的是让我们在着色器中可以 使用多于一个的纹理 。通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理,只要我们首先激活对应的纹理单元。就像glBindTexture一样,我们可以使用glActiveTexture激活纹理单元,传入我们需要使用的纹理单元:

glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);

激活纹理单元之后,接下来的glBindTexture函数调用会绑定这个纹理到当前激活的纹理单元,纹理单元 GL_TEXTURE0默认总是被激活,所以我们在前面的例子里当我们使用glBindTexture的时候,无需激活任何纹理单元。

OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从
GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们
也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我
们需要循环一些纹理单元的时候会很有用。

多个纹理

#version 330 core
...

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
    // 0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,
    //即返回两个纹理的混合色
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}

使用
先绑定两个纹理到对应的纹理单元,然后定义哪个uniform采样器对应哪个纹理单元

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);

glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

我们还要通过使用glUniform1i设置每个采样器的方式告诉OpenGL每个着色器采样器属于哪个纹理单元

ourShader.use(); // 别忘记在激活着色器前先设置uniform!
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // 手动设置
ourShader.setInt("texture2", 1); // 或者使用着色器类设置

while(...)
{
    [...]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值