1. 程式人生 > 其它 >The First Step For Vertex Processing: Vertex Stream

The First Step For Vertex Processing: Vertex Stream

So as you have stored a bunch of bytes in the buffer object, we now take in these bytes and form a vertex stream, and tell OpenGL how to interpret this stream.

The start endian for the stream is the buffer object, and the stop endian is user-defined input variables in the Vertex Shader (for example, in

vec4 vPosition).

Now let's think about how we can interpret the vertex stream. First of all we take each vertex as a whole, which is natural, and we get a bunch of vertices. Inside each vertex we have some data. The data now is no more than a bunch of bytes, but it can be grouped to interpret. Let's say we define the first 16 bytes as 4 coordinates in the type of float (You know, in OpenGL the float is 32 bit long), and the next 16 bytes as 4 components for color. So here we define the "attribute" for each vertex, with this example being the position and color. And then we could create input variables in accrod with the attribute, that is in vec4 vPosition, in vec4 vColor or whatever you'd like to name it.

Now we could setup methods to distribute the vertex stream into different input variables in the shader. We use glVertexAttribPointer to deal with the vertex stream. Each vertex attribute pointer processes a certain attribute in the vertex stream, forms and plugs the substream to the input variable in the shader.

Let's think deep about the function glVertexAttribPointer. First of all you need to setup pipes in between the variables and the corresponding substreams. You need something to identify which vertex attribute pointer outputs the substream a certain input variable wants. Luckily OpenGL has done this for you: we use "location" and "index" to identify. When you declare layout (location == index) before the input variable, it is able to get the substream that is from the indexed glVertexAttribPointer. That means, of course, glVertexAttribPointer has a parameter called index, which determines the index of vertex attribute pointer. (Some tutorials assume that you can use name and index interchangably as they are both unique intergers for certain things, but you'd better follow the name given by the Official Wiki.)

As the pipe is set up thanks to the index, what we need to do is to just define how big each attribute is, and the stride between each adjacent attribute data, with the data type. Remember, one attribute pointer per attribute. So you are supposed to call glVertexAttribPointer as many times as the attributes each vertex contains. That is, call glVertexAttribPointer3 times if each of your vertex has 3 attributes. In OpenGL specification, the maximum of vertex attibutes you can have (depends on hardware platform) is no smaller than 16.

So what if I have tens of vertex attributes and hundreds of objects? You need to bind and unbind buffer objects hundreds of times and each time you need to call glVertexAttribPointer tens of times to setup for the shader. That consumes to much resources. So we've got something called Vertex Array Object, which contains an array of vertex attribute pointer. Each time it is bound to the context, vertex stream of all buffers will be interpreted as the pointers setup in the Vertex Array Object. When rendering is done or you want to change the setup, just unbind the current VAO and provide a new one, or modify the pointers using glEnableVertexAttribArray and glDisableVertexAttribArray and rebind it to the context. One call of binding replaces tens of calls of glVertexAttribPointer.

(P.S the glGetAttribLoaction is another choice for setting up the pipes between vertex streams and shaders. But the layout qualifier has more priority. Why this is called "location" is still a mystery to me.)