c++ - 纹理映射到瓷砖

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

我被这项任务卡住了,不知道如何解决。我需要绘制带有纹理的图块网格,我的世界代表一个图块网格,其中每个图块的宽度和高度均等于1。绘制时,我计算了一个顶点缓冲区,其中包含可在相机看到的图块的顶点,例如屏幕上:
enter image description here

(所以我所有这些顶点都有一个VBO)
另外,我有一个包含索引的元素缓冲区,并使用GL_TRIANGLE_STRIP模式绘制它们:
enter image description here
实际结果:
enter image description here

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

最佳答案

好吧,如果其他问答对它的解释不够好,让我尝试一下。
问题确实在于您正在使用triangle strip。三角带有很多用途,最重要的原因是减少用于存储顶点的数据量:

The primary reason to use triangle strips is to reduce the amount of data needed to create a series of triangles. The number of vertices stored in memory is reduced from 3N to N+2, where N is the number of triangles to be drawn.

(Wikipedia)


只需简单地重用先前三角形中的数据,三角形带即可实现这一神奇的性能。它从上一个三角形获取两个顶点,并从另一个三角形获取一个顶点,这使您可以在该组点上形成一个新的三角形。如果所有顶点都形成一个连续的表面,并且每个顶点作为第一个三角形的一部分和下一个三角形的一部分,则每个顶点都有意义。
例如对于一系列顶点:
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),该坐标同时是第二个三角形的右边缘和第三个三角形的左边缘。这个顶点自然在位置和纹理上都属于它们。

现在,考虑一个瓦片 map ,其中每个正方形可以是一个不同的瓦片。通常,这可以通过带有方形瓷砖的纹理图集来实现,每种类型的瓷砖仅以不同的偏移量存储。
因此,第一对三角形的纹理坐标可能是相同的,而第二对三角形的纹理坐标可能具有说(+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),第一个三角形的纹理坐标为t0t3,第二个三角形的纹理坐标为u0u3。现在:
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分别与t3u1交互。这是因为顶点2是第一个三角形的第三个顶点,但是第二个三角形的第一个顶点,依此类推。
就是这样!现在,您只需要编写代码来生成这样的布局,就可以随意设置VBO(请记住,位置的顶点属性可能与图块的VBO完全不同,而无需重写即可更新图块内容瓷砖本身),您就完成了。
请记住,正如我之前提到的,所有这些仍然在一次调用中绘制。整个VBO由GPU尽可能快地进行线性处理,这将导致非常好的性能,考虑到我们正在处理的数据类型和内存大小,略高的内存使用量可以忽略不计。当今的典型GPU。

我有几句话是后记。
  • 实际上,如果确实使用了索引渲染,则仅索引会获得重复的,而不是实际的顶点数据。在这里使用它是一个好主意
  • 记住生成缓冲区时的绕线顺序。由于您是以编程方式进行操作,因此更改起来并不难,但是如果您未正确地获得订单,可能会导致一些有趣的故障。
  • 将纹理索引保留在单独的缓冲区或相同的缓冲区中,但不要交错存取,这似乎很诱人,因为您可以更新它们而无需触摸最终不会改变的位置。这听起来很诱人,但并非没有缺点。交错格式之所以好,恰好是因为它使使用在一起的数据保持紧密,这意味着缓冲区的提取非常非常有效。如果将它们分开,则将迫使GPU从两个不同的内存位置进行流传输,从而可能导致较差的渲染性能。同样,对于简单的2D网格,这可能无关紧要,但是要记住这一点。
  • 关于c++ - 纹理映射到瓷砖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59858065/

    相关文章:

    api - opengl当前顶点是什么?

    c++ - 何时编写迭代器?

    c++ - 用 C C++ 测试代码

    c++ - 在 C++ 中的合并排序中传递数组时出错

    ios - CVOpenGLESTexture 方法类型的官方文档在哪里?

    android - Open GL ES - GLM 库和 Android Studio

    android - Android 上的 OpenGL 深度缓冲区

    c++ - 尝试分配函数指针时获取 "Void value not ignored as it ought to be"

    c++ - glGetError() 总是在成功获取上下文后返回 GL_INVALID_OPERATION

    android - 在 ARCore anchor Android openGL ES 2.0 上放置 2D Bitmap