59

I am learning OpenGL from this tutorial. My question is about the specification in general, not about a specific function or topic. When seeing code like the following:

glGenBuffers(1, &positionBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

I'm confused about the utility of calling the bind functions before and after setting the buffer data. It seems superfluous to me, due to my inexperience with OpenGL and Computer Graphics in general.

The man page says that:

glBindBuffer lets you create or use a named buffer object. Calling glBindBuffer with target set to GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER or GL_PIXEL_UNPACK_BUFFER and buffer set to the name of the new buffer object binds the buffer object name to the target. When a buffer object is bound to a target, the previous binding for that target is automatically broken.

What exactly is the concept/utility of 'binding' something to a 'target' ?

AlexWei
  • 1,093
  • 2
  • 8
  • 32
manasij7479
  • 1,665
  • 1
  • 14
  • 22
  • 6
    one thing to keep in mind... when someone says "bind"... in general. `b=5` you just bound `5` to `b`. `c="asdf"` you just bound `"asdf"` to `c`. you "bind" "memory" to pretty (or ugly) "names". – n611x007 Jan 25 '17 at 21:27
  • 1
    @n611x007, This was exactly what I was looking for. I keep reading the documentation and see the word "bind" but had no clue what they meant by that. – KANJICODER Jun 16 '19 at 00:53

5 Answers5

45

the commands in opengl don't exist in isolation. they assume the existence of a context. one way to think of this is that there is, hidden in the background, an opengl object, and the functions are methods on that object.

so when you call a function, what it does depends on the arguments, of course, but also on the internal state of opengl - on the context/object.

this is very clear with bind, which says "set this as the current X". then later functions modify the "current X" (where X might be buffer, for example). [update:] and as you say, the thing that is being set (the attribute in the object, or the "data member") is the first argument to bind. so GL_ARRAY_BUFFER names a particular thing that you are setting.

and to answer the second part of the question - setting it to 0 simply clears the value so you don't accidentally make unplanned changes elsewhere.

andrew cooke
  • 45,717
  • 10
  • 93
  • 143
  • 7
    Thanks, If I understand your answer correctly,I can think of GL_ARRAY_BUFFER as a 'data member' of the context, to which I assign the positionBufferObject as an handle to the array vertexPositions. Correct ? – manasij7479 Mar 18 '12 at 14:28
  • 4
    may `stateful` be a word some could say here – n611x007 Jan 25 '17 at 20:24
  • It sounds like you have some idea of what "bind" means in your head but aren't able to accurately describe it to someone else – Andy Ray Jan 27 '19 at 01:49
  • 1
    I have a question, why don't we just skip the `binding` and just provide the handle as an extra parameter for glBufferData? (in other words, why OpenGL API doesn't accept handle as parameters?) – wdanxna Oct 17 '19 at 06:13
37

The OpenGL technique can be incredibly opaque and confusing. I know! I've been writing 3D engines based upon OpenGL for years (off and on). In my case part of the problem is, I write the engine to hide the underlying 3D API (OpenGL), so once I get something working I never see the OpenGL code again.

But here is one technique that helps my brain comprehend the "OpenGL way". I think this way of thinking about it is true (but not the whole story).

Think about the hardware graphics/GPU cards. They have certain capabilities implemented in hardware. For example, the GPU may only be able to update (write) one texture at a time. Nonetheless, it is mandatory that the GPU contain many textures within the RAM inside the GPU, because transfer between CPU memory and GPU memory is very slow.

So what the OpenGL API does is to create the notion of an "active texture". Then when we call an OpenGL API function to copy an image into a texture, we must do it this way:

1:  generate a texture and assign its identifier to an unsigned integer variable.
2:  bind the texture to the GL_TEXTURE bind point (or some such bind point).
3:  specify the size and format of the texture bound to GL_TEXTURE target.
4:  copy some image we want on the texture to the GL_TEXTURE target.

And if we want to draw an image on another texture, we must repeat that same process.

When we are finally ready to render something on the display, we need our code to make one or more of the textures we created and copied images upon to become accessible by our fragment shader.

As it turns out, the fragment shader can access more than one texture at a time by accessing multiple "texture units" (one texture per texture unit). So, our code must bind the textures we want to make available to the texture units our fragment shaders expect them bound to.

So we must do something like this:

glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, mytexture0);

glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, mytexture1);

glActiveTexture (GL_TEXTURE2);
glBindTexture (GL_TEXTURE_2D, mytexture2);

glActiveTexture (GL_TEXTURE3);
glBindTexture (GL_TEXTURE_2D, mytexture3);

Now, I must say that I love OpenGL for many reasons, but this approach drive me CRAZY. That's because all the software I have written for years would look like this instead:

error = glSetTexture (GL_TEXTURE0, GL_TEXTURE_2D, mytexture0);
error = glSetTexture (GL_TEXTURE1, GL_TEXTURE_2D, mytexture1);
error = glSetTexture (GL_TEXTURE2, GL_TEXTURE_2D, mytexture2);
error = glSetTexture (GL_TEXTURE3, GL_TEXTURE_2D, mytexture3);

Bamo. No need for setting all this state over and over and over again. Just specify which texture-unit to attach the texture to, plus the texture-type to indicate how to access the texture, plus the ID of the texture I want to attach to the texture unit.

I also wouldn't need to bind a texture as the active texture to copy an image to it, I would just give the ID of the texture I wanted to copy to. Why should it need to be bound?

Well, there's the catch that forces OpenGL to be structured in the crazy way it is. Because the hardware does some things, and the software driver does other things, and because what is done where is a variable (depends on GPU card), they need some way to keep the complexity under control. Their solution is essentially to have only one bind point for each kind of entity/object, and to require we bind our entities to those bind points before we call functions that manipulate them. And as a second purpose, binding entities is what makes them available to the GPU, and our various shaders that execute in the GPU.


At least that's how I keep the "OpenGL way" straight in my head. Frankly, if someone really, really, REALLY understands all the reasons OpenGL is (and must be) structured the way it is, I'd love them to post their own reply. I believe this is an important question and topic, and the rationale is rarely if ever described at all, much less in a manner that my puny brain can comprehend.

honestann
  • 1,374
  • 12
  • 19
  • 2
    Indeed, however I would clarify one thing bout this statement: ***1: generate a texture and assign its identifier to an unsigned integer variable.*** Although an identifier is taken from a pool of names (in OpenGL 3.0+ this pool is unique to each type of object, in older versions it did not have to be), it really is not an actual texture until the first time you bind the object. Nitpicky, I know, but GL is full of quirks like this :P Also, the `glSetTexture (...)` type of syntax does exist in the form of `GL_EXT_direct_state_access`, but the extension seems to be permastuck in `EXT` status :( – Andon M. Coleman Sep 18 '13 at 03:45
  • 1
    `glBindMultiTextureEXT (...)` it's called, in fact. So OpenGL does have a solution to the stupid bind-then-modify semantics, the ARB just are not in any big rush to fix the problem officially. The good news is `GL_EXT_direct_state_access` is implemented by all major vendors on most platforms. We may even have side-stepped the issue completely with the new bindless texture feature in OpenGL 4.4: `GL_ARB_bindless_texture`, where instead of binding textures you can just pass handles directly to GLSL shaders. – Andon M. Coleman Sep 18 '13 at 03:56
23

From the section Introduction: What is OpenGL?

Complex aggregates like structs are never directly exposed in OpenGL. Any such constructs are hidden behind the API. This makes it easier to expose the OpenGL API to non-C languages without having a complex conversion layer.

In C++, if you wanted an object that contained an integer, a float, and a string, you would create it and access it like this:

struct Object
{
    int count;
    float opacity;
    char *name;
};

//Create the storage for the object.
Object newObject;

//Put data into the object.
newObject.count = 5;
newObject.opacity = 0.4f;
newObject.name = "Some String";

In OpenGL, you would use an API that looks more like this:

//Create the storage for the object
GLuint objectName;
glGenObject(1, &objectName);

//Put data into the object.
glBindObject(GL_MODIFY, objectName);
glObjectParameteri(GL_MODIFY, GL_OBJECT_COUNT, 5);
glObjectParameterf(GL_MODIFY, GL_OBJECT_OPACITY, 0.4f);
glObjectParameters(GL_MODIFY, GL_OBJECT_NAME, "Some String");

None of these are actual OpenGL commands, of course. This is simply an example of what the interface to such an object would look like.

OpenGL owns the storage for all OpenGL objects. Because of this, the user can only access an object by reference. Almost all OpenGL objects are referred to by an unsigned integer (the GLuint). Objects are created by a function of the form glGen*, where * is the type of the object. The first parameter is the number of objects to create, and the second is a GLuint* array that receives the newly created object names.

To modify most objects, they must first be bound to the context. Many objects can be bound to different locations in the context; this allows the same object to be used in different ways. These different locations are called targets; all objects have a list of valid targets, and some have only one. In the above example, the fictitious target “GL_MODIFY” is the location where objectName is bound.

This is how most OpenGL objects work, and buffer objects are "most OpenGL objects."

And if that's not good enough, the tutorial covers it again in Chapter 1: Following the Data:

void InitializeVertexBuffer()
{
    glGenBuffers(1, &positionBufferObject);

    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

The first line creates the buffer object, storing the handle to the object in the global variable positionBufferObject. Though the object now exists, it does not own any memory yet. That is because we have not allocated any with this object.

The glBindBuffer function binds the newly-created buffer object to the GL_ARRAY_BUFFER binding target. As mentioned in the introduction, objects in OpenGL usually have to be bound to the context in order for them to do anything, and buffer objects are no exception.

The glBufferData function performs two operations. It allocates memory for the buffer currently bound to GL_ARRAY_BUFFER, which is the one we just created and bound. We already have some vertex data; the problem is that it is in our memory rather than OpenGL's memory. The sizeof(vertexPositions) uses the C++ compiler to determine the byte size of the vertexPositions array. We then pass this size to glBufferData as the size of memory to allocate for this buffer object. Thus, we allocate enough GPU memory to store our vertex data.

The other operation that glBufferData performs is copying data from our memory array into the buffer object. The third parameter controls this. If this value is not NULL, as in this case, glBufferData will copy the data referenced by the pointer into the buffer object. After this function call, the buffer object stores exactly what vertexPositions stores.

The fourth parameter is something we will look at in future tutorials.

The second bind buffer call is simply cleanup. By binding the buffer object 0 to GL_ARRAY_BUFFER, we cause the buffer object previously bound to that target to become unbound from it. Zero in this cases works a lot like the NULL pointer. This was not strictly necessary, as any later binds to this target will simply unbind what is already there. But unless you have very strict control over your rendering, it is usually a good idea to unbind the objects you bind.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 9
    Yes I've read it. The only reason I asked the question is because it wasn't clear to me. Especially what sentences like "The glBindBuffer function binds the newly-created buffer object to the GL_ARRAY_BUFFER binding target." actually meant. – manasij7479 Mar 18 '12 at 18:22
  • @manasij7479: `To modify most objects, they must first be bound to the context. Many objects can be bound to different locations in the context; this allows the same object to be used in different ways. These different locations are called targets; all objects have a list of valid targets, and some have only one. In the above example, the fictitious target “GL_MODIFY” is the location where objectName is bound.` The information is all there. If you have some suggestions for making it more clear, I'd be happy to hear them. – Nicol Bolas Mar 18 '12 at 18:24
  • 4
    ||"they must first be bound to the context" <- what is 'bound'? ||"different locations in the context" <- locations,targets ? ||I understand that going into more detail would be counter productive, but some analogies can't hurt. ||(If you're the author, thanks for writing this, great job. I had much trouble building it though.) – manasij7479 Mar 18 '12 at 18:32
  • @manasij7479: "bound" is the past-tense of "bind". As in what **Bind** functions do ;) – Nicol Bolas Mar 18 '12 at 18:37
  • 5
    :facepalm: I know that. I did not understand its meaning in the context of (context,target,location) etc. I don't know about others, but just after starting OpenGL, the unintuitive jargon is devastating. – manasij7479 Mar 18 '12 at 18:43
  • 1
    Just -1 for attitude. This is a little rude. – Michael Bishop Mar 10 '13 at 16:14
  • 3
    -1. If it was hard for him to comprehend the material in the tutorial, copy pasting it here without any reinterpretation won't be much help either. – wardd Aug 09 '13 at 14:32
  • It doesn't sound like you understand what binding actually means, based on the documentation you've quoted? – Andy Ray Jan 27 '19 at 01:54
  • @AndyRay: If that's not "what binding actually means", then what *does* binding actually mean? – Nicol Bolas Jan 27 '19 at 02:35
7

Binding a buffer to a target is something like setting a global variable. Subsequent function calls then operate on that global data. In the case of OpenGL all the "global variables" together form a GL context. Virtually all GL functions read from that context or modify it in some way.

The glGenBuffers() call is sort of like malloc(), allocating a buffer; we set a global to point to it with glBindBuffer(); we call a function that operates on that global (glBufferData()) and then we set the global to NULL so it won't inadvertently operate on that buffer again using glBindBuffer().

Ernest Friedman-Hill
  • 80,601
  • 10
  • 150
  • 186
  • Why does glgenBuffers() need to allocate more memory ? Can't the array I've already stored the data in, be reused? – manasij7479 Mar 18 '12 at 14:50
  • 5
    @manasij7479: glGenBuffers doesn't allocate memory, it generates a handle by which you can identify the buffer, you later bind using glBindBuffer – datenwolf Mar 18 '12 at 15:08
  • 3
    @manasij7479: ...and besides, GL buffers need to reside at GPU side, so data allocated at CPU side is "no use", just worth of a source for transfers. – MaKo Jun 16 '15 at 09:56
2

OpenGL is what is known as a "state machine," to that end OpenGL has several "binding targets" each of which can only have one thing bound at once. Binding something else replaces the current bind, and thus changes it's state. Thus by binding buffers you are (re)defining the state of the machine.

As a state machine, whatever information you have bound will have an effect on the next output of the machine, in OpenGL that is its next draw-call. Once that is done you could bind new vertex data, bind new pixel data, bind new targets etc then initiate another draw call. If you wanted to create the illusion of movement on your screen, when you were satisfied you had drawn your entire scene (a 3d engine concept, not an OpenGL concept) you'd flip the framebuffer.

  • 1
    very useful if you know what a state-machine is. otherwise probably not so much. "bind" is not a very meaningful word neither is "state machine" if you haven't recognized you've written one. – n611x007 Jan 25 '17 at 21:24
  • The API is basically a glorified printer. You're just loading the machine with some data, like if you right click the texture and click "print" you are "binding" that data to your inkjet, just copying it into whatever memory chip/location the printer is programmed to look at come a print cmd, at some point after binding you send another command and the printer or opengl takes the data runs it through some pre-defined process, whether it's all your GLSL shaders putting renders on your framebuffer or your laser printer that does color grading and sepia effects or w/e else, on a sheet of paper. – Russell Barlow Mar 20 '17 at 08:07
  • Just fyi this analogy only works if the PRINTER has these effects built in, using some program to make a new image with a sepia filter first and then sending it in is something else all together, more like "texture baking" but for example a printer that can take a raw image to grayscale on the printer hardware itself is quite analogous with a "pixel shader," in fact in a loose sense it is one. Fragment and vertex shaders don't fit the analogy well, but it is the same load the machine, fire the gun/signal, get a result process. – Russell Barlow Mar 20 '17 at 08:20
  • 1
    You keep saying "bind" but never explain what binding is. Do you know? – Andy Ray Jan 27 '19 at 01:55