使用比制服更多的值进行实例化可以存储

时间:2016-09-26 16:04:44

标签: c++ qt opengl

我对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;
}

如果它重要,我正在使用QOpenGLExtraFunctions。

1 个答案:

答案 0 :(得分:1)

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

例如,

UBOs;它们通常比非阻挡制服具有更大的存储容量。现在在你的情况下,这可能不会起作用,因为存储200,000 vec3svec4s将需要比大多数实现允许UBO提供更多的存储。你需要的是无限存储。

实例化数组

Instanced arrays使用instanced rendering mechanism根据实例索引自动获取顶点属性。这要求您的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永远不会受其影响。

缓冲纹理

缓冲区纹理是线性的一维纹理,可以从缓冲区对象的存储中获取数据。

您的着色器逻辑将如下所示:

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*/
}

缓冲区纹理只能通过direct texel fetching functions texelFetch来访问。

GL 4.x中的缓冲区纹理可以使用一些3通道格式,但早期的GL版本不能为您提供该选项(不是没有扩展名)。因此,您可能希望将数据扩展为4通道值,而不是3通道。

另一个问题是缓冲区纹理确实有最大的大小限制。并且所需的最小值仅为64KB,因此实例化数组方法可能更可靠(因为它具有 no 大小限制)。但是,所有非英特尔OpenGL实现都为缓冲区纹理提供了巨大的尺寸。

SSBOs

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

您的着色器代码如下所示:

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。这是因为缓冲区支持的接口块中的you should never use vec3(即:UBO / SSBO)。

在代码中,SSBO的工作方式与UBO非常相似。您绑定它们以与glBindBufferRange一起使用。