.函数glActiveTexture、glBindTexture、glTexParameteri的调用顺序
为了解决这个问题,OpenGL使用了一个称为mipmap(多级渐进纹理)的概念,这基本上是一组纹理图像,其中每个后续的纹理图像比前一个纹理图像小一倍。由于远处的物体可能只产生几个片段(fragments),OpenGL在从高分辨率纹理中为片段检索正确的颜色值时会遇到困难,因为它需要为一个覆盖纹理很大一部分的片段选择一个纹理颜色。纹理坐标并不依赖于分辨率,而可以是任意的浮点数值,因此OpenGL必须
首先分析: Textures中的Exercise 2
起因是我的程序和网站提供的示例跑出的程序不一样。
1.函数glActiveTexture
(1) glActiveTexture用来选择“当前活动的纹理单元”(texture unit),后续所有与纹理状态相关的调用(如glBindTexture、glTexImage2D、glTexParameteri 等)都会作用到这个活动单元上。
(2) glBindTexture(target, tex) 把tex绑定到当前活动单元的指定target(如GL_TEXTURE_2D)上——也就是说: 先选单元(glActiveTexture),再绑定(glBindTexture)
(3) 着色器里sampler类型的uniform(例如 uniform sampler2D tex;)并不保存一个 texture id,而是保存“要采样的纹理单元编号”(一个整数索引)。你用 glUniform1i(location, unitIndex) 把 sampler 指向某个单元(unitIndex 是 0、1、2…,对应 GL_TEXTURE0、GL_TEXTURE1…)。注意:glUniform1i 传的是索引(0,1,2),而不是 GL_TEXTURE0 常量本身
(4) 最佳调用时机
(5) 案例: 2个纹理
// 假设 shader 有两个 sampler2D:uDiffuse, uSpecular
glUseProgram(shaderProgram);
// 单元 0 -> 漫反射纹理
glActiveTexture(GL_TEXTURE0); // 单元编号 0
glBindTexture(GL_TEXTURE_2D, diffuseTex);
glUniform1i(glGetUniformLocation(shaderProgram, "uDiffuse"), 0); // 0 对应 GL_TEXTURE0
// 单元 1 -> 高光纹理
glActiveTexture(GL_TEXTURE1); // 单元编号 1
glBindTexture(GL_TEXTURE_2D, specularTex);
glUniform1i(glGetUniformLocation(shaderProgram, "uSpecular"), 1); // 1 对应 GL_TEXTURE1
// 画
glDrawElements(GL_TRIANGLES, ...);
、
2.函数glBindTexture
(1) 该函数的主要功能
将一个纹理对象(texture ID)绑定到当前活动的纹理单元的指定目标(target)上;
状态绑定的中心枢纽
间接控制shader采样
(2) 调用时机分析



、
3.纹理环绕
纹理坐标通常从(0,0)到(1,1),但如果指定超出这个范围的坐标,会发生什么呢?OpenGL的默认行为是重复纹理图像(我们基本上忽略浮点纹理坐标的整数部分),但OpenGL还提供了更多选项:
• GL_REPEAT:纹理的默认行为。重复纹理图像。
• GL_MIRRORED_REPEAT:与GL_REPEAT相同,但在每次重复时会镜像图像。
• GL_CLAMP_TO_EDGE:将坐标限制在0到1之间。结果是更高的坐标会被限制到边缘,导致边缘图案被拉伸。
• GL_CLAMP_TO_BORDER:超出范围的坐标现在会使用用户指定的边界颜色。
每种选项在使用超出默认范围的纹理坐标时,都会产生不同的视觉效果。让我们看看这些效果在一张示例纹理图像上是什么样的:
上述提到的每种选项都可以通过glTexParameter*函数为每个坐标轴(s、t,以及如果使用三维纹理的话还有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);
第一个参数指定了纹理目标;我们正在处理二维纹理,因此纹理目标是GL_TEXTURE_2D。第二个参数要求我们说明要设置哪个选项以及为哪个纹理轴设置;我们希望为S轴和T轴都进行配置。最后一个参数要求我们传入我们想要的纹理环绕模式,而在此情况下,OpenGL会将当前激活的纹理的纹理环绕选项设置为GL_MIRRORED_REPEAT。
如果我们选择GL_CLAMP_TO_BORDER选项,我们还应该指定一个边界颜色。这可以通过glTexParameterfv函数完成,使用GL_TEXTURE_BORDER_COLOR作为选项,并传入一个表示边界颜色值的浮点数组:
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
4.纹理过滤
纹理坐标并不依赖于分辨率,而可以是任意的浮点数值,因此OpenGL必须确定将纹理坐标映射到哪一个纹理像素(也称为纹理单元,texel)。这在你有一个非常大的物体和一个低分辨率的纹理时变得尤为重要。你可能已经猜到了,OpenGL也有针对这种纹理过滤的选项。虽然有几种可用的选项,但目前我们先讨论最重要的两种:GL_NEAREST和GL_LINEAR。
GL_NEAREST(也称为最近邻或点过滤)是OpenGL的默认纹理过滤方法。当设置为GL_NEAREST时,OpenGL会选择中心最接近纹理坐标的那个纹理单元。下面你可以看到4个像素,其中的交叉点表示精确的纹理坐标。左上角的纹理单元的中心最接近纹理坐标,因此被选为采样颜色:
GL_LINEAR(也称为(双)线性过滤)会从纹理坐标周围的纹理单元中取一个插值,近似出纹理单元之间的颜色。纹理坐标到纹理单元中心的距离越小,该纹理单元的颜色对采样颜色的贡献就越大。在下面的例子中,我们可以看到返回的是周围像素的混合颜色:
但是,这种纹理过滤方法的视觉效果是什么呢?让我们看看在将低分辨率纹理应用于大型物体时(纹理因此被放大,单个纹理单元变得明显),这些方法是如何工作的:
GL_NEAREST会产生块状图案,我们可以清楚地看到构成纹理的像素,而GL_LINEAR则会产生更平滑的图案,其中单个像素不太明显。GL_LINEAR会产生更逼真的输出,但有些开发者更喜欢8位像素风格,因此选择GL_NEAREST选项。
纹理过滤可以分别设置用于放大和缩小操作(无论是向上还是向下缩放)。例如,你可以在纹理缩小的时候使用最近邻过滤,而在纹理放大的时候使用线性过滤。因此,我们需要通过glTexParameter*函数分别为这两种情况指定过滤方法。代码看起来应该和设置环绕方式类似:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
(1) mipmap(多级渐进纹理)
想象一下,我们有一个很大的房间,里面有成千上万的物体,每个物体都附加了一张纹理。远处的物体和靠近观察者的物体可能都使用了相同高分辨率的纹理。由于远处的物体可能只产生几个片段(fragments),OpenGL在从高分辨率纹理中为片段检索正确的颜色值时会遇到困难,因为它需要为一个覆盖纹理很大一部分的片段选择一个纹理颜色。这会在小物体上产生明显的伪影,更不用说在小物体上使用高分辨率纹理会浪费内存带宽。
为了解决这个问题,OpenGL使用了一个称为mipmap(多级渐进纹理)的概念,这基本上是一组纹理图像,其中每个后续的纹理图像比前一个纹理图像小一倍。理解mipmap的概念应该很容易:在距离观察者达到某个阈值之后,OpenGL会使用一个更适合该物体距离的mipmap纹理。由于物体距离较远,较低的分辨率对用户来说是察觉不到的。OpenGL因此能够正确地采样纹理单元,并且在采样mipmap的那部分时涉及的缓存内存更少。让我们更仔细地看看mipmap纹理是什么样的:
为每张纹理图像手动创建一组多级渐进纹理(mipmap)是相当繁琐的,但幸运的是,OpenGL可以通过在创建纹理后调用一次glGenerateMipmap函数来为我们完成所有工作。
在渲染过程中切换mipmap级别时,OpenGL可能会显示出一些伪影,比如在两个mipmap层级之间可以看到明显的锐利边缘。就像普通的纹理过滤一样,也可以使用NEAREST和LINEAR过滤来在mipmap级别之间进行切换。为了指定mipmap级别之间的过滤方法,我们可以用以下四个选项之一替换原来的过滤方法:
• GL_NEAREST_MIPMAP_NEAREST:选择最接近像素大小的mipmap,并使用最近邻插值进行纹理采样。
• GL_LINEAR_MIPMAP_NEAREST:选择最接近的mipmap级别,并使用线性插值对该级别进行采样。
• GL_NEAREST_MIPMAP_LINEAR:在线性插值两个最接近像素大小的mipmap之间进行选择,并通过最近邻插值对插值后的级别进行采样。
• GL_LINEAR_MIPMAP_LINEAR:在线性插值两个最接近的mipmap之间进行选择,并通过线性插值对插值后的级别进行采样。
就像纹理过滤一样,我们也可以使用 glTexParameteri 函数将过滤方法设置为上述四种方法之一:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
一个常见的错误是将mipmap过滤选项设置为放大过滤器。这是没有效果的,因为mipmap主要用于纹理缩小的情况:纹理放大不会使用mipmap,而将mipmap过滤选项分配给它会生成一个OpenGL错误代码GL_INVALID_ENUM。
5.函数glActiveTexture、glBindTexture、glTexParameteri的调用顺序

在初始化纹理时,一般这样写:
GLuint texID;
glGenTextures(1, &texID);
glActiveTexture(GL_TEXTURE0 + unitIndex); // 1. 选择单元
glBindTexture(GL_TEXTURE_2D, texID); // 2. 绑定纹理对象
// 3. 配置参数
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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 上传数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
// shader 中设置 sampler
glUniform1i(glGetUniformLocation(program, "uSampler"), unitIndex);
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)