週一到週五,天天一篇,北京時間早上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去渲染該模型。在本書剩餘的內容中,咱們將使用這個模型類去加載並渲染模型。
本日的翻譯就到這裏,明天見,拜拜~~
第一時間獲取最新橋段,請關注東漢書院以及圖形之心公衆號
東漢書院,等你來玩哦