【问题标题】:How do I render multiple textures in modern OpenGL?如何在现代 OpenGL 中渲染多个纹理?
【发布时间】:2014-12-07 17:14:07
【问题描述】:

我目前正在为一个小游戏编写 2d 引擎。

我的想法是我可以在一次绘制调用中渲染整个场景。我认为我可以在四边形上渲染每个 2d 图像,这意味着我可以使用实例化。

我想象我的顶点着色器可能看起来像这样

...
in vec2 pos;
in mat3 model;
in sampler2d tex;
in vec2 uv;
...

我以为我可以像处理 VBO 一样在 gpu 上加载纹理并获取它的句柄,但似乎没那么简单。

看来我得打电话了

glActiveTexture(GL_TEXTURE0..N);

对于我要加载的每个纹理。现在这似乎不像我想象的那么容易编程。现代游戏引擎如何渲染多个纹理?

我读到GL_TEXTURE 的纹理限制取决于 GPU,但至少为 45。如果我要渲染包含超过 45 个纹理的图像,例如 90,该怎么办?

看来我必须渲染前 45 个纹理并从 gpu 中删除所有纹理并将其他 45 个纹理从 hdd 加载到 gpu。每一帧都做这似乎不太合理。特别是当我想为 2D 图像制作动画时。

我可以很容易地认为,一个简单的 2d 角色动画可能包含 10 个不同的图像。这意味着我可以轻松超过纹理限制。

我的一个小想法是将多个图像组合成一个巨型图像,然后通过 uv 坐标偏移它们。

我想知道我是否误解了纹理在 OpenGL 中的工作原理。

如何在 OpenGL 中渲染多个纹理?

【问题讨论】:

  • 不需要删除旧贴图,绑定新贴图即可。还有纹理图集
  • 你的“小创意”其实是一种很常见的技巧,在很多游戏中都有使用。 GPU 非常擅长处理大型纹理。
  • 我似乎记得很多类似的问题,但我现在找不到重复的问题。你所有的纹理大小都一样吗?您在最后提到的想法主要被称为“纹理图集”,并且是其中一种选择。
  • 其实Texture Image Units的数量至少不是45个。它的最小值精确地等于16 * 现代 OpenGL 中的 。这意味着在 GL 3.3 中它是 16 * 3 (Vertex, Geometry, Fragment) = 48,在 GL 4.5 中它是 16 * 5 (Vertex, Tessellation Control, Tessellation Evaluation, Geometry, Fragment) = 80。这样做的原因是您可以绑定一组 16 个独特的纹理以供每个阶段使用,但实际上在最小实现中的着色器的一次执行中不能采样超过 16 个纹理。
  • 我已经在这里发布了答案:stackoverflow.com/questions/16075791/…

标签: opengl


【解决方案1】:

这个问题有点宽泛,所以这只是对在同一个绘图调用中使用多个纹理的一些选项的快速概述。

绑定到多个纹理单元

对于这种方法,您可以使用典型序列将每个纹理绑定到不同的纹理单元:

glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, tex[i]);

在着色器中,您可以拥有一组单独的 sampler2D 制服,也可以拥有一组 sampler2D 制服。

这样做的主要缺点是您受到可用纹理单元数量的限制。

阵列纹理

您可以使用数组纹理。这是通过使用GL_TEXTURE_2D_ARRAY 纹理目标来完成的。在许多方面,2D 纹理数组类似于 3D 纹理。它基本上是一堆堆叠在一起的 2D 纹理,并存储在一个纹理对象中。

缺点是所有纹理都需要具有相同的大小。如果他们不这样做,您必须使用最大的尺寸作为纹理数组的大小,并且为较小的纹理浪费内存。如果尺寸不完全相同,您还必须对纹理坐标应用缩放。

纹理图集

这是您已经提出的想法。您将所有纹理存储在单个大纹理中,并使用纹理坐标来控制使用哪个纹理。

虽然是一种流行的方法,但它存在一些技术挑战。在使用线性采样时,您必须小心纹理之间的接缝,以免它们相互渗入。虽然这种方法与纹理数组不同,允许在不浪费内存的情况下使用不同的纹理大小,但在图集中分配区域会因大小可变而变得有点棘手。

无绑定纹理

目前仅作为扩展程序提供:ARB_bindless_texture

【讨论】:

    【解决方案2】:

    你需要了解纹理单元和纹理对象的区别。

    纹理单元就像 OpenGL 光栅化器的“纹理盒”。光栅化器有数量有限的“墨盒”插槽(称为纹理单元)。要将纹理加载到纹理单元中,首先使用glActiveTexture 选择单元,然后使用glBindTexture 加载纹理“墨盒”(纹理对象)。

    您可以拥有的纹理对象数量仅受系统内存(和存储能力)的限制,但只能将有限数量的纹理同时“插入”到纹理单元中。

    采样器就像“点击”到纹理单元中。着色器中的不同采样器可以“点击”到同一个纹理单元中。通过将采样器统一设置为纹理单元,您可以选择要从哪个单元进行采样。

    然后您还可以将相同的纹理同时“开槽”到多个纹理单元中。

    更新(一些说明)

    我读到 GL_TEXTURE 的纹理限制取决于 GPU,但它至少为 45。如果我要渲染包含超过 45 个纹理的图像,例如 90,该怎么办?

    通常您不会尝试通过一次绘图调用来渲染整个图像。实际上不可能捕捉到在什么情况下使用哪些纹理的所有变化。通常,您为“材质”的特定外观编写着色器。假设您有一个着色器模拟某些金属上的油漆。您将拥有 3 种纹理:金属、油漆和一个调制纹理,用于控制金属和油漆可见的位置。然后着色器将有 3 个采样器制服,每个纹理一个。要呈现具有该外观的表面,您需要

    • 选择要使用的着色器程序 (glUseProgram)
    • 为每个纹理依次激活纹理单元 (glActiveTexture(GL_TEXTURE_0+i) 并绑定纹理 ('glBindTexture`)
    • 将采样器制服设置为要使用的纹理单元 (glUniform1i(…, i))。
    • 绘制几何图形。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-27
      • 2014-10-28
      • 1970-01-01
      • 1970-01-01
      • 2014-08-09
      • 2023-03-30
      相关资源
      最近更新 更多