温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Feeding Vertex Shaders from Buffers从缓冲区给shader输入数据

发布时间:2020-08-10 13:27:31 来源:网络 阅读:405 作者:萌谷王 栏目:游戏开发

周一到周五,每天一篇,北京时间早上7点准时更新~

In Chapter 2, “Our First OpenGL Program,” you were briefly introduced to the vertex array object (VAO). During that discussion, we explained how the VAO represented the inputs to the vertex shader—though at the time, we didn’t use any real inputs to our vertex shaders and opted instead for hard-coded arrays of data. Then, in Chapter 3 we introduced the concept of vertex attributes, but we discussed only how to change their static values. Although the vertex array object stores these static attribute values for you, it can do a lot more. Before we can proceed, we need to create a vertex array object to store our vertex array state and bind it to our context so that we can use it:

在第二章中,我们已经接触过VAO了,在那里,我们已经解释过VAO如何给shader输入数据了-虽然那时候我们没有真的使用VAO而是使用了硬编码的数据。 在第三章,我们介绍了顶点属性的概念,但我们仅仅是讨论了如何改变他们静态的值。VAO不仅可以为你存储那些静态属性,它还可以做更多事情。在我们开始之前,我们需要创建一个VAO的对象 去存储我们顶点数组的状态,并且把它绑定到我们的上下文中,这样我们才能使用它

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);
Now that we have our VAO created and bound, we can start filling in its state. Rather than using hard-coded data in the vertex shader, we can instead rely entirely on the value of a vertex attribute and ask OpenGL to fill it automatically using the data stored in a buffer object that we supply. Each vertex attribute gets to fetch data from a buffer bound to one of several vertex buffer bindings. To set the binding that a vertex attribute uses to reference a buffer, call the glVertexArrayAttribBinding() function:

现在我们创建了VAO了,我们可以干大事了。这次我们使用缓冲区对象提供数据而不是使用硬编码。每个顶点属性都需要从缓冲区对象绑定到的节点上去获取数据。 为了让顶点属性知道自己去哪里缓冲区的那个节点上拿数据,我们调用glVertexArrayAttribBinding去设置

void glVertexArrayAttribBinding(GLuint vaobj,GLuint attribindex,GLuint bindingindex);
The glVertexArrayAttribBinding() function tells OpenGL that when the vertex array object named vaobj is bound, the vertex attribute at the index specified in attribindex should source its data from the buffer bound at bindingindex. To tell OpenGL which buffer object our data is in and where in that buffer object the data resides, we use the glVertexArrayVertexBuffer() function to bind a buffer to one of the vertex buffer bindings. We use the glVertexArrayAttribFormat() function to describe the layout and format of the data, and finally we enable automatic filling of the attribute by calling glEnableVertexAttribArray(). The prototype of glVertexArrayVertexBuffer() is

这个函数就告诉OpenGL,当vaobj这个VAO被绑定到上下文的时候,在attribindex上的顶点属性去缓冲区的那个bindingindex上拿数据。 为了告知OpenGL数据在哪个缓冲区里以及数据在那个缓冲区里的内存格局,我们使用glVertexArrayVertexBuffer去设置这些。我们使用glVertexArrayAttribFormat去描述数据的格式,也就是内存分布啦。 不清楚内存分布的同学可以看看我们的C++ Tricks课程,不讲C++基础语法,只通过一些小的例子来加深对C++的理解。并且最终,我们调用glEnableVertexAttribArray函数去启动让OpenGL 自动的去把缓冲区里的数据按照设置的模式发送给shader处理。glVertexArrayVertexBuffer的函数如下:

void glVertexArrayVertexBuffer(GLuint vaobj,
GLuint bindingindex,
GLuint buffer,
GLintptr offset,
GLsizei stride);
Here, the first parameter is the vertex array object whose state you’re modifying. The second parameter, bindingindex, is the index of the vertex buffer, which matches the parameter sent to glVertexArrayAttribBinding(). The buffer parameter specifies the name of the buffer object that we’re binding. The last two parameters, offset and stride, tell OpenGL where in the buffer object the attribute data lies. offset says where the first vertex’s data starts and stride says how far apart each vertex is. Both are measured in bytes. Next, we have glVertexArrayAttribFormat(), whose prototype is

这里,第一个参数是VAO,第二个参数是缓冲区对象的索引,这个索引与glVertexArrayAttribBinding里面设置的那个一一对应。第三个参数是缓冲区对象。 最后俩参数offset和stride告诉OpenGL这些数据的内存格局,offset告诉OpenGL数据的起始位置,stride告诉OpenGL同一个属性之间的数据间隔,大小都是字节。

void glVertexArrayAttribFormat(GLuint vaobj,
GLuint attribindex,
GLint size,
GLenum type,
GLboolean normalized,
GLuint relativeoffset);
For glVertexArrayAttribFormat(), the first parameter is again the vertex array whose state we’re modifying. attribindex is the index of the vertex attribute. You can define a large number of attributes as input to a vertex shader and then refer to them by their index, as explained in the “Vertex Attributes” section in Chapter 3. size is the number of components that are stored in the buffer for each vertex and type is the type of the data, which would normally be one of the types in Table 5.3.

glVertexArrayAttribFormat的第一个参数是VAO,attribindex是顶点属性的索引。你可以定义很多属性,然后试用索引来引用他们。 size参数是是指每一个顶点有多少个组成部分,类型指的是数据的类型,一般来说就是指表5.3里的那些。

The normalized parameter tells OpenGL whether the data in the buffer should be normalized (scaled between 0.0 and 1.0) before being passed to the vertex shader or if it should be left alone and passed as is. This parameter is ignored for floating-point data, but for integer data types, such as GL_UNSIGNED_BYTE or GL_INT, it is important. For example, if GL_UNSIGNED_BYTE data is normalized, it is divided by 255 (the maximum value representable by an unsigned byte) before being passed to a floating-point input to the vertex shader. The shader will therefore see values of the input attribute between 0.0 and 1.0. However, if the data is not normalized, it is simply cast to floating-point values and the shader will receive numbers between 0.0 and 255.0, even though the input to the vertex shader consists of floating-point data. The stride parameter tells OpenGL how many bytes are between the start of one vertex’s data and the start of the next, but you can set this parameter to 0 to let OpenGL calculate it for you based on the values of size and type. Finally, relative offset is the offset from the vertex’s data where the specific attribute’s data starts. This all seems pretty complex, but the pseudocode to compute the location in a buffer object is fairly simple:

normalized参数告诉OpenGL,数据是否需要在传给shader前被缩放到0~1之间去。对于GL_FLOAT类型来说,这个参数会被OpenGL忽略,但是对于GL_UNSIGNED_BYTE或者GL_INT类型的数据来说,这个参数 很重要。对于GL_UNSIGNED_BYTE来说,如果你传入的数据是255,如果这里告诉OpenGL需要缩放,那么shader里收到的数据是1.0,如果你告诉OpenGL不需要缩放,那么shader里收到的数据是255.0这样一个浮点数。 stride参数告诉OpenGL数据间隔。最后相对偏移指示了某一个属性数据的数据在顶点所有数据中的偏移位置。虽然这些概念看起来很复杂,但是计算偏移的伪代码很简单:

location = binding[attrib.binding].memory + // Start of data store in memory
binding[attrib.binding].offset + // Offset of vertex attribute in buffer
binding[attrib.binding].stride vertex.index + // Start of this* vertex
vertex.relative_offset; // Start of attribute relative to vertex
Finally, glEnableVertexAttribArray() and the converse glDisableVertexAttribArray() have the prototypes:

最后,使用glEnableVertexAttribArray和相反的操作glDisableVertexAttribArray有如下的函数申明:

void glEnableVertexAttribArray(GLuint index);
When a vertex attribute is enabled, OpenGL will feed data to the vertex shader based on the format and location information you’ve provided with glVertexArrayVertexBuffer() and glVertexArrayAttribFormat(). When the attribute is disabled, the vertex shader will be provided with the static information you provide with a call to glVertexAttrib(). Listing 5.4 shows how to use glVertexArrayVertexBuffer() and glVertexArrayAttribFormat() to configure a vertex attribute. Notice that we also call glEnableVertexArrayAttrib() after setting up the offset, stride, and format information. This tells OpenGL to use the data in the buffer to fill the vertex attribute rather than using data we provide through one of the glVertexAttrib() functions.

当一个顶点属性启用之后,OpenGL将会根据我们前面设置的那些来给shader发送参数.如果顶点属性被禁用了,那么OpenGL则会给shader发送你使用glVertexAttrib那些函数设置的一些固定的数据。 清单5.4展示了如何使用glVertexArrayVertexBuffer和glVertexArrayAttribFormat去配置顶点属性。注意到,我们同样在设置offset、stride以及format的信息后调用了glEnableVertexArrayAttrib。 这是在告诉OpenGL,我们使用缓冲区里的数据作为shader输入参数俄入世glVertexAttrib那些函数设置的东西。

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, // Vertex array object
0, // First vertex buffer
binding
buffer, // Buffer object
0, // Start from the beginning
sizeof(vmath::vec4)); // Each vertex is one vec4
// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, // Vertex array object
0, // First attribute
4, // Four components
GL_FLOAT, // Floating-point data
GL_FALSE, // Normalized - ignored for floats
0); // First element of the vertex
glEnableVertexArrayAttrib(vao, 0);
Listing 5.4: Setting up a vertex attribute

After Listing 5.4 has been executed, OpenGL will automatically fill the first attribute in the vertex shader with data it has read from the buffer that was bound to the VAO by glVertexArrayVertexBuffer(). We can modify our vertex shader to use only its input vertex attribute rather than a hardcoded array. This updated shader is shown in Listing 5.5.

当清单5.4倍执行了之后,OpenGL将会自动使用glVertexArrayVertexBuffer绑定在VAO上的缓冲区的数据去填充shader里的第一个属性。我们现在来修改shader,让shader去使用这样的输入数据,而不是硬编码数据。 清单5.5展示了我们最新版本的shader代码该如何写

#version 450 core
layout (location = 0) in vec4 position;
void main(void)
{
gl_Position = position;
}
Listing 5.5: Using an attribute in a vertex shader

As you can see, the shader of Listing 5.5 is greatly simplified over the original shader shown in Chapter 2. Gone is the hard-coded array of data. As an added bonus, this shader can be used with an arbitrary number of vertices. You can literally put millions of vertices’ worth of data into your buffer object and draw them all with a single command such as a call to glDrawArrays(). If you are done using data from a buffer object to fill a vertex attribute, you can disable that attribute again with a call to glDisableVertexAttribArray(), whose prototype is

如你所见,清单5.5比起以前那个shader简单多了。删除了硬编码的数据。这个shader可以使用任意数量的顶点。可以说,你可以在缓冲区里放上百万的数据,然后使用一个绘制指令就将他们画出来。 如果你使用完毕了缓冲区里的顶点属性,你可以使用glDisableVertexAttribArray禁用这个属性。

void glDisableAttribArray(GLuint index);
Once you have disabled the vertex attribute, it goes back to being static and passing the value you specify with glVertexAttrib*() to the shader.

当你禁用了这个属性后,未来调用shader的时候,传给shader的数据又是那些使用glVertexAttrib*函数设置的数据了。

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

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

东汉书院,等你来玩哦

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI