向Shader中传递数据

【OpenGL】向Shader中传递数据

返回脚本百事通

传递顶点属性信息

之前讲过,vertex shader会被每个顶点调用,通常一个顶点会包含很多信息,例如顶点坐标、顶点法向量、纹理坐标等等,我们称这些信息为顶点的属性。在之前的OpenGL版本里,每个属性都对应了一个特定的通道,我们使用glVertex,glTexCoord,,glNormal(或者通过访问指针函数glVertexPointer, glTexCoordPointer, or glNormalPointer)等函数来访问和设置它们。随后,shader会自己通过内置变量gl_Vertex 和 gl_Normal来访问这些属性。但在OpenGL3.0版本里,这些都被弃用了。在后续版本里,甚至都被移除了。而现在,这些通过generic vertex attributes,图形顶点属性,来提供(通常和顶点缓冲对象互相协作来完成)。我们可以根据需要为每个顶点定义任意数量的(其实是在0到GL_MAX_VERTEX_ATTRIBS – 1之间)属性。OpenGL会为这些属性定义一个索引,我们只要根据这些索引来访问就可以了。

在一个vertex shader里,一个顶点属性是由GLSL 标识符 “in” 来定义的。例如,下面的代码里定义了两个vec3的属性,VertexPosition和VertexColor。

#version 400
in vec3 VertexPosition;
in vec3 VertexColor;
out vec3 Color;
void main()
{
    Color = VertexColor;
    gl_Position = vec4(VertexPosition,1.0);
}

注意到这里还有一个类型为vec3的输出 Color,它将传递给下一层处理的shader中,这里也就是我们的fragment shader(fragment可以理解为具有很多属性的像素)。这里,我们还是使用之前那个简单的fragment shader。

#version 400
in vec3 Color;
out vec4 FragColor;
void main() {
    FragColor = vec4(Color, 1.0);
}

当然,vertex shader真正的数据还需要我们从OpenGL程序里传入。这就有了两种向Shader传递顶点属性的方法。

在这之前,我们首先要创建一个VAO,vertex array object,顶点数组对象。主要想要仔细了解这个是干嘛的,还请自行查阅资料,还是很多滴。这里,我们只要知道,它包含了我们的缓冲区和输入的顶点属性之间的对应关系就可以了。在一个OpenGL程序里,我们可以使用多个VAO,并在它们之间切换。现在我们只使用一个。

GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);

最后一句话表明,接下来我们所做的所有绑定操作,例如建立顶点属性和输入之间的对应关系,都是针对这个VAO的。

下面我们需要为要传输的数据生成对应的缓冲区。这些缓冲区随后会在绘制函数中,通过顶点属性索引传递给我们的shader。

因为在本例中,我们有两个属性值,因此需要建立两个缓冲区。

首先定义缓冲区的数据,也就是我们的顶点信息。我们现在需要画一个三角形,因此只需要三个点。

// An array of 3 vectors which represents 3 vertices
float positionData[] = {
    -0.8f, -0.8f, 0.0f,
    0.8f, -0.8f, 0.0f,
    0.0f, 0.8f, 0.0f };
然后创建一个缓冲区,并用上面的数据为其填充。

// This will identify our vertex buffer
GLuint vertexbuffer;

// Generate 1 buffer, put the resulting identifier in vertexbuffer
glGenBuffers(1, &vertexbuffer);

// The following commands will talk about our 'vertexbuffer' buffer
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);

// Give our vertices to OpenGL.
glBufferData(GL_ARRAY_BUFFER, sizeof(positionData), positionData, GL_STATIC_DRAW);
这样就完成的第一个顶点属性,VertexPosition数据的前期准备工作。VertexColor的数据是类似的。

float colorData[] = {
    1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 1.0f };

// This will identify our vertex buffer
GLuint colorbuffer;

// Generate 1 buffer, put the resulting identifier in vertexbuffer
glGenBuffers(1, &colorbuffer)
// The following commands will talk about our 'vertexbuffer' buffer
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);

// Give our vertices to OpenGL.
glBufferData(GL_ARRAY_BUFFER, sizeof(colorData), colorData, GL_STATIC_DRAW);

现在准备好了数据(现在存储在两个buffer里),我们就得让OpenGL程序知道所要传递的数据应该给谁。前面说过,OpenGL会为顶点属性创建一个0到GL_MAX_VERTEX_ATTRIBS – 1的索引,既然如此,我们首先要定义一下索引和Shader里变量的对应关系,就好比我们在写一个目录,我们必须告诉OpenGL,页码0对应的是什么内容,而页码1又对应着什么内容,这里的内容指的就是shader中以“in”为关键字的变量。定义了索引对应关系后,在绘制函数里,我们就可以根据索引来传递数据了。

这里,根据定义这种索引对应关系的途径,可以分为下面两种。

使用glBindAttribLocation

第一种方法是通过glBindAttribLocation函数来实现索引和变量之间的对应关系。

首先,我们为shader中的每个顶点属性变量指定一个索引(一般从0开始)。

// Bind index 0 to the shader input variable "VertexPosition"
glBindAttribLocation(programHandle, 0, "VertexPosition");
// Bind index 1 to the shader input variable "VertexColor"
glBindAttribLocation(programHandle, 1, "VertexColor");


上述代码规定,shader里名字为VertexPosition的变量对应顶点属性索引为0,VertexColor对应索引为1.

在Shader中直接指定

另一种方法则是在shader中直接指定,这是通过GLSL的关键词layout来是实现。为了实现这样的效果,我们需要更改之前的vertex shader的内容。如下:

#version 400
layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec3 VertexColor;
out vec3 Color;
void main()
{
    Color = VertexColor;
    gl_Position = vec4(VertexPosition,1.0);
}

类似这样的代码layout(location = 0)指定了顶点属性VertexPosition对应了索引值0.


现在所有的准备工作都做完了:我们既完成了缓冲区数据的填充,也完成了顶点属性索引和shader输入变量之间的对应关系,剩下的工作就是在绘制函数中告诉OpenGL,请使用xxx buffer内的数据来为索引为x的顶点属性赋值。

// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
   0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
   3,                  // size
   GL_FLOAT,           // type
   GL_FALSE,           // normalized?
   0,                  // stride
   (void*)0            // array buffer offset
);

第一行代码表示我们需要启用索引为0的顶点属性。然后第二行表明我们要使用vertexbuffer内的数据,下面的操作都是针对这个缓冲区的。第三行代码中函数的参数比较多,第一个参数指明要操作的属性索引值,第二个参数则说明每个顶点属性需要几个数据(可以为1,2,3或4),因为vertexbuffer里现在存储了3X3=9个数据,而实际上3个是一组,每个顶点需要使用3个数据。第三个参数指定了缓冲区内每个数据的类型,这里顶点坐标使用的是浮点类型。第四个参数表明数据是否需要normalized,归一化(对于有符号整数,归一化将使数据保持在[-1, 1]范围内,对于无符号整数,则在范围[0, 1])。第五个参数是步幅,指定了两个连续的顶点属性之间的偏移量(以字节为单位)。这里我们的数据是连续的,因此数值为0。最后一个参数看似是一个指针,但实际上它并不是起到指针的作用。实际上,它表明缓冲区的开头距离第一个顶点属性之间的偏移量。这里,缓冲区里第一个顶点属性之前并没有任何额外的信息,因为我们取值为0。

colors的传递工作是一样的。

// 2cond attribute buffer : vertices
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glVertexAttribPointer(
   1,                  // attribute 1. No particular reason for 0, but must match the layout in the shader.
   3,                  // size
   GL_FLOAT,           // type
   GL_FALSE,           // normalized?
   0,                  // stride
   (void*)0            // array buffer offset
);

哇哈哈,终于所有的工作都完成了,现在就可以告诉OpenGL真正开始绘制了!这一步非常简单只需要一个函数。(在这之前请确保已经绑定了我们最开始创建的VAO,如果没有,请调用glBindVertexArray(VertexArrayID))

glDrawArrays(GL_TRIANGLES, 0, 3 );

这个函数表明,OpenGL将逐步访问每个顶点属性的缓冲区,然后把数据传递到OpenGL管线里交给vertex shader。第一个参数是渲染模式,这里表明我们将使用三角形进行绘制。第二个参数是指,在开启的属性中的第一个索引,这里我们开启了两个属性,分别为0和1,因此第一个索引为0。第三个参数是指渲染所需的索引数量,这里我们使用三角形模式,因此每个三角形需要3个顶点。每个顶点需要一个索引。

除了glDrawArrays,我们还可以使用glDrawElements进行绘制,这在之后的文章会讲到。


你可能注意到,对于fragment shader的输出,FragColor我们没有进行任何工作,它存储了每个像素最后输出的颜色值。这里我们先不用管它,我们只要知道,它默认将会传递给后台的颜色缓冲就可以了。


作者:candycat1992 发表于2013-4-21 16:23:18  原文链接
阅读:13 评论:0  查看评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值