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.