I have the following working code, however I'm not convinced that I'm calling glDeleteBuffers in a safe way. In practice it's working (for now at least) but from what I've been reading I don't think it should work.
GLuint vao_id;
glGenVertexArrays(1, &vao_id);
glBindVertexArray(vao_id);
GLuint VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
//Alternate position <<----
//Unbind the VAO
glBindVertexArray(0);
//Current position <<----
glDeleteBuffers(1, &VBO);
I am currently calling glDeleteBuffers straight after unbinding the VAO. I have tried calling it in the alternative position marked - immediately after I have set the attribute pointer. This however caused a crash - my guess is this was because when I made the draw call there was no data to be drawn because I'd deleted it.
The thing that confuses me is that it works as I currently have it. I'm worried that a) I don't quite understand what happens when the buffer is delete and b) that it only works by chance and could unexpectedly break.
As far as I understand calling glDeleteBuffers deletes the data so there shouldn't be any data to draw - but there is. So my other thought was that when I re-bind the VAO the data is restored, although that didn't make much sense to me because I can't reason where the data would be restored from.
Can someone let me know if I am using glDeleteBuffer correctly? and if not where it should be called (I'm guessing once there is no need for the data to be drawn any more, probably at the end of the program).
What you're seeing is well defined behavior. The following are the key parts of the spec related to this (emphasis added).
From section "5.1.2 Automatic Unbinding of Deleted Objects" in the OpenGL 4.5 spec:
When a buffer, texture, or renderbuffer object is deleted, it is unbound from any bind points it is bound to in the current context, and detached from any attachments of container objects that are bound to the current context, as described for DeleteBuffers, DeleteTextures, and DeleteRenderbuffers.
and "5.1.3 Deleted Object and Object Name Lifetimes":
When a buffer, texture, sampler, renderbuffer, query, or sync object is deleted, its name immediately becomes invalid (e.g. is marked unused), but the underlying object will not be deleted until it is no longer in use.
A buffer, texture, sampler, or renderbuffer object is in use if any of the following conditions are satisfied:
the object is attached to any container object
...
The VAO is considered a "container object" for the VBO in this case. So as long as the VBO is referenced in a VAO, and the VAO itself is not deleted, the VBO stays alive. This is why your version of the code with the glDeleteBuffers() at the end works.
However, if the VAO is currently bound, and you delete the VBO, it is automatically unbound from the VAO. Therefore, it is not referenced by the VAO anymore, and deleted immediately. This applies to the case where you call glDeleteBuffers() immediately after glVertexAttribPointer().
In any case the id (aka name) becomes invalid immediately. So you would not be able to bind it again, and for example modify the data.
There are some caveats if you dig into the specs more deeply. For example, if you delete a buffer, and it stays alive because it is still referenced by a VAO, the name of the buffer could be used for a new buffer. This means that you basically have two buffers with the same name, which can result in some confusing behavior.
Partly for that reason, I personally wouldn't call glDelete*() for objects that you want to keep using. But others like to call glDelete*() as soon as possible.
I would like to highlight in a separate answer what #Onyxite has pointed out in the first comment of the accepted answer. This has driven me nuts and I have been hours tracking down this issue.
AMD Windows drivers have a BUG where if you delete a VBO after unbinding all its referenced VAOs, it will DELETE the buffer and its underlying object, so nothing will be drawn. This may result in a black screen, or OpenGL not drawing that part of the VAO.
So, taking this in consideration, the answer to the question would be:
Even when it is correct as per the OpenGL specification, you should not call glDeleteBuffers() until you are going to actually delete the VAOs referencing that buffer.
So you should follow Reto Korandi's advice and do not call glDelete*() for objects that you want to keep using.
The position that u have mention is not correct to call glDeleteBuffer because till at u haven't rendered the object. i think it would be better if u call is function after rendering object mean;s after calling glDrawArray or glDrawIndex.
if u first delete the buffer and later u call draw, u might have to face crash problem. because draw call would try to access the buffer that u have deleted before.
Related
Suppose I have 2 different objects, each one has its own VAO and draw call. Something like this:
void Object::Draw() {
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, foo, bar);
}
First I call the first object's draw call which binds its VAO and renders it. Then, I do the same thing for the second object.
I know this will work and both objects will be rendered. My question is why. What happens in OpenGL's state when I bind a VAO without unbinding any other previously bound VAOs?
Override.
If the bind is successful no change is made to the state of the vertex array object, and any previous vertex array object binding is broken.
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindVertexArray.xhtml
I created a VBO (Vertex Buffer Object) and VAO (Vertex Array Objects) and did this:
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(...);
glVertexAttribPointer(...);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
Can I delete the vbo after I did this and then draw with the vao assuming everything is in order?
I know that the buffers bind to the vao so I am assuming I can.
The problem is, if I delete the buffer on my computer (Intel graphics) it works great (everything is displayed correctly), but on my friend computer (AMD) there is nothing displayed.
What could be the problem with that?
(By the way, if I don't delete the buffers the program works both on my computer and on my friend's)
Yes, as per OpenGL 4.5, it is legal to delete it after you unbind the VAO.
2.6.1.2 Name Deletion and Object Deletion
If an object is deleted while it is currently in use by a GL context, its name
is immediately marked as unused, and some types of objects are automatically
unbound from binding points in the current context, as described in section 5.1.2.
However, the actual underlying object is not deleted until it is no longer in use.
This situation is discussed in more detail in section 5.1.3.
5.1.2 Automatic Unbinding of Deleted Objects
When a buffer, texture, or renderbuffer object is deleted, it is unbound from any
bind points it is bound to in the current context, and detached from any attachments
of container objects that are bound to the current context, as described for DeleteBuffers, DeleteTextures, and DeleteRenderbuffers. [...] Attachments to unbound container
objects, such as deletion of a buffer attached to a vertex array object which is not
bound to the context, are not affected and continue to act as references on the
deleted object, as described in the following section.
5.1.3 Deleted Object and Object Name Lifetimes
When a buffer, texture, sampler, renderbuffer, query, or sync object is deleted, its
name immediately becomes invalid (e.g. is marked unused), but the underlying
object will not be deleted until it is no longer in use.
A buffer, texture, sampler, or renderbuffer object is in use if any of the following conditions are satisfied:
the object is attached to any container object
[...]
So either it's an AMD driver bug, or the situation is not as you describe.
I've been learning OpenGL for three days and I can get stuff done but I feel like copy pasting without knowing what I'm doing. I seriously think I lack basic understanding about when is exactly what (VBO, attributes, ...) bound to a Vertex Array Object (VAO), and haven't found any resources that clarify these aspects in detail.
In particular, these are some of my issues. If I create a VAO:
GLuint vao;
glGenVertexArrays(1, &vao);
can anything get bound to it before I bind the VAO? (if I create a VBO now, is it bound to the VAO?)
glBindVertexArray(vao);
After binding the VAO, if I create a VBO:
GLuint vbo;
glGenBuffers(1, &vbo);
is it bound to the VAO? Or does it happen when I bind it?
glBindVertexArray(vbo);
Or maybe when I copy something to it?
If I get an attribute location:
att = glGetAttribLocation(program_id, "name");
is it bound to the VAO? Or does it happen after enabling it:
glEnableVertexAttribArray(att);
... or after setting it:
glVertexAttribPointer(att, ...);
?
I guess EBOs behave just like VBOs, so I hope the same "rules" apply.
Uniforms should behave like globals, so they shouldn't be affected by VAOs at all.
Now, about unbinding:
If I "bind" a VBO to a VAO, and then unbind the VBO, does it get detached from a VAO?
If I have a VBO that is bound to multiple VAOs, what happens when I unbind that VBO?
And about freeing resources:
What happens when I delete an VBO? Does it get deleted from all the VAOs ? Or do they still have "dangling references" to that VBO?
And about programs:
IIUC I can reuse VBOs between programs. However, if VAOs bind attributes and VBOs, and attributes take a program parameter, can I reuse VAOs between programs? Why do attributes take a program parameter at all?
And about debugging:
Is there a way to pretty print the OpenGL state machine? I would like a way to know the programs that have been linked, with which shaders, which VAOs are there, which VBOs are bound to which VAOs, which attributes are bound to which VAOs and VBOs, have they been set? are they enabled? which uniforms are there...
And about drawing calls:
Suppose someone gives me a VAO, and I have to draw it. Is there a way to know if I should be calling glDrawArrays or glDrawElements? Can I query somehow this information from a VAO? Maybe along with the sizes of my VBOs stored in there?
That's a lot of sub-questions. But since this an area that is often confusing newer OpenGL enthusiasts, let me try and provide some content that will hopefully help more people. I will intentionally skim over some details, like vertex attributes that are not sourced from a buffer, to avoid writing a book here.
The key thing to understand is that a VAO is a collection of state. It does not own any data. It's VBOs that own vertex data. A VAO, on the other hand, contains all the state used to describe where a draw call gets its vertex attributes from. This includes, for each attribute:
If it's enabled.
Which buffer the attribute is stored in.
At which offset in the buffer the data starts.
The spacing between subsequent attributes (aka the stride).
The type of the data.
The number of components.
Plus, once only:
Which element array buffer is bound.
Mapping this to API calls, the following calls change state tracked by the currently bound VAO:
glEnableVertexAttribArray(...)
glDisableVertexAttribArray(...)
glVertexAttribPointer(...)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...)
Note that this does not include the current binding of GL_ARRAY_BUFFER. The buffer used for each attribute is tracked indirectly, based on which buffer was bound when glVertexAttribPointer() is called for the specific attribute.
This should set the basis for the specific sub-questions:
If I create a VAO, can anything get bound to it before I bind the VAO?
No. The VAO needs to be bound before you can modify any state stored in it.
(if I create a VBO now, is it bound to the VAO?)
No. You can bind a VBO before you bind a VAO, and fill the VBO with data using glBufferData(). A VBO is essentially just a dumb data container. But any kind of vertex attribute setup tracked in the VAO can only be done after the VAO is bound.
If I get an attribute location, is it bound to the VAO?
No, glGetAttribLocation() only does what the name suggests, which is get an attribute location. It does not change any state. You will use the attribute locations for calls like glEnableVertexAttribArray() and glVertexAttribPointer().
Or does it happen after enabling it ... or after setting it
The association of an attribute with a given VBO is established when you call glVertexAttribPointer() while the given VBO is bound.
I guess EBOs behave just like VBOs, so I hope the same "rules" apply.
Mostly, but not entirely. The GL_ELEMENT_ARRAY_BUFFER binding is part of the state stored in the VAO. This makes sense because there is only one element array buffer used for a draw call (while the vertex attributes could come from multiple different array buffers), and there is no separate call that specifies "use the index data from the currently bound element array buffer", like glVertexAttribPointer() specifies "use the vertex data from the currently bound array buffer". Instead that happens implicitly when you call glDrawElements(). Therefore, the element array buffer needs to be bound at the time of the draw call, and this binding is part of the VAO state.
Uniforms should behave like globals, so they shouldn't be affected by VAOs at all.
Correct. Uniforms are associated with shader programs, not VAOs.
If I "bind" a VBO to a VAO, and then unbind the VBO, does it get detached from a VAO?
No. I believe this is already covered by the explanations above.
If I have a VBO that is bound to multiple VAOs, what happens when I unbind that VBO?
Since nothing happens with one VAO, still nothing with multiple VAOs.
What happens when I delete an VBO? Does it get deleted from all the VAOs ? Or do they still have "dangling references" to that VBO?
This is one of the darker corners of OpenGL. If you can recite the exact deletion rules for all object types (they are not all the same), you have reached the advanced level... In this case, the VBO is automatically unbound from the currently bound VAO, but not from other VAOs that are not currently bound. If other VAOs have references to the VBO, the VBO will stay alive until all those bindings are broken, or the VAOs deleted.
However, if VAOs bind attributes and VBOs, and attributes take a program parameter, can I reuse VAOs between programs?
Yes, you can use a VAO for multiple programs. Program state and VAO state are independent. The vertex attribute location in the program specifies which vertex attribute is used to source the values for each attribute/in variable in the vertex shader.
As long as multiple programs use the same locations for the same attributes, you can use the same VAO. To make this possible, you may want to specify the attribute location for each program by using the layout (location=...) directive the vertex shader, or by calling glBindAttribLocation() before linking the program.
Is there a way to pretty print the OpenGL state machine?
There are glGet*() calls that let you retrieve pretty much all of the current OpenGL state. Not convenient, but it's all available. Many platforms/vendors also provide developer tools that allow you to look at OpenGL state at a given point in your program execution.
Suppose someone gives me a VAO, and I have to draw it. Is there a way to know if I should be calling glDrawArrays or glDrawElements?
This is an unusual scenario. Most of the time, you create the VAO, so you know how to draw it. Or if somebody else created it, you would ask them to draw it. But if you really need it, you can get the currently bound element array buffer with glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ...).
the VAO table can be found in the State Tables section of the spec
in psuedo code it looks like:
struct VAO{
GL_INT element_array_binding; //IBO used for glDrawElements and friends
char* label;//for debugging
struct{//per attribute values
bool enabled; //whether to use a VBO for it
//corresponding to the values passed into glVertexAttribPointer call
int size;
unsigned int stride;
GL_ENUM type;
bool normalized;
bool integer; //unconverted integers
bool long; //double precision
void* offset;
int bufferBinding;//GL_ARRAY_BUFFER bound at time of glVertexAttribPointer call
int attributeDiviser; //as used for instancing
} attributes[MAX_VERTEX_ATTRIBS];
};
Notably absent is the program state (which one is bound, the uniform values etc.)
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' ?
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.
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.
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.
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().
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.
Sample code:
1. glGenBuffers(1, &VboId);
2. glBindBuffer(GL_ARRAY_BUFFER, VboId);
3. glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
4. glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
So we generate a generic VBO handle, and then we bind it using "GL_ARRAY_BUFFER". Binding it seems to have 2 purposes:
We must bind the buffer before we can copy data to the GPU via glBufferData
We must bind the buffer before we can add attributes to it via glVertexAttribPointer
And I think those are the only 2 times you need to bind the VBO. My question is, is there any scenario in which target (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, or GL_PIXEL_UNPACK_BUFFER) would be different on lines 2 and 3? Or we would want to rebind it to a different target before line 4?
Can we bind multiple buffer targets to a single VBO?
You do not bind targets to a buffer object. Targets are locations in the OpenGL context that you can bind things (like buffer objects) to. So you bind buffer objects to targets, not the other way around.
A buffer object (there is no such thing as a VBO. There are simply buffer objects) is just a unformatted, linear array of memory owned by the OpenGL driver. You can use it as source for vertex array data, by binding the buffer to GL_ARRAY_BUFFER and calling one of the gl*Pointer functions. These function only work with the buffer currently bound to GL_ARRAY_BUFFER. You can use them as the source for index data by binding them to GL_ELEMENT_ARRAY_BUFFER and calling one of the glDrawElements functions.
The functions used to modify a buffer objects contents (glBufferData, glMapBuffer, glBufferSubData, etc) all specifically take a target for their operations to work on. So glBufferData(GL_ARRAY_BUFFER, ...) does its stuff to whatever buffer is currently bound to GL_ARRAY_BUFFER.
So there are two kinds of functions that affect buffer objects: those that modify their contents, and those that use them in operations. The latter are specific to a source; glVertexAttribPointer always uses the buffer currently bound to GL_ARRAY_BUFFER. You can't make it use a different target. Similarly, glReadPixels always uses the buffer bound to GL_PIXEL_PACK_BUFFER. And so forth. If a function does stuff with buffer objects but doesn't take a target as a parameter, then its documentation will tell you which target it looks for its buffer from.
Note: Vertex arrays are kinda weird. The association between a vertex attribute and a buffer object is made by calling glVertexAttribPointer. What this function does is set the appropriate data for that attribute, using the buffer object that is currently bound to GL_ARRAY_BUFFER. By "currently bound", I mean bound at the time this function is called. So immediately after calling this function, you can call glBindBuffer(GL_ARRAY_BUFFER, 0), and it will change nothing about what happens when you go to render. It will render just fine.
In this way, you can use different buffer objects for different attributes. The information will be retained until you change it with another glVertexAttribPointer call for that particular attribute.