2

I've the following image :

enter image description here

That I was able to triangulate like the following : enter image description here

I did it using the triangle library of python. My triangulation result is stored into a dict object which look like that:

>>> 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 ...

Now, I want to distord and draw this texture as a mesh using OpenGL. I was wondering which primitive should I use? I think that TRIANGLE_STRIP is the right solution but it's a complex triangulation and it seems obvious that only one TRIANGLE_STRIP will not be enough.

snoob dogg
  • 2,491
  • 3
  • 31
  • 54

1 Answers1

4

You've 2 arrays. The first array contains the 2 dimensional vertex coordinates and the second array contains the indices.
You have to use glDrawElements tor draw the triangle primitives which are contained in the 2nd array. The indices refer to the corresponding vertex coordinates of the 1st array.

First you've to create a floating point buffer for the vertex coordinates and an integral buffer for the indices. The easiest way is to use NumPy, to transform nested list or arrays to array buffers.

Assuming that you've an array of vertices in the form [[x0, y0], [x1, y1], [x2, y2],...] and an array of indices in the form [[a0, b0, c0], [a1, b1, c1], [a2, b2, c2],...], then the buffers can be created like this:

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)

The same can be done by using ctypes, instead of NumPy. But then the lists have to be flattened in the form [x0, y0, x1, y1, x2, y2,...] respectively [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)

Create a vertex array object. See 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)

If you want to draw the triangle primitives it is sufficient to bind the vertex array object and to call glDrawElements:

glBindVertexArray(vao)
glDrawElements(GL_TRIANGLES, no_of_indices, GL_UNSIGNED_INT, None)

Regarding to the comment:

[...] 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.

The easiest way is to create a separate array of texture coordinates. Assuming you've the texture coordinates texAttr in the form [[u0, v0], [u1, v1], [u2, v2],...]. Generating the buffer is straight forward:

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

If you have a shader program, then you've to add the texture coordinate attribute:

e.g.

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

and to define an array of generic vertex attribute data.

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)

If you use compatibility profile and the fixed function attributes, then you've to specify the glTexCoordPointer and enable the client state GL_TEXTURE_COORD_ARRAY.
Note, the client state GL_VERTEX_ARRAY and glVertexPointer 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)

Note, if you don't use a shader, then 2 dimensional texturing has to be enabled by

glEnable(GL_TEXTURE_2D)

Regarding to the comment:

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

A single vertex coordinate can be changed by glBufferSubData, where the 2nd parameter is a byte offset to the vertex coordinate. The offset for the coordinate i would be 4*2*i (4 is the size of float in bytes, and each coordinate consist of 2 components x and y).

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thank you, how can this vertex array be textured ? And do you think it will be easy to modify its vertex position in real time ? – snoob dogg Mar 10 '19 at 15:03
  • For the texturing you need texture coordinates. See [How do opengl texture coordinates work?](https://stackoverflow.com/questions/5532595/how-do-opengl-texture-coordinates-work). What do you mean by *" modify its vertex position in real time"*? Do you want to displace or rotate the mesh? Do you want to generate distortions? – Rabbid76 Mar 10 '19 at 15:06
  • I d'like to displace vertex one by one. 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. – snoob dogg Mar 10 '19 at 15:13
  • 1
    and I'm still learning and it's damn hard ^^ . Not sure If it should be a vertex or a fragment shader. I'm sure that your answer is correct but I need to make it work before accept it. Good exercice by the way. – snoob dogg Mar 10 '19 at 21:17
  • 1
    of course, I can't make it work : ) Are you sure about `glEnableClientState(GL_VERTEX_ARRAY)` after `glTexCoordPointer()` ? it doesn't looks like the example I found here : https://www.khronos.org/opengl/wiki/VBO_-_just_examples where `GL_TEXTURE_COORD_ARRAY` is used instead of `GL_VERTEX_ARRAY`. it perturbs me a bit.. – snoob dogg Mar 17 '19 at 14:43
  • and about `glTexCoordPointer(2, False, 0, None)`, it is `False` and not `GL_FLOAT` ? not sure about this too – snoob dogg Mar 17 '19 at 14:48
  • @snoobdogg Of course it has to be `GL_TEXTURE_COORD_ARRAY` respectively `GL_FLOAT`. Further `glEnable(GL_TEXTURE_2D)` is needed – Rabbid76 Mar 17 '19 at 14:50
  • it wooorkks : ) – snoob dogg Mar 17 '19 at 15:28
  • 1
    By the way, I had to invert tex coordinates vertically: `texAttr = [[vert[0] / img_width, img_height - vert[1] / img_height] for vert in vertices]` and then `vertex_array[:, 1] = img_height - vertex_array[:, 1]` – snoob dogg Mar 17 '19 at 15:54