我应该使用哪个 OpenGL 基元来绘制三角剖分?

Which OpenGL Primitive should I use for draw an triangulation?

我有以下图片:

我能够像下面这样进行三角测量:

我是使用 python 的 triangle 库完成的。我的三角测量结果存储在 dict 对象中,如下所示:

>>> triangulation["vertices"]
array([[ 23.        , 282.        ],
       [ 24.        , 254.        ],
       [ 30.        , 239.        ],
       [ 43.        , 219.        ],
       [ 60.        , 204.        ], ...  And so on ...



>>> triangulation["triangles"]
array([[ 89, 106, 105],
       [ 99,  35,  86],
       [110,  68,  87],
       [ 47,  66,  83],
       [ 72,  82,  74], ...  And so on ...

现在,我想使用 OpenGL 将此纹理扭曲并绘制为网格。我想知道我应该使用哪个原语?我认为 TRIANGLE_STRIP 是正确的解决方案,但这是一个复杂的三角剖分,而且显然只有一个 TRIANGLE_STRIP 是不够的。

你有 2 个数组。第一个数组包含二维顶点坐标,第二个数组包含索引。
您必须使用 glDrawElements 来绘制包含在第二个数组中的三角形图元。索引是指第一个数组的相应顶点坐标。

首先,您必须为顶点坐标创建一个浮点缓冲区,为索引创建一个整数缓冲区。最简单的方法是使用 NumPy,将嵌套列表或数组转换为数组缓冲区。

假设您有一个 vertices 的数组,格式为 [[x0, y0], [x1, y1], [x2, y2],...]indices 的数组,格式为 [[a0, b0, c0], [a1, b1, c1], [a2, b2, c2],...] ,那么可以这样创建缓冲区:

import numpy as np

vertex_array = np.array(vertices, dtype=np.float32)

no_of_indices = len(indices) * 3
index_array = np.array(indices, dtype=np.uint32)

同样可以通过使用 ctypes 而不是 NumPy 来完成。但是随后列表必须 flattened 形式分别为 [x0, y0, x1, y1, x2, y2,...] [a0, b0, c0, a1, b1, c1, a2, b2, c2,...]:

vertex_array = (ctypes.c_float * len(flat_vertices))(*flat_vertices)

no_of_indices = len(flat_indices) * 3
index_array = (ctypes.c_uint32 * no_of_indices)(*flat_indices)

创建一个顶点数组对象。见 Vertex Specification:

vao = glGenVertexArrays(1)
glBindVertexArray(vao)

ibo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_array, GL_STATIC_DRAW)

vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, vertex_array, GL_STATIC_DRAW)

glVertexAttribPointer(0, 2, GL_FLOAT, False, 0, None)
glEnableVertexAttribArray(0)

glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)

如果要绘制三角形图元,绑定顶点数组对象并调用 glDrawElements:

就足够了
glBindVertexArray(vao)
glDrawElements(GL_TRIANGLES, no_of_indices, GL_UNSIGNED_INT, None)

关于评论:

[...] I already know how to texture a plane by using glTexCoord2f then glVertex3f but didn't figure out how I can do it with a vao.

最简单的方法是创建一个单独的纹理坐标数组。假设您的纹理坐标 texAttr 的形式为 [[u0, v0], [u1, v1], [u2, v2],...]。 生成缓冲区很简单:

texAttr_array = np.array(texAttr, dtype=np.float32)

如果你有着色器程序,那么你必须添加纹理坐标属性:

例如

layout (location = 0) in vec2 vertex;
layout (location = 1) in vec2 texAttr;

并定义一个通用顶点属性数据数组。

vao = glGenVertexArrays(1)
glBindVertexArray(vao)

# [...] index buffer

vbo = glGenBuffers(2)
glBindBuffer(GL_ARRAY_BUFFER, vbo[0])
glBufferData(GL_ARRAY_BUFFER, vertex_array, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, vbo[1])
glBufferData(GL_ARRAY_BUFFER, texAttr_array, GL_STATIC_DRAW)

glVertexAttribPointer(0, 2, GL_FLOAT, False, 0, None)
glEnableVertexAttribArray(0)
glVertexAttribPointer(1, 2, GL_FLOAT, False, 0, None)
glEnableVertexAttribArray(1)

glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)

如果您使用兼容性配置文件和固定功能属性,则必须指定 glTexCoordPointer and enable the client state GL_TEXTURE_COORD_ARRAY
注意,客户端状态 GL_VERTEX_ARRAYglVertexPointer is mapped to the vertex attribute 0. See What are the Attribute locations for fixed function pipeline in OpenGL 4.0++ core profile?:

vao = glGenVertexArrays(1)
glBindVertexArray(vao)

# [...] index buffer

vbo = glGenBuffers(2)
glBindBuffer(GL_ARRAY_BUFFER, vbo[0])
glBufferData(GL_ARRAY_BUFFER, vertex_array, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, vbo[1])
glBufferData(GL_ARRAY_BUFFER, texAttr_array, GL_STATIC_DRAW)

glVertexPointer(2, GL_FLOAT, 0, None)
glEnableClientState(GL_VERTEX_ARRAY)
glTexCoordPointer(2, GL_FLOAT, 0, None)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)

glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)

请注意,如果您不使用着色器,则必须通过

启用二维纹理
glEnable(GL_TEXTURE_2D)

关于评论:

I d'like to displace vertex one by one [...]

可以通过glBufferSubData更改单个顶点坐标,其中第二个参数是顶点坐标的字节偏移量。坐标 i 的偏移量将是 4*2*i (4 是以字节为单位的浮点数大小,每个坐标由 2 个分量组成 xy)。