1
\$\begingroup\$

Follow up for my previous question, whose answer lead to another question:

As the OpenGL specification documentation states, in glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices), indices "Specifies a pointer to the location where the indices are stored."

So why does calling gl::glDrawElements(gl::GL_TRIANGLES, IndexBuffer.size(), gl::GL_UNSIGNED_INT, (void*)(0)); work, but calling gl::glDrawElements(gl::GL_TRIANGLES, IndexBuffer.size(), gl::GL_UNSIGNED_INT, &(IndexBuffer.at(0))); doesn't ?

\$\endgroup\$
1
\$\begingroup\$

As stated in the comment:

Looking at the spec this actually is not stated there.. for whatever reason.

In the case that no buffer object is currently bound to GL_ELEMENT_ARRAY_BUFFER the statement in the spec is true.

But when there is an active buffer object, directly bound as GL_ELEMENT_ARRAY_BUFFER or via a VAO, the last parameter acts as an offset into the bound buffers data, starting at 0.

The spec really could be more specific here..

I can't clearly say it for all functions, but usually when the last parameter of an OpenGL function is of void* type it acts like this. Sadly it seems like that it's not always stated which buffer object target applies to this for which function. In case of the glTexImage*() functions it does, for example.

\$\endgroup\$
1
\$\begingroup\$

This is an artefact of the history of OpenGL.

For OpenGL versions 1.1 to 1.4, providing a pointer to actual system memory data was the only way to use glDrawElements; the last parameter was interpreted as a pointer to data rather than an offset into a buffer, and the second code sample you give (gl::glDrawElements(gl::GL_TRIANGLES, IndexBuffer.size(), gl::GL_UNSIGNED_INT, &(IndexBuffer.at(0)));) actually did work.

With OpenGL 1.5 or the GL_ARB_vertex_buffer_object extension this behaviour changed. Now if a non-zero buffer object is bound to GL_ELEMENT_ARRAY_BUFFER the pointer is re-interpreted as an offset into the buffer. I recommend reading the "Issues" section of the extension specification as it often gives insight into the reasons why certain decisions which may seem odd today actually made some degree of sense at the time they were originally made. For now it will suffice to quote:

The standard GL pointer APIs have been overloaded to be able to refer to offsets within these buffers, rather than adding new entry points ..... It is easily and cleanly added with just the addition of a binding point for the index buffer object. Since our approach of overloading pointers works for any pointer in GL, no additional APIs need be defined...

In modern OpenGL this behaviour changed again. Because it was no longer legal to source vertex or index data from system memory pointers, the only variant that may be used is the overloaded variant, where the parameter is still a pointer but is reinterpreted as an offset.

So hopefully you'll see that what looks like a very odd API today was actually originally designed as something quite different but because of the way the API evolved it too evolved into what it is today.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.