实例化时使用的值超过制服所能存储的值

Instancing using more values than uniforms can store

本文关键字:制服 存储 实例化      更新时间:2023-10-16

我对OpenGL相当陌生,并试图使用统一数组实现实例化。但是,我试图调用的实例数量大于MAX_UNIFORM_LOCATIONS限制:

QOpenGLShader::link: error: count of uniform locations > MAX_UNIFORM_LOCATIONS(262148 > 98304)error: Too many vertex shader default uniform block components
error: Too many vertex shader uniform components

还有什么方法可以处理这么多的对象?到目前为止,这是我的着色器代码:

layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
out vec3 vert;
out vec3 vertNormal;
out vec3 color;
uniform mat4 projMatrix;
uniform mat4 camMatrix;
uniform mat4 worldMatrix;
uniform mat4 myMatrix;
uniform sampler2D sampler;
uniform vec3 positions[262144];
void main() {
   vec3 t = vec3(positions[gl_InstanceID].x, positions[gl_InstanceID].y, positions[gl_InstanceID].z);
   float val = 0;
   mat4 wm = myMatrix * mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1) * worldMatrix;
   color = vec3(0.4, 1.0, 0);
   vert = vec3(wm * vertex);
   vertNormal = mat3(transpose(inverse(wm))) * normal;
   gl_Position = projMatrix * camMatrix * wm * vertex;
}

如果它应该是重要的,我使用qopenglextrfunctions

有许多方法可以克服统一存储的限制:

例如,

ubo;他们通常比非街区制服有更大的存储容量。现在在您的情况下,这可能不起作用,因为存储200,000 vec3 s vec4s将需要比大多数实现允许ubo提供的更多的存储空间。你需要的是无限的存储空间。

数组中实例化

实例数组使用实例渲染机制根据实例索引自动获取顶点属性。这需要您的VAO设置工作更改一点。

你的着色器看起来像这样:

layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec3 position;
out vec3 vert;
out vec3 vertNormal;
out vec3 color;
uniform mat4 projMatrix;
uniform mat4 camMatrix;
uniform mat4 worldMatrix;
uniform mat4 myMatrix;
uniform sampler2D sampler;
void main() {
  vec3 t = position;
  /*etc*/
}

在这里,着色器本身从不使用gl_InstanceID。这将根据您的VAO自动发生。

设置代码必须包含以下内容:

glBindBuffer(GL_ARRAY_BUFFER, buffer_containing_instance_data);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
glVertexAttribDivisor(2, 1);

此代码假设实例数据位于缓冲区的开头,并且每个值为3个浮点数(紧密打包)。因为你正在使用顶点属性,你可以使用通常的顶点属性压缩技术。

最后一个调用,对glVertexAttribDivisor是什么告诉OpenGL,它将只移动到数组中的下一个值一次,而不是基于顶点的索引。

注意,通过使用实例化数组,您还可以使用baseInstance glDraw*调用。OpenGL中的baseInstance仅受实例化数组的尊重;gl_InstanceID不受此影响。

缓冲纹理

Buffer textures是线性的,一维纹理,从Buffer对象的存储中获取数据。

你的着色器逻辑看起来像这样:

layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
out vec3 vert;
out vec3 vertNormal;
out vec3 color;
uniform mat4 projMatrix;
uniform mat4 camMatrix;
uniform mat4 worldMatrix;
uniform mat4 myMatrix;
uniform sampler2D sampler;
uniform samplerBuffer positions;
void main() {
  vec3 t = texelFetch(positions, gl_InstanceID).xyz;
  /*etc*/
}

缓冲区纹理只能通过texelFetch等直接获取纹理的函数来访问。

GL 4中的缓冲纹理。x可以使用一些3通道格式,但早期的GL版本没有给你这个选项(除非有扩展)。因此,您可能希望将数据扩展到4通道值,而不是3通道。

另一个问题是缓冲纹理确实有最大大小限制。所需的最小大小只有64KB,因此实例化的数组方法可能更可靠(因为它没有大小限制)。然而,所有非intel的OpenGL实现都为缓冲区纹理提供了巨大的尺寸。

SSBOs

着色器存储缓冲区对象就像ubo,只是你可以读和写它们。后一种工具对您来说并不重要。这里的主要优势是它们所需的最小OpenGL大小是16M b(并且实现通常根据可用视频内存的顺序返回大小限制)。所以尺寸限制不是问题。

你的着色器代码看起来像这样:

layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
out vec3 vert;
out vec3 vertNormal;
out vec3 color;
uniform mat4 projMatrix;
uniform mat4 camMatrix;
uniform mat4 worldMatrix;
uniform mat4 myMatrix;
uniform sampler2D sampler;
buffer PositionSSBO
{
  vec4 positions[];
};
void main() {
  vec3 t = positions[gl_InstanceID].xyz;
  /*etc*/
}

注意,我们在这里显式地使用了vec4。这是因为你永远不应该在缓冲区支持的接口块(如:UBO/SSBO)中使用vec3

在代码中,ssbo的工作方式与ubo非常相似。您可以将它们与glBindBufferRange绑定使用。