OpenGL着色器存储缓冲区/ memoryBarrierBuffer

Pri*_*ter 2 c++ opengl glsl

我目前创建了两个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);
Run Code Online (Sandbox Code Playgroud)

着色器文件[GLSL]:

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

所以这第一个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);
Run Code Online (Sandbox Code Playgroud)

GLSL:

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

layout(std430, binding = 0) buffer TangentSpace
{
    TangentProperties tangentProperties[];
}tspace;
Run Code Online (Sandbox Code Playgroud)

所以在这里你注意到我传递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();
}
Run Code Online (Sandbox Code Playgroud)

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

输出结果是这样的: 像素窃听

Nic*_*las 6

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

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();
}
Run Code Online (Sandbox Code Playgroud)

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();
Run Code Online (Sandbox Code Playgroud)

请注意,屏障位于循环外部.

这造成了一个新问题.此代码将每个顶点着色器调用写入相同的内存缓冲区.毕竟,SSBO不是VS 输出变量.输出变量存储为顶点的一部分.然后光栅化器在对基元进行光栅化时在基元上插入该顶点数据,这为FS 提供输入值.所以一个VS不能踩到另一个VS的输出变量.

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

因此,您尝试做的唯一方法是,如果您的缓冲区包含整个场景中numLights每个顶点的条目.

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

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

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