c++ - OpenGL 着色器存储缓冲区/memoryBarrierBuffer

标签 c++ opengl glsl

我目前创建了两个 SSBO 来处理一些光线,因为 VS-FS in out 接口(interface)无法处理很多光线(我使用的是正向着色)。 对于第一个,我只将值传递给着色器(基本上是只读的)[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 接口(interface)的情况下,存在一些问题:

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 读取值,结果证明这些值只是垃圾。我在内存障碍方面做错了吗?

输出结果是这样的: Pixels bugging

最佳答案

好吧,让我们把明显的错误排除在外:

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 在此循环中永远不会改变,因此您只写了一个灯,并且只写了最后 个灯的值。所有其他灯将具有垃圾/未定义的值。

所以你的意思可能是 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 输出变量。输出变量存储为顶点的一部分。然后,光栅化器在光栅化图元时将此顶点数据插值到图元中,这提供了 the input values。对于FS。所以一个 VS 不能踩踏另一个 VS 的输出变量。

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

因此,您尝试做的唯一方法可能会起作用,如果您的缓冲区在整个场景中的每个顶点都有 numLights 条目.

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

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

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

关于c++ - OpenGL 着色器存储缓冲区/memoryBarrierBuffer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52515023/

相关文章:

OpenGL/D3D : How do I get a screen grab of a game running full screen in Windows?

c++ - 覆盖 C++ 流

c++ - 使用 C++ 将哈希表复制到另一个哈希表

c++ - 如何在 C++ 中被 EOF 停止后恢复输入流?

c++ - 'WCHAR' 和 'wchar_t' 有什么区别?

c++ - Visual Studio 2015 中 OpenGL 4 实现中的纹理单元数 (GL_TEXTUREi)

opengl - 在 OpenGL 中渲染到纹理数组的一层

opengl - 为什么 GL 为您将 `gl_Position` 除以 W 而不是让您自己做?

c++ - 解析 GLSL 着色器字符串以在 Android NDK 中查找变量名称

opengl - 从噪声纹理推导出不确定性值?