7

For some functions in OpenGL, one must specify a byte offset, such as in glVertexAttribPointer(), for stride. At first I would have guessed that it would be a normal number value like an integer. But upon inspection, I realized that it needs to be casted to void* (more specifically GLvoid*). My question is: what is the intended meaning of void* and why must it be used for a byte offset?

glampert
  • 4,371
  • 2
  • 23
  • 49
sgtHale
  • 1,507
  • 1
  • 16
  • 28
  • Um, [this says](https://www.opengl.org/sdk/docs/man/html/glVertexAttribPointer.xhtml) *stride* is a `GLsizei`, which is [apparently](http://stackoverflow.com/questions/8996743/why-isnt-glsizei-defined-as-unsigned) a `int`. – T.C. Aug 01 '14 at 06:29
  • 2
    possible duplicate of [How to cast int to const GLvoid\*?](http://stackoverflow.com/questions/23177229/how-to-cast-int-to-const-glvoid) – Reto Koradi Aug 01 '14 at 06:32
  • Well that other question is more WHY it needs to be a pointer. How is void* different than char*? -should have been my question. – sgtHale Aug 01 '14 at 06:40
  • 2
    I explained the gory details in http://stackoverflow.com/a/8284829/524368 – datenwolf Aug 01 '14 at 13:40

3 Answers3

6

glVertexAttribPointer() is an older function from before Vertex Buffer Objects.

Before VBO's your vertex data would be stored in client side arrays and you would need to pass a pointer to the data to OpenGL before you could draw.

When VBO's came along they repurposed this function by allowing the pointer to be used to pass an integer offset.

e.g. void* offset = (void*)offsetof(vertexStructName, vertexMemberName);

CynicismRising
  • 940
  • 4
  • 5
3

Some OpenGL functions, such as glDrawElements take a GLvoid * parameter that is context dependant. On old GL, pre Vertex Buffers, the programmer would pass an array of integer indexes directly to glDrawElements, like this:

const GLuint indexes[] = { ... };
glDrawElements(GL_TRIANGLES, numIndexes, GL_UNSIGNED_INT, indexes);

That was called the immediate mode drawing.

When Vertex and Index Buffers were introduced, the OpenGL architecture board decided that they should reuse the existing interfaces, thus giving a new context dependant meaning to that last void pointer parameter of glDrawElements, glVertexAttribPointer and a few other similar functions.

With Index Buffers, the rendering data is already on the GPU, so the void pointer param is meant to be an offset into the buffer. E.g.: The first index to render. Leading to a new usage of glDrawElements:

size_t firstIndex = ...
size_t indexDataSize = sizeof(GLuint);
glDrawElements(GL_TRIANGLES, numIndexes, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid *>(firstIndex * indexDataSize));

This applies to all the older functions that were repurposed on modern OpenGL, like glDrawElementsInstanced, glDrawElementsBaseVertex, glDrawRangeElements and others.

Now in the specific case of glVertexAttribPointer:

void glVertexAttribPointer(GLuint index​, GLint size​, GLenum type​, GLboolean normalized​, GLsizei stride​, const GLvoid * pointer​);

The const GLvoid * pointer​ parameter is an offset in bytes from the beginning of the vertex to the given element. Again, it was kept like this because the function existed before Vertex/Index Buffers and was repurposed to work with them, whereas in the immediate mode days, you would pass an array of vertexes as the 'pointer' parameter.

So in the old days, glVertexAttribPointer would be used somewhat like:

const vec3 positions[] = { ... };
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, positions);

And in modern GL, you would use:

struct Vert {
    vec3 position;
    vec3 normal;
};

size_t offset;

offset = offsetof(Vert, position);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, reinterpret_cast<const GLvoid *>(offset));

offset = offsetof(Vert, normal);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, reinterpret_cast<const GLvoid *>(offset));
glampert
  • 4,371
  • 2
  • 23
  • 49
0

It's called void pointer and it can point at any type, like char* does - but it's slightly different. Firstly you have to remember that the pointer is just a number, an address to right location, nothing more. To use data pointed by void pointer you have to explicitly cast it to correct type, they cannot be dereferenced directly. They allow you to skip type checking and they should be avoided.

Another difference I see is clarity of intentions. When you see char* pointer you can't be sure it'll point at char/char array. It can be something else, it's highly situational: I saw cases when it's confusing, sometimes it's obvious; on the other hand void pointer intentions are clear: it can be really anything and you have to consider the context of application to find the type.

Another thing is pointer arithmetics, AFAIR in case of void pointers it's not defined by standard.

I guess void pointers in OpenGL are used to make API more general, giving more control to drivers (it makes changing types from floats to doubles easier, by settings). But that's only speculation, confirmation is needed.

gecio
  • 41
  • 6
  • 2
    -1: [unsigned] char* is actually allowed to alias pointers to any type T. – thokra Aug 01 '14 at 12:53
  • Yes, that's true, I missed that. But when you use char* you don't know it was intended to point at char or on something else. With void* that intention is clear. I'll edit my answer in free time. – gecio Aug 01 '14 at 13:09
  • Casting to `void*` *completely* erases the type. You need to know exactly to what you are allowed to cast back to, otherwise you'll likely violate aliasing rules. In this regard, conversions to `void*` bare the same "disadvantage" as conversion to `char*`. If you don't want to violate aliasing rules, you need to know what type you came from, if you want to be able to convert back to a permissible type. – thokra Aug 01 '14 at 16:12
  • Yes, I never said otherwise. I'll repeat myself: with char* there is room for interpretation, under the pointer can be char or something else, you are not forced to cast. In void* scenario it's different - you have to cast to use the data. – gecio Aug 01 '14 at 19:07