c++ - 纹理映射到平铺
问题描述
我被困在这个任务上,不知道如何解决这个问题。我需要用纹理绘制平铺网格,我的世界表示一个平铺网格,其中每个平铺的宽度和高度等于 1。当我绘制时,我计算一个顶点缓冲区,其中包含相机可见的平铺顶点,比如屏幕上:
(所以我对所有这些顶点都有一个 VBO)
另外,我有一个包含索引的元素缓冲区,我使用 GL_TRIANGLE_STRIP 模式绘制它们:
实际结果:
在这一步中一切正常,但接下来我需要在每个方格上映射他的纹理,这是我从网上收到的,所有纹理都不同。我该怎么做?我正在使用OpenGL ES 2.0和 C++。
解决方案
好吧,如果其他问答解释得不够好,让我试一试。
问题确实在于您使用的是三角形带。三角带有多种用途,使用它最重要的原因是为了减少存储顶点的数据量:
使用三角形条的主要原因是减少创建一系列三角形所需的数据量。存储在内存中的顶点数量从 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.5和5。它们只是两个三角形的不同顶点,它们恰好在位置上彼此相邻,但在纹理上是完全独立的。重用来自先前顶点的所有属性的三角形带现在是障碍。
这就是爆炸开始的地方。您需要单独的三角形,而不是将几何图形绘制为三角形条带。您仍然可以在一个 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),纹理坐标是第一个三角形的t0
to和第二个三角形的to 。现在:t3
u0
u3
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
呸!现在更容易发现关键区别:位置与第一个三角形中的2
texcoord 结合出现t2
,但u0
在第二个三角形中出现位置。类似地,位置分别与和3
相互作用。这是因为顶点 2 是第一个三角形的第三个顶点,但是第二个三角形的第一个顶点,以此类推。t3
u1
就是这样!现在您只需要编写代码来生成这样的布局,随意设置您的 VBO(请记住,位置的顶点属性可能与平铺的 VBO 完全不同,以便更轻松地更新平铺内容而无需重写瓷砖本身),你就完成了。
请记住,正如我之前提到的,这仍然是在一个 drawcall 中绘制的。整个 VBO 由 GPU 尽可能快地线性处理,它应该会产生非常好的性能,考虑到我们在这里处理的数据类型和内存大小,稍微高一点的内存使用可以忽略不计现在典型的GPU。
我有一些结束语,有点像附言
- 事实上,如果您确实使用索引渲染,则只有索引会被复制,而不是实际的顶点数据。在这里使用它是个好主意
- 记住生成缓冲区时的缠绕顺序。由于您是以编程方式进行的,因此更改并不难,但如果您没有正确执行顺序,可能会导致一些有趣的故障。
- 将纹理索引保存在单独的缓冲区或相同的缓冲区中,但不交错似乎很诱人,因为您可以在不触及最终永远不会改变的位置的情况下更新它们。这听起来很诱人,但并非没有缺点。交错格式之所以很好,正是因为它可以使数据紧密地一起使用,这意味着缓冲区提取可以非常非常有效。如果你把它们分开,你会迫使 gpu 从两个不同的内存位置流式传输,可能会导致更差的渲染性能。同样,对于简单的 2D 网格,这可能无关紧要,但要牢记这一点。
推荐阅读
- kotlin - 使用 RxJava 在一段时间后发出项目
- magento - 在 Magento 2 中使用 REST API 为现有货件添加新的跟踪号
- unit-testing - 将测试从一个 HTTP 客户端重用到另一个
- ios - 快速过滤多维数组
- javascript - 尝试在 wordpress javascript 中提取 acf 字段时出现错误
- python - opencv:使用蒙版的黑色边框
- php - (php) 条件运算符有什么问题?有一个函数可以确定关联数组是否有任何键
- flake8 - 如何安装 Flake8-SQL 插件并运行其规则
- sql - 使用 T-SQL 从列中提取第一个争论
- android - 为什么只有某些应用程序在使用 adb 卸载应用程序的命令中出现错误?