【问题标题】:Setup of matrix for instance shader为实例着色器设置矩阵
【发布时间】:2014-07-01 00:57:45
【问题描述】:

我想绘制实例立方体。

我可以成功拨打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(对于 Vector4)* 4(对于 4 个向量)* array-size。

绘制循环:

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 的数字都会导致崩溃。我觉得我必须将该值设置为 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;
}

【问题讨论】:

    标签: c# opengl matrix opentk


    【解决方案1】:

    您已经发现顶点属性位置始终是 4 分量的困难方式。

    使 4x4 矩阵成为每个顶点属性的唯一方法是,如果您承认 mat4vec4 的 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);
    

    顺便说一句,因为顶点属性总是四分量的,你实际上可以声明:

    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。这确实是解决这个问题的最明智的方法,因为您可能会发现自己在每个实例中使用的属性比您拥有的顶点属性位置(mininum 在 GL 3.3 中为 16,实际上只有少数 GPU 支持超过16).

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

    【讨论】:

    • 我修复了我的代码并且它工作了:) 现在我尝试按照您的建议使用统一缓冲区来完成它。您可以看到在我的问题中编辑的代码。问题是,当我将instanceMatrix[0] 更改为instanceMatrix[gl_InstanceID] 时,我收到以下错误:“OpenGL 需要常量索引来访问未调整大小的数组(instanceMatrix)”。你能帮我解决这个问题吗?
    • 如果你走这条路或使用着色器存储缓冲区,你将需要为统一数组使用一个恒定大小。但是,SSB 是 GL4 功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多