首页 > 解决方案 > 纹理映射到平铺

问题描述

我被困在这个任务上,不知道如何解决这个问题。我需要用纹理绘制平铺网格,我的世界表示一个平铺网格,其中每个平铺的宽度和高度等于 1。当我绘制时,我计算一个顶点缓冲区,其中包含相机可见的平铺顶点,比如屏幕上:
在此处输入图像描述

(所以我对所有这些顶点都有一个 VBO)
另外,我有一个包含索引的元素缓冲区,我使用 GL_TRIANGLE_STRIP 模式绘制它们: 实际结果:
在此处输入图像描述

在此处输入图像描述


在这一步中一切正常,但接下来我需要在每个方格上映射他的纹理,这是我从网上收到的,所有纹理都不同。我该怎么做?我正在使用OpenGL ES 2.0和 C++。

标签: c++opengl-esopengl-es-2.0

解决方案


好吧,如果其他问答解释得不够好,让我试一试。

问题确实在于您使用的是三角形带。三角带有多种用途,使用它最重要的原因是为了减少存储顶点的数据量:

使用三角形条的主要原因是减少创建一系列三角形所需的数据量。存储在内存中的顶点数量从 3N 减少到 N+2,其中 N 是要绘制的三角形的数量。

(维基百科)

三角形条通过重用以前三角形的数据就实现了这个神奇的特性。它需要前一个三角形的两个顶点和一个额外的顶点,这使您可以在该组点上形成一个新三角形。如果顶点都形成一个连续的曲面,并且每个顶点作为第一个三角形和下一个三角形的一部分都有意义,则此方法非常有效。

例如对于一系列顶点:

0:  (0,0)
1:  (0,1)
2:  (1,0)
3:  (1,1)

我们最终得到

1---3
|\  |
| \ |
|  \|
0---2

事实上,三角形是由索引 (0,1,2) 和 (1,2,3) 形成的。

即使我们添加纹理,它仍然有效。假设纹理有四个瓦片那么大,我们可能会得到:

0:  (0,0) (0  ,   0)
1:  (0,1) (0  , 0.5)
2:  (1,0) (0.5,   0)

3:  (1,1) (0.5, 0.5)
4:  (2,0) (1  ,   0)
5:  (2,1) (1  , 0.5)

结果:

1---3---5
|\  |\  |
| \ | \ |
|  \|  \|
0---2---4

要观察的关键顶点是 2 和 3。对于顶点 2,纹理坐标为 (0.5, 0),同时是第 2 个三角形的右边缘和第 3 个三角形的左边缘。这个顶点自然属于它们两者,无论是位置方面还是纹理方面。


现在,考虑一个平铺地图,其中每个方块都可以是不同的平铺。通常,这可以通过带有方形图块的纹理图集来实现,每种类型的图块都简单地存储在不同的偏移量处。

因此,第一对三角形的纹理坐标可能相同,而第二对三角形的纹理坐标可能有一个偏移量,比如 (+5, +5)(假设纹理现在是 100x100,因为这样更容易读书)。

那么现在顶点 2 和 3 会发生什么?它们不能同时使纹理坐标为 0.55。它们只是两个三角形的不同顶点,它们恰好在位置上彼此相邻,但在纹理上是完全独立的。重用来自先前顶点的所有属性的三角形带现在是障碍。


这就是爆炸开始的地方。您需要单独的三角形,而不是将几何图形绘制为三角形条带。您仍然可以在一个 drawcall 中绘制它们,但是您将不得不忍受一些额外的数据重复:

-- triangle 0
0:  (0,0) (0  ,   0)
1:  (0,1) (0  , 0.5)
2:  (1,0) (0.5,   0)

-- triangle 1
3:  (0,1) (0  , 0.5)
4:  (1,0) (0.5,   0)
5:  (1,1) (0.5, 0.5)

-- triangle 2
6:  (1,0) (5  ,   5)
7:  (1,1) (5  , 5.5)
8:  (2,0) (5.5,   5)

-- triangle 3
9:  (1,1) (5  , 5.5)
10: (2,0) (5.5,   5)
11: (2,1) (5.5, 5.5) 

这是原始数据,但我知道很难跟踪,所以让我们使用索引再看一遍。假设位置和之前一样 (0-5),纹理坐标是第一个三角形的t0to和第二个三角形的to 。现在:t3u0u3

0:  0 t0
1:  1 t1
2:  2 t2

3:  1 t1
4:  2 t2
5:  3 t3

6:  2 u0
7:  3 u1
8:  4 u2

9:  3 u1
10: 4 u2
11: 5 u3

呸!现在更容易发现关键区别:位置与第一个三角形中的2texcoord 结合出现t2,但u0在第二个三角形中出现位置。类似地,位置分别与和3相互作用。这是因为顶点 2 是第一个三角形的第三个顶点,但是第二个三角形的第一个顶点,以此类推。t3u1

就是这样!现在您只需要编写代码来生成这样的布局,随意设置您的 VBO(请记住,位置的顶点属性可能与平铺的 VBO 完全不同,以便更轻松地更新平铺内容而无需重写瓷砖本身),你就完成了。

请记住,正如我之前提到的,这仍然是在一个 drawcall 中绘制的。整个 VBO 由 GPU 尽可能快地线性处理,它应该会产生非常好的性能,考虑到我们在这里处理的数据类型和内存大小,稍微高一点的内存使用可以忽略不计现在典型的GPU。


我有一些结束语,有点像附言

  1. 事实上,如果您确实使用索引渲染,则只有索引会被复制,而不是实际的顶点数据。在这里使用它是个好主意
  2. 记住生成缓冲区时的缠绕顺序。由于您是以编程方式进行的,因此更改并不难,但如果您没有正确执行顺序,可能会导致一些有趣的故障。
  3. 将纹理索引保存在单独的缓冲区或相同的缓冲区中,但不交错似乎很诱人,因为您可以在不触及最终永远不会改变的位置的情况下更新它们。这听起来很诱人,但并非没有缺点。交错格式之所以很好,正是因为它可以使数据紧密地一起使用,这意味着缓冲区提取可以非常非常有效。如果你把它们分开,你会迫使 gpu 从两个不同的内存位置流式传输,可能会导致更差的渲染性能。同样,对于简单的 2D 网格,这可能无关紧要,但要牢记这一点。

推荐阅读