tags:

views:

549

answers:

3

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.

+1  A: 

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 needung more than 8 verts in a cube definition.

Goz
+4  A: 

Some nomenclature first:

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.

For the actual way of solving this issue, see Goz's answer. Keep 24 separate vertices.

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.

Bahbar
Thanks again, Bahbar, for a very thorough answer :) But now I have trouble seeing the point of index buffers at all... The "red book" (7th ed) led me to believe it was meant to accomodate reuse vertex positions that appear more than once in a model. it even uses the cube as an example, stating "each vertex is used by exactly three faces". Of course, they do not mention normals/colors in the example...
Wonko
Is there any way to get this behaviour in OpenGL - to store position, normal and color separately (and uniquely) and connet them into vertices "on the fly"? It would be very convenient when working with .obj-files, which do exactly this, and algorithms that process each vertex-position once.
Wonko
Typically, a single vertex position with differing normals is a sign of a seam. As far as most meshes go, these vertices are the exception, not the norm. Most applications benefit greatly from using indices, because all the attributes on smooth surfaces happen to stay shared. Regarding .obj files... well, they were created for 3ds max, not for run-time rendering. You do have to do some processing to use them with any graphics API. There is no way to get this behavior in GL, because that's not how the __hardware__ does it.
Bahbar
To add to Bahbar's explanation, a cube is probably the worst model to seen the benefits of index buffers as every edge is a seam.
Simon H.
Hey. So if I'm drawing models where I expect almost every edge to be a seam (e.g. compositions of thousands of cubes), is it still worth using indices at all? Presumably I should measure performance to determine it empirically? Thanks for any thoughts, and sorry for the necromancer sub-query.
Tartley
Ah! I've realised this is more of a fiddly issue than I thought. I'll raise a new question to get it straight.
Tartley
+1  A: 

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.

Taken from http://tartley.com/?p=1045

Tartley