28

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.

Wonko
  • 651
  • 2
  • 6
  • 17

5 Answers5

45

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.

buergi
  • 6,039
  • 3
  • 19
  • 15
Jonathan Hartley
  • 15,462
  • 9
  • 79
  • 80
  • That's a nice pic. Care to specify a license ? – Bahbar Apr 04 '11 at 07:08
  • sure. Is there a good way to formally declare it, or do I just annoint it here in a comment? I'm not familiar with choosing licenses for images, but am happy to make is as open as possible. Is there a liberal CC license that is appropriate? (other suggestions welcome) – Jonathan Hartley Apr 06 '11 at 12:00
  • If you want CC, it has a nice tool to pick a license here: http://creativecommons.org/choose/. Image is one of the selectable formats. Otherwise, you can just say it's public domain if you dont care about keeping the copyright. In all cases, you ought to specify it directly on your website. (needless to say, IANAL, but hey, I try to care) – Bahbar Apr 06 '11 at 12:10
  • +1 This is a great diagram that really clarifies the draw calls. – Christian Rau Jul 02 '11 at 14:31
33

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.

George R
  • 3,784
  • 3
  • 34
  • 38
Bahbar
  • 17,760
  • 43
  • 62
  • 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 Nov 03 '09 at 10:31
  • 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 Nov 03 '09 at 10:54
  • 1
    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 Nov 03 '09 at 12:33
  • 1
    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. Nov 03 '09 at 12:56
  • 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. – Jonathan Hartley Jun 01 '10 at 10:36
  • Ah! I've realised this is more of a fiddly issue than I thought. I'll raise a new question to get it straight. – Jonathan Hartley Jun 01 '10 at 23:09
  • Regarding @Bahbar's comment about seams I can recommend [this tutorial](http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/) on VBO indexing. Made the problem very clear to me. – buergi Aug 16 '20 at 07:37
7

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.

MTGradwell
  • 151
  • 2
  • 2
6

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.

Ashwin Nanjappa
  • 76,204
  • 83
  • 211
  • 292
Goz
  • 61,365
  • 24
  • 124
  • 204
2

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.

Cristian Vrabie
  • 3,972
  • 5
  • 30
  • 49