C# 为实例着色器设置矩阵

C# 为实例着色器设置矩阵,c#,opengl,matrix,opentk,C#,Opengl,Matrix,Opentk,我想画实例立方体 我可以调用GL.DrawArraysInstanced(PrimitiveType.Triangles,0,36,2)成功 我的问题是所有的立方体都是在相同的位置和相同的旋转。我如何为每个多维数据集单独更改它 为了创建不同的位置等等,我需要为每个立方体创建一个矩阵,对吗?我创造了这个: Matrix4[] Matrices = new Matrix4[]{ Matrix4.Identity, //do nothing Matrix4.Identity * Matrix4

我想画实例立方体

我可以调用
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
是指向我的缓冲区的指针。我不确定大小是否正确,我取了float*4(对于向量4)*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);
VertexAttributePointer(…,4,VertexAttributePointerType.Float,…)中大于4的任何数字导致崩溃。我想我必须把这个值设为16

我不确定我是否需要一个VertexAttributeDivisor,可能我每36个顶点就需要一个,所以我调用
GL.VertexAttributeDivisor(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;
}
所以我的问题是:

  • 我的代码有什么问题
  • 为什么我只能将VertexAttribute指针的大小参数设置为4
  • VertexAttributeDivisor的正确值是多少

编辑:

根据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分量的困难方法。 使4x4矩阵成为逐顶点属性的唯一方法是承认
mat4
vec4
的4倍

考虑您的代码< Matt4/COD>顶点属性的声明: 您可能会自然而然地认为位置3存储16个浮点值,但您可能错了。GLSL中的位置始终为4分量。因此,
mat4 instanceMatrix
实际上占据了4个不同的位置

这就是
instanceMatrix
实际工作的方式: 幸运的是,您不必这样编写着色器,拥有
mat4
顶点属性是完全有效的

但是,您必须编写您的C#代码才能这样做: 同样,必须为所有4个位置设置顶点属性除数:
顺便说一句,由于顶点属性始终为4分量,因此实际上可以声明: 不要再写这样难看的代码了: 这是因为顶点属性中缺少的组件由OpenGL自动展开

(0.0,0.0,0.0,1.0)

如果在GLSL着色器中将顶点属性声明为
vec4
,但仅为XYZ提供数据,则
W
会自动指定一个1.0的值


实际上,您不希望按顶点存储矩阵。这是对多个顶点属性位置的浪费。你可以考虑的是一套制服,或者更好的是统一的缓冲区。可以使用顶点着色器预先声明的变量为该数组编制索引:
gl\u InstanceID
。这确实是最明智的方法,因为您可能会发现每个实例使用的属性比顶点属性位置更多(GL 3.3中的mininum 16,只有少数GPU实际支持16个以上)


请记住,顶点着色器在一次调用中可以使用的
vec4
数量是有限的,并且
mat4
计数为
vec4
大小的4倍。使用统一缓冲区将允许您绘制比普通旧的统一数组多得多的实例。

我修复了代码,它成功了:)现在,我尝试按照您的建议使用统一缓冲区。您可以在我的问题中看到已编辑的代码。问题是,当我将
instanceMatrix[0]
更改为
instanceMatrix[gl\u InstanceID]
时,会出现以下错误:“OpenGL需要常量索引才能进行非大小数组访问(instanceMatrix)”。你能帮我一下吗?如果你这样做或者使用着色器存储缓冲区,你需要为均匀数组使用一个恒定的大小。但是,SSB是GL4特性。
layout(location = 3) in mat4 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;
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
GL.VertexAttribDivisor (3, 1);
GL.VertexAttribDivisor (4, 1);
GL.VertexAttribDivisor (5, 1);
GL.VertexAttribDivisor (6, 1);
layout(location = 0) in vec4 position;
gl_Position = instanceMatrix * projMatrix * vec4(position, 1.0);