设置着色器的矩阵
我想绘制实例立方体。
我可以调用GL.DrawArraysInstanced(PrimitiveType.Triangles, 0, 36, 2);
成功。
我的问题是所有的立方体都是在相同的位置和相同的旋转绘制的。 我怎样才能为每个立方体单独更改?
要创建不同的位置等,我需要每个立方体的矩阵,对吧? 我创造了这个:
Matrix4[] Matrices = new Matrix4[]{ Matrix4.Identity, //do nothing Matrix4.Identity * Matrix4.CreateTranslation(1,0,0) //move a little bit }; GL.BindBuffer(BufferTarget.ArrayBuffer, matrixBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(sizeof(float) * 16 * Matrices.Length), Matrices, BufferUsageHint.StaticDraw);
这应该创建一个缓冲区,我可以存储我的矩阵。 matrixBuffer
是指向我缓冲区的指针。 我不确定大小是否正确,我采用浮动* 4(对于Vector4)* 4(对于4个向量)*数组大小。
绘制循环:
GL.BindBuffer(BufferTarget.ArrayBuffer, matrixBuffer); GL.VertexAttribPointer(3, 4, VertexAttribPointerType.Float, false, 0, 0); //GL.VertexAttribDivisor(3, ?); GL.EnableVertexAttribArray(3); GL.DrawArraysInstanced(PrimitiveType.Triangles, 0, 36, 2);
VertexAttribPointer(..., 4, VertexattribPointerType.Float, ...);
任何高于4的VertexAttribPointer(..., 4, VertexattribPointerType.Float, ...);
导致崩溃。 我认为我必须将该值设置为16?
我不确定我是否需要一个VertexAttribDivisor,可能我需要每36个顶点所以我称之为GL.VertexAttribDivisor(3, 36);
? 但是当我这样做时,我看不到任何立方体。
我的顶点着色器:
#version 330 core layout(location = 0) in vec3 position; layout(location = 1) in vec4 color; layout(location = 2) in vec2 texCoord; layout(location = 3) in mat4 instanceMatrix; uniform mat4 projMatrix; out vec4 vColor; out vec2 texCoords[]; void main(){ gl_Position = instanceMatrix * projMatrix * vec4(position, 1.0); //gl_Position = projMatrix * vec4(position, 1.0); texCoords[0] = texCoord; vColor = color; }
所以我的问题:
- 我的代码出了什么问题?
- 为什么我只能将VertexAttribPointer的size-parameter设置为4?
- VertexAttribDivisor的正确值是多少?
编辑:
根据Andon M. Coleman的回答,我做了这些改变:
GL.BindBuffer(BufferTarget.UniformBuffer, matrixBuffer); GL.BufferData(BufferTarget.UniformBuffer, (IntPtr)(sizeof(float) * 16), IntPtr.Zero, BufferUsageHint.DynamicDraw); //Bind Buffer to Binding Point GL.BindBufferBase(BufferRangeTarget.UniformBuffer, matrixUniform, matrixBuffer); matrixUniform = GL.GetUniformBlockIndex(shaderProgram, "instanceMatrix"); //Bind Uniform Block to Binding Point GL.UniformBlockBinding(shaderProgram, matrixUniform, 0); GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, (IntPtr)(sizeof(float) * 16 * Matrices.Length), Matrices);
并且着色器:
#version 330 core layout(location = 0) in vec4 position; //gets vec3, fills w with 1.0 layout(location = 1) in vec4 color; layout(location = 2) in vec2 texCoord; uniform mat4 projMatrix; uniform UniformBlock { mat4 instanceMatrix[]; }; out vec4 vColor; out vec2 texCoords[]; void main(){ gl_Position = projMatrix * instanceMatrix[0] * position; texCoords[0] = texCoord; vColor = color; }
您已经发现顶点属性位置始终是4分量的困难方式。
使4×4矩阵成为每顶点属性的唯一方法是,如果你承认mat4
是vec4
4倍。
考虑mat4
顶点属性的声明:
layout(location = 3) in mat4 instanceMatrix;
您可能自然会认为位置3存储了16个浮点值,但您会错。 GLSL中的位置始终为4个组件。 因此, mat4 instanceMatrix
实际上占据了4个不同的位置。
这实质上就是instanceMatrix
实际工作方式:
layout(location = 3) in vec4 instanceMatrix_Column0; layout(location = 4) in vec4 instanceMatrix_Column1; layout(location = 5) in vec4 instanceMatrix_Column2; layout(location = 6) in vec4 instanceMatrix_Column3;
幸运的是,您不必以这种方式编写着色器,具有mat4
顶点属性是完全有效的。
但是,您必须编写C#代码以表现这种方式:
GL.BindBuffer(BufferTarget.ArrayBuffer, matrixBuffer); GL.VertexAttribPointer(3, 4, VertexAttribPointerType.Float, false, 64, 0); // c0 GL.VertexAttribPointer(4, 4, VertexAttribPointerType.Float, false, 64, 16); // c1 GL.VertexAttribPointer(5, 4, VertexAttribPointerType.Float, false, 64, 32); // c2 GL.VertexAttribPointer(6, 4, VertexAttribPointerType.Float, false, 64, 48); // c3
同样,您必须为所有4个位置设置顶点属性除数:
GL.VertexAttribDivisor (3, 1); GL.VertexAttribDivisor (4, 1); GL.VertexAttribDivisor (5, 1); GL.VertexAttribDivisor (6, 1);
顺便说一下,因为顶点属性总是4个组件,所以你可以实际声明:
layout(location = 0) in vec4 position;
并停止编写丑陋的代码,如下所示:
gl_Position = instanceMatrix * projMatrix * vec4(position, 1.0);
这是因为OpenGL会自动扩展顶点属性中缺少的组件。
(0.0,0.0,0.0,1.0)
如果在GLSL着色器中将顶点属性声明为vec4
,但仅为XYZ提供数据,则会自动为W
指定值1.0 。
实际上,您不希望存储每个顶点的矩阵。 这浪费了多个顶点属性位置。 您可以考虑的是一系列制服,或者更好的统一缓冲区。 您可以使用Vertex Shader预先声明的变量索引此数组: gl_InstanceID
。 这是解决此问题的最明智的方法,因为您可能会发现每个实例使用的属性多于顶点属性位置(GL 3.3中的mininum 16,只有少数GPU实际支持16个以上)。
请记住,顶点着色器可以在单个调用中使用的vec4
制服数量有限制,而mat4
计数为vec4
大小的4倍。 使用统一缓冲区将允许您绘制比普通旧制服arrays更多的实例。