Using Multiple Vertex Shader Inputs

周一到周五,天天一篇,北京时间早上7点准时更新~数据结构

As you have learned, you can get OpenGL to feed data into your vertex shaders and use data you’ve placed in buffer objects. You can also declare multiple inputs to your vertex shaders, and assign each one a unique location that can be used to refer to it. Combining these things together means that you can get OpenGL to provide data to multiple vertex shader inputs simultaneously. Consider the input declarations to a vertex shader shown in Listing 5.6.app

你已经学会了使用使用缓冲区为shader输入数据,你也能够为你的vertex shader定义多个输入的属性。结合这些所学的东西,就意味着你能够同时给shader输入多组数据。 让咱们来看看清单5.6的代码框架

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
Listing 5.6: Declaring two inputs to a vertex shaderide

清单5.6申明了vertex shader的两个输入数据函数

If you have a linked program object whose vertex shader has multiple inputs, you can determine the locations of those inputs by calling学习

若是你生成了一个有多组输入数据的GPU程序,那么你能够经过下面的API来获取某一个输入数据的绑定位置。ui

GLint glGetAttribLocation(GLuint program,const GLchar * name);
Here, program is the name of the program object containing the vertex shader and name is the name of the vertex attribute. In our example declarations of Listing 5.6, passing "position" to glGetAttribLocation() will cause it to return 0, and passing "color" will cause it to return 1. Passing something that is not the name of a vertex shader input will cause glGetAttribLocation() to return −1. Of course, if you always specify locations for your vertex attributes in your shader code, then glGetAttribLocation() should return whatever you specified. If you don’t specify locations in shader code, OpenGL will assign locations for you, and those locations will be returned by glGetAttribLocation(). There are two ways to connect vertex shader inputs to your application’s data, referred to as separate attributes and interleaved attributes. When attributes are separate, they are located either in different buffers or at least at different locations in the same buffer. For example, if you want to feed data into two vertex attributes, you could create two buffer objects, bind each to a different vertex buffer binding with a call to glVertexArrayVertexBuffer(), and then specify the two indices of the two vertex buffer binding points that you used when you call glVertexArrayAttribBinding() for each. Alternatively, you could place the data at different offsets within the same buffer, bind it to a single vertex buffer binding with one call to glVertexArrayVertexBuffer(), and then call glVertexArrayAttribBinding() for both attributes, passing the same binding index to each. Listing 5.7 shows this approach.this

第一个参数是GPU程序,第二个参数是顶点属性的名字。在咱们清单5.6的代码中,名字这个参数若是传position,那么该API会返回0,若是传color,那么该API会返回1. 若是你传了一个不存在的名字,那么该API返回-1.固然,若是你在shader代码中去定义了属性的绑定位置,那么你总会拿到跟你设置的那些位置一致的返回值。若是你不去在shader里 设置属性的绑定位置,那么OpenGL将会为你分配这些位置,你经过这个API就能够获取到那些位置。有两种方法可让你给shader中的属性传输数据,一种是separate,另外一种叫interleaved。 当使用separate方式传输数据的时候,你的数据会存放在不一样的缓冲区对象里,或者存储在同一个缓冲区对象里的不一样位置。好比你但愿给shader中的两个属性传输数据,那么你能够建立两个缓冲区对象, 而后调用glVertexArrayVertexBuffer去将它们各自绑定到不一样的缓冲区的绑定节点,而后经过glVertexArrayAttribBingding分别为它们指定两个它们使用的索引。 另外,你也能够把数据放在同一个缓冲区的不一样位置,而后使用跟上面相同的方法去作这件事。清单5.7展现了如何作到这些操做翻译

GLuint buffer[2];
GLuint vao;
static const GLfloat positions[] = { ... };
static const GLfloat colors[] = { ... };
// Create the vertex array object
glCreateVertexArrays(1, &vao)
// Get create two buffers
glCreateBuffers(2, &buffer[0]);
// Initialize the first buffer
glNamedBufferStorage(buffer[0], sizeof(positions), positions, 0);
// Bind it to the vertex array - offset zero, stride = sizeof(vec3)
glVertexArrayVertexBuffer(vao, 0, buffer[0], 0, sizeof(vmath::vec3));
// Tell OpenGL what the format of the attribute is
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
// Tell OpenGL which vertex buffer binding to use for this attribute
glVertexArrayAttribBinding(vao, 0, 0);
// Enable the attribute
glEnableVertexArrayAttrib(vao, 0);
// Perform similar initialization for the second buffer
glNamedBufferStorage(buffer[1], sizeof(colors), colors, 0);
glVertexArrayVertexBuffer(vao, 1, buffer[1], 0, sizeof(vmath::vec3));
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(vao, 1, 1);
glEnableVertexAttribArray(1);
Listing 5.7: Multiple separate vertex attributescode

清单5.7:经过separate方式为顶点属性传入数据

In both cases of separate attributes, we have used tightly packed arrays of data to feed both attributes. This is effectively structure-of-arrays (SoA) data. We have a set of tightly packed, independent arrays of data. However, it’s also possible to use an array of-structures (AoS) form of data. Consider how the following structure might represent a single vertex:

两种separate的数据传输方式咱们都用到了很是紧凑的数据块。固然,使用AOS也是能够的,咱们来看看下面这样的结构体。

struct vertex
{
// Position
float x;
float y;
float z;
// Color
float r;
float g;
float b;
};
Now we have two inputs to our vertex shader (position and color) interleaved together in a single structure. Clearly, if we make an array of these structures, we have an AoS layout for our data. To represent this with calls to glVertexArrayVertexBuffer(), we have to use its stride parameter. The stride parameter tells OpenGL how far apart in bytes the beginning of each vertex’s data is. If we leave it as 0, OpenGL will use the same data for every vertex. However, to use the vertex structure declared above, we can simply use sizeof(vertex) for the stride parameter and everything will work out. Listing 5.8 shows the code to do this

如今咱们使用interleaved方式为shader的两个属性输入数据。很明显,若是咱们有一组这样的结构体类型的数据,咱们使用的是AOS的数据块。咱们使用glVertexArrayVertexBuffer来设置的时候,咱们 须要用到它的stride参数。stride参数告诉OpenGL两个vertex之间的间隔,若是这个参数咱们写0,那么全部的顶点都使用的是同一个数据。使用这个数据结构是很简单的,咱们直接使用sizeof(vertex)来赋值 给stride参数。清单5.8展现了刚才讨论的这种方式的样本代码

GLuint vao;
GLuint buffer;
static const vertex vertices[] = { ... };
// Create the vertex array object
glCreateVertexArrays(1, &vao);
// Allocate and initialize a buffer object
glCreateBuffers(1, &buffer);
glNamedBufferStorage(buffer, sizeof(vertices), vertices, 0);
// Set up two vertex attributes - first positions
glVertexArrayAttribBinding(vao, 0, 0);
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, offsetof(vertex,x));
glEnableVertexArrayAttrib(0);
// Now colors
glVertexArrayAttribBinding(vao, 1, 0);
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, offsetof(vertex,
r));
glEnableVertexArrayAttrib(1);
// Finally, bind our one and only buffer to the vertex array object
glVertexArrayVertexBuffer(vao, 0, buffer);
Listing 5.8: Multiple interleaved vertex attributes

清单5.8:使用interleaved方式为shader的多个属性输入数据

After executing the code in Listing 5.8, you can bind the vertex array object and start pulling data from the buffers bound to it. After the vertex format information has been set up with calls to glVertexArrayAttribFormat(), you can change the vertex buffers that are bound with further calls to glVertexArrayAttribBinding(). If you want to render a lot of geometry stored in different buffers but with similar vertex formats, simply call glVertexArrayAttribBinding() to switch buffers and start drawing from them.
当执行完毕了清单5.8的代码以后,你就能够拿着它去画画了。在使用glVertexArrayAttribFormat设置好了缓冲区以后,你能够调用glVertexArrayAttribBinding去修改当前的缓冲区对象。 若是你想渲染不少内存类似,却存储在不一样缓冲区里的几何形体时,你能够简单调用glVertexArrayAttribBinding去切换输入缓冲区,而后直接调用绘图函数。

Loading Objects from Files(从文件加载物体)

As you can see, you could potentially use a large number of vertex attributes in a single vertex shader. As we progress through various techniques, you will see that we’ll regularly use four or five vertex attributes, and possibly more. Filling buffers with data to feed all of these attributes and then setting up the vertex array object and all of the vertex attribute pointers can be a chore. Further, encoding all of your geometry data directly in your application isn’t practical for anything but the simplest models. Therefore, it makes sense to store model data in files and load it into your application. There are plenty of model file formats out there, and most modeling programs support several of the more common formats.

如你所见,你可能在shader里须要使用大量的顶点属性。随着学习的深刻,咱们时常须要常常用到四五个顶点属性,或许更多。手工的去干这个活很麻烦。另外, 直接在应用程序里手写这些几何数据,会让你内分泌失调。所以,从文件加载模型这个选择看起来很是不错。模型的种类有不少,不少建模软件都支持一些比较常见的模型格式。

For the purpose of this book, we have devised a simple object file definition called an .SBM file, which stores the information we need without being either too simple or too overly engineered. Complete documentation for the format is found in Appendix B, “The SBM File Format.” The sb7 framework also includes a loader for this model format, called sb7::object. To load an object file, create an instance of sb7::object and call its load function as follows:

为了学习的须要,咱们来使用一个后缀叫SBM的模型格式。该格式的完整格式描述在附录B。咱们教程的框架代码提供加载该格式的代码,sb7::object。你能够经过下面的代码加载一个sbm的模型
sb7::object my_object;
my_object.load("filename.sbm");
If this operation is successful, the model will be loaded into the instance of sb7::object and you will be able to render it. During loading, the class will create and set up the object’s vertex array object and then configure all of the vertex attributes contained in the model file. The class also includes a render function that binds the object’s vertex array object and calls the appropriate drawing command. For example, calling

若是加载成功,你以后就能够渲染它了。在加载的时候,这个类会配置好VAO以及全部的顶点属性。这个类还包括了渲染的API接口,里面绑定了VAO以后,而后调用了绘图函数。好比下面这样

my_object.render();
will render a single copy of the object with the current shaders. In many of the examples in the remainder of this book, we’ll simply use our object loader to load object files (several of which are included with the book’s source code) and render them.

上面的接口就会使用当前的shader去渲染该模型。在本书剩余的内容中,咱们将使用这个模型类去加载并渲染模型。

本日的翻译就到这里,明天见,拜拜~~

第一时间获取最新桥段,请关注东汉书院以及图形之心公众号

东汉书院,等你来玩哦

相关文章
相关标签/搜索