PDA

View Full Version : VAO and shaders



Filip
06-22-2009, 06:55 AM
I am trying to get a grip on how to use VAOs in a good way.

I understand the basics and have some simple test code that works alright, but I don't understand how one is supposed to use VAOs with shaders.

Some working pseudo-code for what I am doing, but want to improve on:



// Setup
glGenVertexArrays(1, &gVAO);
glBindVertexArray(gVAO);

sizeInBytes = sizeof(float)*3*NUM_VERTICES;
glGenBuffers(1, &gVBO);
glBindBuffer(GL_ARRAY_BUFFER, gVBO);
glBufferData(GL_ARRAY_BUFFER, sizeInBytes, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeInBytes, vertices);

sizeInBytes = sizeof(unsigned int)*3*NUM_INDICES;
glGenBuffers(1, &gEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeInBytes,NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeInBytes, indices);


// Draw
GLuint positionLoc = glGetAttribLocation(gShader, "inPosition");
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionLoc);

GLuint projMatrixLoc = glGetUniformLocation(gShader, "inProjectionMatrix");
glUniformMatrix4fv(projMatrixLoc, 1, GL_FALSE, gProjectionMatrixf);

GLuint mvMatrixLoc = glGetUniformLocation(gShader, "inModelViewMatrix");
glUniformMatrix4fv(mvMatrixLoc, 1, GL_FALSE, gModelViewMatrixf);

glUseProgram(gShader);

glBindVertexArray(gVAO);
glDrawElements(GL_TRIANGLES, NUM_INDICES*3, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));


I want to put the attrib bindings within the VAO. (which I thought was one of the main reasons for using it in the first place)
If I use deprecated glVertexPointer code this works fine but obviously, in this example, I want to try out how to work with "pure" opengl 3.

Since I have no idea on which shader to use at the setup point (it might change dynamically after all) I cannot use

glGetAttribLocation(gShader, "inPosition");

The only thing I can think of is me inventing a standard for myself always using location 0 for position, 1 for normals etc. but that seems to only recreate what opengl had as a standard before it got deprecated.

Am I approaching this the wrong way and can anyone with some insight clarify the situation?

Xmas
06-22-2009, 07:49 AM
The only thing I can think of is me inventing a standard for myself always using location 0 for position, 1 for normals etc. but that seems to only recreate what opengl had as a standard before it got deprecated.
That's hardly the same as you are able to define your attribute set exactly to your needs instead of being tied to a limited set of attributes.

If you want to use one VAO with multiple shaders, use a fixed mapping of attributes to attribute locations (i.e. use glBindAttribLocation - that's what it's meant for after all).

Filip
06-22-2009, 09:42 AM
Ok, so lets say I do my setup: (pseudo code again just to make sure the context of the usage is somewhat clear)


// Shader
#define MY_POSITION_LOCATION 7

glAttachShader(gShader, vertexShader);
glAttachShader(gShader, fragmentShader);

glBindAttribLocation(gShader, MY_POSITION_LOCATION, "inPosition");

glLinkProgram(gShader);



And the for the geometry:



glGenVertexArrays(1, &gVao);
glBindVertexArray(gVao);

glGenBuffers(1, gVbo);
glBindBuffer(GL_ARRAY_BUFFER, gVbo);
glBufferData(GL_ARRAY_BUFFER, sizeInBytes, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeInBytes, vertices);

glEnableVertexAttribArray(MY_POSITION_LOCATION);
glVertexAttribPointer(MY_POSITION_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0);

glGenBuffers(1, gEbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gEbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSizeInBytes, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexSizeInBytes, indices);


and call this in my drawing code:



GLuint projMatrixLoc = glGetUniformLocation(gShader, "inProjectionMatrix");
glUniformMatrix4fv(projMatrixLoc, 1, GL_FALSE, gProjectionMatrixf);
printf("projMatrixLoc: %d\n", projMatrixLoc);

GLuint mvMatrixLoc = glGetUniformLocation(gShader, "inModelViewMatrix");
glUniformMatrix4fv(mvMatrixLoc, 1, GL_FALSE, gModelViewMatrixf);
printf("mvMatrixLoc: %d\n", mvMatrixLoc);

glBindVertexArray(gVao);
glDrawElements(GL_TRIANGLES, numIndices*3, GL_UNSIGNED_INT, BUFFER_OFFSET(0));


Is this correct thinking in principle regarding this extension/core feature?

Xmas
06-22-2009, 10:09 AM
That seems to be about right, however I would also recommend getting the uniform locations once at initialisation time and storing them in some data structure along with the program name.

Also, you need to make sure the program you want is active (glUseProgram) before you set uniform values with glUniform*.

Alfonse Reinheart
06-22-2009, 11:18 AM
The only thing I can think of is me inventing a standard for myself always using location 0 for position, 1 for normals etc. but that seems to only recreate what opengl had as a standard before it got deprecated.

OpenGL never had that as standard. OpenGL never defined how generic attributes alias with regular ones.

NVIDIA did define such a mapping, but that was their choice.

And yes, that is the only way to do what you are talking about. Unfortunately.

Nicolas Lelong
06-22-2009, 12:25 PM
always using location 0 for position
OpenGL never had that as standard. OpenGL never defined how generic attributes alias with regular ones.
From my understanding, the location 0 is the only one that is specified, it must match the vertex position, it is the only attribute that has no 'current state' and must be specified at each vertex.

Filip
06-22-2009, 03:39 PM
Ok, good to get things cleared up little. I understand that there are many ways to use this but it would be great if there were a series of examples showcasing how a good use of "the new opengl" looks... Is there work going on a a new red book? Is there some community effort that I have missed?

Anyway, thanks for the answers.

Xmas, thanks for the hints. Just to let you know, I set the uniforms after the program is bound. Regarding saving program uniforms at setup, do you mean something like:



struct _MyUniforms {
GLuint projectionMatrixLoc;
char* projectionMatrixName;
...
};

struct _MyUniforms uniforms = { MY_PROJECTION_LOC, "inProjectionMatrix"};


that can be used when binding like so:


glUniformMatrix4fv(MY_PROJECTION_LOC, 1, GL_FALSE, gProjectionMatrixf);



Or did you have something else in mind?


Alfonse, I didn't mean that OpenGL had a standard for aliasing generic vertex attributes. I just meant that there was things like glVertexPointer and glNormalPointer that you could access through gl_Vertex and gl_Normal in your shader code. Now I have to create something very similar myself. It is ok, if that is the way it is, It just feels slightly weird recreating so much from classic opengl like matrix setup, rotations, how parameters gets passed etc. It's more like OpenGL has turned into a very thin hardware abstraction layer instead of a full fledged Graphics Library. I understand this is desireble if you write a new game engine that has to squeeze every last drop of power out of the hardware, but if you just want to make some quick graphics hack you have to write so much more now... anyway that is another discussion :)

Xmas
06-23-2009, 02:24 AM
Xmas, thanks for the hints. Just to let you know, I set the uniforms after the program is bound. Regarding saving program uniforms at setup, do you mean something like:



struct _MyUniforms {
GLuint projectionMatrixLoc;
char* projectionMatrixName;
...
};

struct _MyUniforms uniforms = { MY_PROJECTION_LOC, "inProjectionMatrix"};


that can be used when binding like so:


glUniformMatrix4fv(MY_PROJECTION_LOC, 1, GL_FALSE, gProjectionMatrixf);



Or did you have something else in mind?
The uniform location is not a compile-time constant. You still need to query it with glGetUniformLocation, but you should only query it once at init time, not per draw loop.

How exactly the structure should look depends on whether you use a similar set of uniforms across many shaders, and how much of the uniform setup you want hard-coded as opposed to content-driven.

V-man
06-23-2009, 11:49 AM
It's more like OpenGL has turned into a very thin hardware abstraction layer instead of a full fledged Graphics Library. I understand this is desireble if you write a new game engine that has to squeeze every last drop of power out of the hardware, but if you just want to make some quick graphics hack you have to write so much more now... anyway that is another discussion :)

Sounds like you are using GL 3.0 or 3.1 with a forward compatible context. What you want to do is make a backward compatible context.

Filip
06-23-2009, 01:31 PM
V-man: thanks, but no, that is not what I am looking for. I chose to do this actively, not by mishap. I am used to using OpenGL with the fixed pipeline and am currently trying to learn to use it in a way that is more forward looking.
As a matter of fact I use GL 2.1 with extensions on a Mac so it is not really gl3, even though that is what I am aiming at. Luckily VAOs are already suppored on the mac side so I don't need gl3 to try this out.

I am trying to find a way to work with this API in a good way now and in the coming years and I have no problem writing my own utility lib or adapting to a new order, it is just that I can't seem to find any good literature on how this is supposed to work. The spec just don't cut it as a tutorial ;)

Anyway, thanks to the constructive support from Xmas and Alfonse I think I figured most things out and I am currently trying things out.

Is there a "Best practices" document? A new red book? Something other? Or are new developers supposed to use the spec and extrapolate?

bootstrap
06-23-2009, 07:47 PM
Amazon shows the new "OpenGL Programming Guide v3.10" and GLSL "OpenGL Shading Language v1.40" will be available as a set ($84) or separately on 2009 August 3. Hopefully another (5th edition) OpenGL SuperBible will not be too far behind (4th edition was released 2007 July 28).

Filip
06-24-2009, 12:51 AM
That sounds great, time to preorder I guess.

V-man
06-24-2009, 11:56 AM
V-man: thanks, but no, that is not what I am looking for. I chose to do this actively, not by mishap. I am used to using OpenGL with the fixed pipeline and am currently trying to learn to use it in a way that is more forward looking.
As a matter of fact I use GL 2.1 with extensions on a Mac so it is not really gl3, even though that is what I am aiming at. Luckily VAOs are already suppored on the mac side so I don't need gl3 to try this out.

I am trying to find a way to work with this API in a good way now and in the coming years and I have no problem writing my own utility lib or adapting to a new order, it is just that I can't seem to find any good literature on how this is supposed to work. The spec just don't cut it as a tutorial ;)

Anyway, thanks to the constructive support from Xmas and Alfonse I think I figured most things out and I am currently trying things out.

Is there a "Best practices" document? A new red book? Something other? Or are new developers supposed to use the spec and extrapolate?

The choice is yours if you want to make a forward compatible context or not. Backwards compatibility was made available since there was complaints that OpenGL is turning into a thin layer. At the same time, there are complaints that OpenGL is too complex and needs to be turned into a thin layer. I am part of the later group.
As for the unavailability of glRotate and such in forward compatible mode, http://www.opengl.org/wiki/FAQ#glTranslate.2C_glRotate.2C_glScale

And who knows, maybe one day we will write a GL 3.0 programming guide in the Wiki but don't hold your breath.

Filip
06-24-2009, 03:45 PM
V-man, thanks for your reply and hints. I guess that I also prefer a thin, effective, layer if I have to chose. I just want to understand this new thin layer and use it in an appropriate way.

Turning back to the original issue with Shaders and VAOs I believe that it is solved: Use predefined locations for attributes, i.e : create a convention and follow that. But observe that the "vertex pointer" has to be at index 0. (right?, that's is the only thing that works on my system at least)

The follow up question is of course: do one gain anything but convenience by using VAOs ?

Alfonse Reinheart
06-24-2009, 04:03 PM
But observe that the "vertex pointer" has to be at index 0.

No. All you need is for one of them to be at index zero. Any one of them.


do one gain anything but convenience by using VAOs ?

Performance improvements require IHVs to optimize them. Optimizations requires that actual shipping games of importance actually use them. Until then, you can expect them to be no faster than without using them.