【问题标题】:OpenGL Shader Storage Buffer / memoryBarrierBufferOpenGL着色器存储缓冲区/memoryBarrierBuffer
【发布时间】:2018-09-26 09:59:31
【问题描述】:

我目前创建了两个 SSBO 来处理一些灯光,因为 VS-FS in out 接口无法处理很多灯光(我使用前向着色)。 对于第一个,我只将值传递给着色器(基本上是只读的)[cpp]:

struct GLightProperties
{
    unsigned int numLights;
    LightProperties properties[];
};

...

glp = (GLightProperties*)malloc(sizeof(GLightProperties) + sizeof(LightProperties) * lastSize);

...

glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLightProperties) + sizeof(LightProperties) * lastSize, glp, GL_DYNAMIC_COPY);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

着色器文件 [GLSL]:

layout(std430, binding = 1) buffer Lights
{
    uint numLights;
    LightProperties properties[];
}lights;

所以这第一个 SSBO 可以正常工作。但是,在另一个目的是 VS-FS 接口中,存在一些问题:

glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo2);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float) * 4 * 3 * lastSize, nullptr, GL_DYNAMIC_COPY);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

GLSL:

struct TangentProperties
{
    vec4 TangentLightPos;
    vec4 TangentViewPos;
    vec4 TangentFragPos;
};

layout(std430, binding = 0) buffer TangentSpace
{
    TangentProperties tangentProperties[];
}tspace;

所以你注意到我将nullptr 传递给glBufferData 因为vs 将写入缓冲区而fs 将读取其内容。 就像在 VS 阶段一样:

for(int i = 0; i < lights.numLights; i++)
{
    tspace.tangentProperties[index].TangentLightPos.xyz = TBN * lights.properties[index].lightPosition.xyz;
    tspace.tangentProperties[index].TangentViewPos.xyz  = TBN * camPos;
    tspace.tangentProperties[index].TangentFragPos.xyz  = TBN * vec3(worldPosition);
    memoryBarrierBuffer();
}

在此之后,FS 读取值,结果只是垃圾。我在记忆障碍方面做错了吗?

输出结果是这样的:

【问题讨论】:

  • 我自己永远不要使用memoryBarrierBuffer(),但是根据文档,它说屏障确保在内存屏障之前执行的所有更改在后续阶段都可见,这意味着它不会对当前阶段(如果我理解正确的话)。既然你有它在一个循环中,我猜你希望它为下一次迭代同步线程?文档:khronos.org/registry/OpenGL-Refpages/gl4/html/…

标签: c++ opengl glsl


【解决方案1】:

好的,让我们解决这个明显的错误:

for(int i = 0; i < lights.numLights; i++)
{
    tspace.tangentProperties[index].TangentLightPos.xyz = TBN * lights.properties[index].lightPosition.xyz;
    tspace.tangentProperties[index].TangentViewPos.xyz  = TBN * camPos;
    tspace.tangentProperties[index].TangentFragPos.xyz  = TBN * vec3(worldPosition);
    memoryBarrierBuffer();
}

index 在这个循环中永远不会改变,所以你只写一个灯,你只写 last 个灯的值。所有其他灯都将具有垃圾/未定义值。

所以你的意思可能是i 而不是index

但这只是问题的开始。看,如果你做出改变,你会得到这个:

for(int i = 0; i < lights.numLights; i++)
{
    tspace.tangentProperties[i].TangentLightPos.xyz = TBN * lights.properties[i].lightPosition.xyz;
    tspace.tangentProperties[i].TangentViewPos.xyz  = TBN * camPos;
    tspace.tangentProperties[i].TangentFragPos.xyz  = TBN * vec3(worldPosition);
}
memoryBarrierBuffer();

注意屏障在循环之外。

这就产生了一个新问题。此代码将使每个顶点着色器调用写入相同的内存缓冲区。毕竟,SSBO 不是 VS 输出 变量。输出变量存储为顶点的一部分。然后,光栅化器在对图元进行光栅化时在图元中插入此顶点数据,从而为 FS 提供the input values。所以一个 VS 不能踩到另一个 VS 的输出变量。

SSBO 不会发生这种情况。每个 VS 都作用于相同 SSBO 内存。因此,如果它们写入同一数组的相同索引,它们将写入相同的内存地址。这是一个竞争条件(因为兄弟调用之间不能同步),因此是未定义的行为。

因此,唯一可行的方法是,如果您的缓冲区对整个场景中的每个顶点都有numLights 条目,则可能会起作用。

这是一个根本不合理的空间量。即使您可以将其降低到特定绘图调用中的顶点数(这是可行的,但我不会说如何),您仍然会落后于性能。每次 FS 调用都必须读取 144 字节的数据每个光源(3 个表条目,三角形的每个顶点一个),线性插值这些值,然后进行光照计算。 p>

将 TBN 矩阵作为 VS 输出传递并在 FS 中进行矩阵乘法会更快。是的,这是很多矩阵乘法,但 GPU 在矩阵乘法上非常快,而在读取内存时真的很慢

另外,重新考虑您是否需要切线空间片段位置。一般来说,你永远不会这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多