How does glDrawArrays know what to draw? - opengl

I am following some begginer OpenGL tutorials, and am a bit confused about this snippet of code:
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject); //Bind GL_ARRAY_BUFFER to our handle
glEnableVertexAttribArray(0); //?
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); //Information about the array, 3 points for each vertex, using the float type, don't normalize, no stepping, and an offset of 0. I don't know what the first parameter does however, and how does this function know which array to deal with (does it always assume we're talking about GL_ARRAY_BUFFER?
glDrawArrays(GL_POINTS, 0, 1); //Draw the vertices, once again how does this know which vertices to draw? (Does it always use the ones in GL_ARRAY_BUFFER)
glDisableVertexAttribArray(0); //?
glBindBuffer(GL_ARRAY_BUFFER, 0); //Unbind
I don't understand how glDrawArrays knows which vertices to draw, and what all the stuff to do with glEnableVertexAttribArray is. Could someone shed some light on the situation?

The call to glBindBuffer tells OpenGL to use vertexBufferObject whenever it needs the GL_ARRAY_BUFFER.
glEnableVertexAttribArray means that you want OpenGL to use vertex attribute arrays; without this call the data you supplied will be ignored.
glVertexAttribPointer, as you said, tells OpenGL what to do with the supplied array data, since OpenGL doesn't inherently know what format that data will be in.
glDrawArrays uses all of the above data to draw points.
Remember that OpenGL is a big state machine. Most calls to OpenGL functions modify a global state that you can't directly access. That's why the code ends with glDisableVertexAttribArray and glBindBuffer(..., 0): you have to put that global state back when you're done using it.

DrawArrays takes data from ARRAY_BUFFER.
Data are 'mapped' according to your setup in glVertexAttribPointer which tells what is the definition of your vertex.
In your example you have one vertex attrib (glEnableVertexAttribArray) at position 0 (you can normally have 16 vertex attribs, each 4 floats).
Then you tell that each attrib will be obtained by reading 3 GL_FLOATS from the buffer starting from position 0.

Complementary to the other answers, here some pointers to OpenGL documentation. According to Wikipedia [1], development of OpenGL has ceased in 2016 in favor of the successor API "Vulkan" [2,3]. The latest OpenGL specification is 4.6 of 2017, but it has only few additions over 3.2 [1].
The code snippet in the original question does not require the full OpenGL API, but only a subset that is codified as OpenGL ES (originally intended for embedded systems) [4]. For instance, the widely used GUI development platform Qt uses OpenGL ES 3.X [5].
The maintainer of OpenGL is the Khronos consortium [1,6]. The reference of the latest OpenGL release is at [7], but has some inconsistencies (4.6 linked to 4.5 pages). If in doubt, use the 3.2 reference at [8].
A collection of tutorials is at [9].
https://en.wikipedia.org/wiki/OpenGL
https://en.wikipedia.org/wiki/Vulkan
https://vulkan.org
https://en.wikipedia.org/wiki/OpenGL_ES
see links in function references like https://doc.qt.io/qt-6/qopenglfunctions.html#glVertexAttribPointer
https://registry.khronos.org
https://www.khronos.org/opengl
https://registry.khronos.org/OpenGL-Refpages/es3
http://www.opengl-tutorial.org

Related

glEnableVertexArrayAttrib throw invalide size

Got 2 computers with both having modern graphic card. Both handle OpenGL 4.6. I got this code :
GLuint VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
//Setting up the VAO Attribute format
glVertexArrayAttribFormat(VAO, 0, 3 * sizeof(float), GL_FLOAT, GL_FALSE, 0);
glEnableVertexArrayAttrib(VAO, 0);
For some reason, this code work well in one of my computer (my triangle is printed correctly) (however OpenGL return error : but the program is running normally without crashing)
On my other computer (which got the exact same code) I get this error when glEnableVertexArrayAttrib(VAO,0) is called and my program crash.
I have no idea of why both error are occuring, my VAO is well generated in both computer. I checked for OpenGL context before calling this code and it is ok.
I can share, if needed, the GLFW and Glew Init code and also my shader generation code (both don't raise any error, and it have been used in some other test code I have done without any problem)
Your size is indeed incorrect, it is not in bytes. The size denotes number of elements per vertex, e.g. vec3 corresponds to 3:
glVertexArrayAttribFormat(VAO, 0, 3 , GL_FLOAT, GL_FALSE, 0);
As why it works on one PC, well, get used to it. OpenGL implementations in GPU drivers are usually quite magical and forgiving until they are not.
The actual sizes in bytes come to play in relativeoffset argument and also in glVertexArrayVertexBuffer while specifying also the offset and stride.
Consider using glCreateVertexArrays with direct state access functions. The reason is glGenVertexArrays only reserves a VAO object, but does not create it (on some drivers it does). Only the first glBindVertexArray(VAO); will create such object. This is dangerous since any calls using DSA might fail before the bind and there is no reason to bind if you use DSA in the first place. I suspect you are doing it already, but other people who will see your code and the "wasteful" bind might not known this.

Is it necessary to bind all VBOs (and textures) each frame?

I'm following basic tutorial on OpenGL 3.0. What is not clear to me why/if I have to bind, enable and unbind/disable all vertex buffers and textures each frame.
To me it seems too much gl**** calls which I guess have some overhead. For example here you see each frame several blocks like:
// do this for each mesh in scene
// vertexes
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glVertexAttribPointer( 0, 3, GL_FLOAT,GL_FALSE,0,(void*)0);
// normals
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, normal_buffer );
glVertexAttribPointer( 1, 3, GL_FLOAT,GL_FALSE,0,(void*)0);
// UVs
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, uv_buffer );
glVertexAttribPointer( 2, 2, GL_FLOAT,GL_FALSE,0,(void*)0);
// ...
glDrawArrays(GL_TRIANGLES, 0, nVerts );
// ...
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
imagine you have not just one but 100 different meshes each with it's own VBOs for vertexes,normas,UVs. Should I really do this procedure each frame for each of them? Sure I can encapsulate that complexity into some function/objects, but I worry about overheads of this gl**** function calls.
Is it not possible some part of this machinery to move from per frame loop into scene setup ?
Also I read that VAO is a way how to pack corresponding VBOs for one object together. And that binding VAO automatically binds corresponding VBOs. So I was thinking that maybe one VAO for each mesh (not instance) is how it should be done - but according to this answer it does not seems so?
First things first: Your concerns about GL call overhead have been addressed with the introduction of Vertex Array Objects (see #Criss answer). However the real problem with your train of thought is, that you equate VBOs with geometry meshes, i.e. give each geometry its own VBO.
That's not how you should see and use VBOs. VBOs are chunks of memory and you can put the data of several objects into a single VBO; you don't have to draw the whole thing, you can limit draw calls to subsets of a VBO. And you can coalesce geometries with similar or even identical drawing setup and draw them all at once with a single draw call. Either by having the right vertex index list, or by use of instancing.
When it comes to the binding state of textures… well, yeah, that's a bit more annoying. You really have to do the whole binding dance when switching textures. That's why in general you sort geometry by texture/shader before drawing, so that the amount of texture switches is minimized.
The last 3 or 4 generations of GPUs (as of late 2016) do support bindless textures though, where you can access textures through a 64 bit handle (effectively the address of the relevant data structure in some address space) in the shader. However bindless textures did not yet make it into the core OpenGL standard and you have to use vendor extensions to make use of it.
Another interesting approach (popularized by Id Tech 4) is virtual textures. You can allocate sparsely populated texture objects that are huge in their addressable size, but only part of them actually populated with data. During program execution you determine which areas of the texture are required and swap in the required data on demand.
You should use vertex array object (generated by glGenVertexArrays). Thanks to it you don't have to perform those calls everytime. Vertex buffer object stores:
Calls to glEnableVertexAttribArray or glDisableVertexAttribArray.
Vertex attribute configurations via glVertexAttribPointer.
Vertex buffer objects associated with vertex attributes by calls to
glVertexAttribPointer.
Maybe this will be better tutorial.
So that you can generate vao object, then bind it, perform the calls and unbind. Now in drawing loop you just have to bind vao.
Example:
glUseProgram(shaderId);
glBindVertexArray(vaoId);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glUseProgram(0);

Rendering in opengl 3.1+ without VBO using client side vertex array

I want to know if its possible to draw in opengl 3.1+ core profile using vertex arrays ( client side rendering not immediate mode) without using the VBO. I know in opengl 3.1+ core profile use of VAO is essential. I want to know if its possible to draw without using VBO by using vertex arrays (client side vertex array not immediate mode).
PS I am talking about client side vertex array not immediate mode. I want to know if I can create a VAO without VBO
I read this somewhere in opengl spec Immediate-mode vertex attribute specification, client-side vertex arrays is depricated. Vertex Attributes must be stored in one or more Buffer Objects, or be specified explicitly using glVertexAttrib*()​. Since OpenGL 3.2 using Vertex Array Objects is mandatory. So does this means I can specify vertex array data using the glVertexAttriPointer like this
glBindVertexArray(m_vaoHandle);
glEnableVertexAttribArray(m_attributes[POSITION_HANDLE]);
glVertexAttribPointer(m_attributes[POSITION_HANDLE], 3, GL_FLOAT, false, 0,vertexArray);
glEnableVertexAttribArray(m_attributes[TEXCOORDINATE_HANDLE]);
glVertexAttribPointer(m_attributes[TEXCOORDINATE_HANDLE], 2, GL_FLOAT, false, 0, textureArray);
glBindVertexArray(0);
Here vertexArray and textureArray are two arrays on CPU not VBO. Then while drawing them
glUseProgram(m_Program);
// Explicitly unbind buffer so attrib pointer knows to slurp in array.
glBindBuffer(GL_ARRAY_BUFFER, 0);
//bind vertex array object
glBindVertexArray(m_vaoHandle);
glDrawArrays(mode, 0, numVertices);
glBindVertexArray(0);
If its possible in OpenGL 3.1+, I would also like to know if its possible in OpenGLES 3.0
The core profile of OpenGL 3.2+ removed the ability to use client-side vertex arrays.
OpenGL ES 2.0 does allow the use of client-side vertex arrays, as do all higher versions of ES. However, the vertex shader input variable gl_VertexID (added in GLSL ES 3.00) is undefined when using client-side arrays. Oh, and client-side vertex arrays in ES 3.0 are mentioned in appendix F.1: Legacy Features. About them, it says:
The following features are present to maintain backward compatibility with OpenGL ES 2.0, but their use in not recommended as it is likely for these features to be removed in a future version.
While I wouldn't expect them to ever go away, clearly you are being advised not to use them.
As to the question of whether VAOs can work with client-side pointers at all, yes (when client-side pointers are supported at all, of course). VAOs always store vertex array state, no matter where those arrays come from. Calling glVertexAttribPointer with a client-side pointer will store the pointer inside the current VAO, just like it would store the buffer object+offset inside the VAO.
As an appendix to the accepted answer, you can 'kind of' emulate client arrays with UBO/SSBO.
On the other hand, it should not be too hard to implement your own 'client pointer' implementation via a hidden VBO and glBufferData.

glDrawElements emits GL_INVALID_OPERATION when using AMD driver on Linux

I have a bit of OpenGL 2.1 rendering code that works great when using an nVidia card/driver or the open-source AMD driver, but doesn't work when using the official fglrx driver. It just displays a grey screen (the glClear colour) and doesn't draw anything.
gDEBugger shows that glDrawElements is giving the error GL_INVALID_OPERATION. According to this page (What can cause glDrawArrays to generate a GL_INVALID_OPERATION error?) there are a lot of half-documented possible causes of this error. The shader is compiling fine, and the buffer size should be good too, and I am not using geometry shaders (obviously). It's just a simple draw call for a cube, with only one vertex attribute. Code is below.
glUseProgram(r->program->getProgram());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->texture->glID );
glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0);
glUniform4f(r->program->getUniform("colour").location, r->colour.x, r->colour.y, r->colour.z, r->colour.w);
glBindBuffer(GL_ARRAY_BUFFER, r->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, r->ibo);
glVertexAttribPointer(
r->program->getAttribute("position").location, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
sizeof(GLfloat)*3, // stride
reinterpret_cast<void*>(0) // array buffer offset
);
glEnableVertexAttribArray(r->program->getAttribute("position").location);
glUniformMatrix4fv(r->program->getUniform("modelToCameraMatrix").location, 1, GL_FALSE, glm::value_ptr(modelToCameraMatrix));
glDrawElements(
r->mesh->mode, // mode
r->mesh->nrOfInds, // count
GL_UNSIGNED_SHORT, // type
reinterpret_cast<void*>(0) // element array buffer offset
);
I have no idea what is going on or what might be causing this error. If anyone has any pointers as to what might be causing this to happen with the fglrx driver and not with any other driver, I would be glad to hear it. If you need more code, I'll happily provide it of course.
Let's dissect this:
glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0 + i);
The OpenGL 2.1 Spec states in section 2.15:
Setting a sampler’s value to i selects texture image unit number i. The values of i range from zero to the implementation-dependent maximum supported number of texture image units.
Setting the value of a sampler is only legal with calls to glUniform1i{v}.
If an implementation takes the liberty to allow statements like the above, it has to do some subtraction under the covers - i.e. it has to map (GL_TEXTURE0 + i) to some value in [0, MAX_UNITS - 1]. (Note: MAX_UNITS is just symbolic here, it's not an actual GL constant!).
This is simply non-conformant behavior, since GL_TEXTURE0 defines a very large value and which is far out of range of any ancient or modern GPU's number of available texture units.
BTW, GL_TEXTURE0 is not a prefix, it's a constant.
The GL 4.4 core specification is much more clear about this in section 7.10:
An INVALID_VALUE error is generated if Uniform1i{v} is used to set a sampler to a value less than zero or greater than or equal to the value of MAX_COMBINED_TEXTURE_IMAGE_UNITS.
Regarding glActiveTexture(): This functions takes a value in [GL_TEXTURE0, GL_TEXTURE0 + MAX_UNITS - 1]. It probably doesn't have to be like this, but that's the way it is defined.
It's no secret among the OpenGL community, that NVIDIA is often more lenient and their drivers sometimes take stuff that simply doesn't work on other hardware and drivers because it's simply not supposed to work.
As a general rule: never rely on implementation-specific behaviour. Rely only on the specification. If conformant and correct application code does not work with a specific implementation, it's a bug in the implementation.
I think I have found out what is wrong with the above code. The faulty line is
glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0 + i);
which should be
glUniform1i(r->program->getUniform("texture").location, i);
I'm not really able to find out exactly why it should be like this (glActiveTexture does need the GL_TEXTURE0 prefix), and why only the AMD driver craps out over it, so if anyone can elaborate on that, I would love to hear it. :)

When should glVertexAttribPointer be called?

It's not obvious from the documentation when glVertexAttribPointer should be called. It looks like it's part of VBO initialisation, but I notice example code calling it during rendering.
glVertexAttribPointer(vertexAttributeId, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), reinterpret_cast<const GLvoid*>(offsetof(Vertex2D, m_x)));
Should glVertexAttribPointer be called during initialisation of a GL_ARRAY_BUFFER or should it be called during rendering (after a call to glBindBuffer)?
The function glVertexAttribPointer specifies the format and source buffer (ignoring the deprecated usage of client arrays) of a vertex attribute that is used when rendering something (i.e. the next glDraw... call).
Now there are two scenarios. You either use vertex array objects (VAOs) or you don't (though not using VAOs is deprecated and discouraged/prohibited in modern OpenGL). If you're not using VAOs, then you would usually call glVertexAttribPointer (and the corresponding glEnableVertexAttribArray) right before rendering to setup the state properly. If using VAOs though, you actually call it (and the enable function) inside the VAO creation code (which is usually part of some initialization or object creation), since its settings are stored inside the VAO and all you need to do when rendering is bind the VAO and call a draw function.
But no matter when you call glVertexAttribPointer, you should bind the corresponding buffer right before (no matter when that was actually created and filled), since the glVertexAttribPointer function sets the currently bound GL_ARRAY_BUFFER as source buffer for this attribute (and stores this setting, so afterwards you can freely bind another VBO).
So in modern OpenGL using VAOs (which is recommended), it's usually similar to this workflow:
//initialization
glGenVertexArrays
glBindVertexArray
glGenBuffers
glBindBuffer
glBufferData
glVertexAttribPointer
glEnableVertexAttribArray
glBindVertexArray(0)
glDeleteBuffers //you can already delete it after the VAO is unbound, since the
//VAO still references it, keeping it alive (see comments below).
...
//rendering
glBindVertexArray
glDrawWhatever
When not using VAOs it would be something like that:
//initialization
glGenBuffers
glBindBuffer
glBufferData
...
//rendering
glBindBuffer
glVertexAttribPointer
glEnableVertexAttribArray
glDrawWhatever
glVertexAttribPointer is something that does not really belong to the Buffer nor to the Program it is - let's say - the glue between them. (The functionality of this is splitten in Opengl 4.3 in different functions VertexAttrib*Format, VertexAttribBinding and BindVertexBuffer aviable through the ARB_vertex_attrib_binding )
But if you want to say that it is part of something i would say it is part of the VAO that stores the state of which Buffer objects are bound, which attribs are enabled and how the Buffer data has to be passed to the Program.
So it belongs to part where you setup your VAOs.
EDIT
Simple setup that illustrates the order:
creating/setup of Buffers and creating programs
create VAO define which attribs are enabled, which buffers should be bound when the VAO is used and how that data is passed to the program (glVertexAttribPointer)
The association of buffer, generic vertex attribute and shader attribute variable are quite subtle. glVertexAttribPointer establishes this association. See OpenGL-Terminology for a detailed explanation.
Also, the link OpenGL-VBO,shader,VAO shows a working example with the necessary sequence of API calls.
glVertexAttribPointer has to be called (in most cases) when the appropriate (i.e. the one you want to use) VBO is bound. Then last parameter to it is offset in said buffer.
The last parameter is defined particularly nice in the reference manual:
Specifies a offset of the first component of the first generic vertex
attribute in the array in the data store of the buffer currently bound
to the GL_ARRAY_BUFFER target. The initial value is 0.
Appropriate Vertex Pointer Bindings with VBO sources are stored inside VAO, and you should use that if possible.
Short example (excuse my pseudocode):
// Setup
CreateVAO(); BindVAO();
CreateVBO(); BindVBO();
VertexAttribPointer(/*id*/ 0, 3, GL_FLOAT, /*starting at offset*/ 0);
// We have specified Vertex attribute bound to location 0,
// with size of 3 floats, starting at offset 0 of VBO we've just created.
//Draw
BindVAO();
Draw();