Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I specify per-face colors when using indexed vertex arrays in OpenGL 3.x?

Tags:

opengl

I'm trying to render a cube using an array of 8 vertices and an index-array of 24 (4*6) indices into the vertex array. But how can I specify per-face variables, like colors and normals without using deprecated functions? For this I need a separate set of indices, but when I specify two index-arrays (GL_ELEMENT_ARRAY_BUFFERs) and point them to different shader-variables (with two calls to glVertexAttribPointer) something goes wrong, and it doesn't render anything (but doesn't report any errors either - checked with glGetError). Do I have to use different calls to glDrawElements for each face, with color and normal loaded into uniform variables?

To clarify, the problem arises when each of the 8 vertices are part of different faces and need different values for color and normal.

like image 397
Wonko Avatar asked Nov 02 '09 23:11

Wonko


People also ask

What is a VAO in OpenGL?

A Vertex Array Object (VAO) is an OpenGL Object that stores all of the state needed to supply vertex data (with one minor exception noted below). It stores the format of the vertex data as well as the Buffer Objects (see below) providing the vertex data arrays.

What are vertex attributes in OpenGL?

A vertex consists of one or more attributes, such as the position, the color, the normal, or texture coordinates. An OpenGL ES 2.0 or 3.0 app is free to define its own attributes; each attribute in the vertex data corresponds to an attribute variable that acts as an input to the vertex shader.

What does a VAO do?

A Vertex Array Object (VAO) is an object which contains one or more Vertex Buffer Objects and is designed to store the information for a complete rendered object. In our example this is a diamond consisting of four vertices as well as a color for each vertex.


5 Answers

People interested in whether to use array indices or not may find some utility in a diagram I created to graphically summarise the behaviour of the different OpenGL calls, so that you can see, for example, which ones draw contiguous blocks of vertex data, versus which ones 'skip around' using indices.

OpenGL draw calls.

I published this image under Creative Commons BY-SA, as noted at this source.

like image 170
Jonathan Hartley Avatar answered Oct 03 '22 23:10

Jonathan Hartley


The actual answer first:
See Goz's answer. Keep 24 separate vertices.

Some nomenclature second:
A Vertex is a set of vertex attributes. Please keep that distinction in mind when reading the following:

You have a misconception that using deprecated APIs would help you solve the issue. This is not the case. OpenGL handles (and has always handled) each vertex as a unique set of attributes. If you read the original spec carefully, you'll notice that when doing:

glNormal()
glVertex()
glVertex()
glVertex()

The specification clearly states that glNormal sets the current normal state, and that glVertex provokes a new vertex, copying in passing all the current state, including the current normal state. That is, even though you passed only one Normal, the GL still sees 3.

The GL, therefore, does not have "per-face" attributes.

Also, you're mixing index arrays GL_ELEMENT_ARRAY_BUFFER, that are used from glDrawElements(..., pointer), where pointer is an offset inside the index array, and vertex attribute arrays GL_ARRAY_BUFFER, that are used from glVertexAttribPointer (and all the deprecated glVertexPointer/glNormalPointer...

Each index that is in the index buffer will be used as an index into each of the attributes, but you can only specify a single index for each vertex. So, setting GL_ELEMENT_ARRAY_BUFFER and then calling glVertexAttribPointer, does not do at all what you think it does. It either uses the last array you set to GL_ARRAY_BUFFER for defining vertex attributes, or if you did not keep one bound, is interpreting your offset as a pointer (and will likely crash).

What you were trying to do, setting an index array for each vertex attribute, is not supported by GL. Let me restate this: you only have 1 index array per draw.

Some additional tidbits for the history enclined:

glVertex is a bit of a misnomer. It specifies only the Vertex position. But, and this is what it gets its name from, it also provokes a vertex to be passed to the GL. For the API to be completely clean, you could have imagined having to do 2 calls:

// not valid code
glPosition(1,2,3); // specifies the current vertex position
glProvoke(); // pass the current vertex to GL

However, when GL was first specified, Position was always required, so fusing those 2 to provoke a vertex made sense (if only to reduce the API call count).

Fast forward to vertex_program_arb: Trying to get away from the fixed-function model while still remaining compatible meant that the special nature of glVertex had to be carried forward. This was achieved by making the vertex attribute 0 provoking, and a synonym to glVertex.

Fast forward to GL3.2: the Begin/End model is gone, and all this specification of what provokes a vertex can finally go away, along with the management of the current state. So can all the semantic APIs (the glVertex*, glNormal*...), since all inputs are just vertex attributes now.

like image 24
Bahbar Avatar answered Oct 03 '22 22:10

Bahbar


Interesting explanations, but they seem to obscure the fact that the original question contains its own answer: "Do I have to use different calls to glDrawElements for each face, with color and normal loaded into uniform variables?" - If having to feed the same vertex position data to the graphics processor three times when once ought to suffice offends one's aesthetic sense (and it ought to) then it's yes to that.

The original questioner doesn't want to know that OpenGL can't do what he's trying to do, and that he needs to maintain twenty-four vertices for a cube which both standard math and common sense tell us should only need eight. He already knows that he CAN do what he wants to, and he even knows how. He just wants confirmation that his proposed approach does actually make sense, and that there isn't some better way to do it which he has overlooked.

Making calls for each face does add an overhead of its own, but that's hardly important when drawing just one cube. When drawing many, all the same-attribute faces can be batched together, e.g. all the South-facing then all the West, or all the red then all the green, or whatever.

like image 39
MTGradwell Avatar answered Oct 03 '22 22:10

MTGradwell


You need more than 8 vertices. Verts may share positions but unless everything else in the vertex is unique it is not a unique vertex. Normals is another classic reason for needing more than 8 verts in a cube definition.

like image 22
Goz Avatar answered Oct 03 '22 22:10

Goz


In OpenGL 3.x you have access to type interpolation qualifiers. Using the flat qualifier, you would force the use of the variable passed by the provoking vertex only. In some cases this should be enough to do what you want, but has the downside that you need to pay very careful attention to the ordering in which you draw the vertexes.

like image 44
Cristian Vrabie Avatar answered Oct 03 '22 22:10

Cristian Vrabie