GLSL - 无法访问多个灯的SSBO阵列的第二个索引

时间:2017-10-31 20:35:20

标签: c++ opengl glsl nsight opengl-4

在我的应用程序中,我添加两个灯。一个在(0,0,2),第二个在(2,0,0)。这是我得到的(x,y,z轴分别用红色,绿色和蓝色线表示):

cube with only one light working

注意第一盏灯是如何工作而第二盏灯不工作的。我使我的应用程序核心配置文件兼容,使用RenderDoc和NSight等各种工具检查缓冲区,并且两者都显示缓冲区中存在第二个光的数据(运行Nsight时拍摄的照片):

nsight capture

这些位置似乎正确地转移到了gpu内存缓冲区。这是我的片段着色器的实现,它使用SSBO处理我的应用程序中的多个灯光:

#version 430

struct Light {
  vec3  position;
  vec3  color;
  float intensity;
  float attenuation;
  float radius;
};

layout (std140, binding = 0) uniform CameraInfo {
  mat4  ProjectionView; 
  vec3  eye;
};

layout (std430, binding = 1) readonly buffer LightsData {
    Light lights[];
};

uniform vec3  ambient_light_color;
uniform float ambient_light_intensity;

in  vec3 ex_FragPos;
in  vec4 ex_Color;
in  vec3 ex_Normal;
out vec4 out_Color;

void main(void)
{
    // Basic ambient light
    vec3 ambient_light = ambient_light_color * ambient_light_intensity;

    int i;
    vec3 diffuse = vec3(0.0,0.0,0.0);
    vec3 specular = vec3(0.0,0.0,0.0);
    for (i = 0; i < lights.length(); ++i) {
        Light wLight = lights[i];
        // Basic diffuse light
        vec3 norm = normalize(ex_Normal); // in this project the normals are all normalized anyway...
        vec3 lightDir = normalize(wLight.position - ex_FragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        diffuse += diff * wLight.color;

        // Basic specular light
        vec3 viewDir = normalize(eye - ex_FragPos);
        vec3 reflectDir = reflect(-lightDir, norm);  
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
        specular += wLight.intensity * spec * wLight.color;  
    }

    out_Color = ex_Color * vec4(specular + diffuse + ambient_light,1.0); 
}

请注意,我已阅读OpenGL 4.5 spec的第7.6.2.2节,如果我理解正确,我的对齐应遵循我的结构中最大成员的大小,这是一个vec3和我的结构大小是36个字节所以一切都应该没问题。我也尝试了不同的std版本(例如std140)并添加了一些填充,但没有什么能解决第二个问题。在我的C ++代码中,我有这些定义来添加我的应用程序中的灯光:

light_module.h / .CC:

struct Light {
  glm::f32vec3  position;
  glm::f32vec3  color;
  float         intensity;
  float         attenuation;
  float         radius;
};
...
constexpr GLuint        LIGHTS_SSBO_BINDING_POINT = 1U;
std::vector<Light>      _Lights;
...
void AddLight(const Light &light) {
  // Add to _Lights
  _Lights.push_back(light);
UpdateSSBOBlockData(
  LIGHTS_SSBO_BINDING_POINT, _Lights.size()* sizeof(Light),
  static_cast<void*>(_Lights.data()), GL_DYNAMIC_DRAW);
}

shader_module.h / .CC:

using SSBOCapacity = GLuint;
using BindingPoint = GLuint;
using ID = GLuint;
std::map<BindingPoint, std::pair<ID, SSBOCapacity> >  SSBO_list;
...
void UpdateSSBOBlockData(GLuint a_unBindingPoint,
  GLuint a_unSSBOSize, void* a_pData, GLenum a_eUsage) {
  auto SSBO = SSBO_list.find(a_unBindingPoint);
  if (SSBO != SSBO_list.end()) {
    GLuint unSSBOID = SSBO->second.first;
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, unSSBOID);
    glBufferData(GL_SHADER_STORAGE_BUFFER, a_unSSBOSize, a_pData, a_eUsage);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); //unbind
  }
  else 
    // error handling...
}

基本上,每次在我的应用中添加灯光时,我都会尝试使用glBufferData更新/重新分配SSBO大小。

现在,由于我在处理第二个光数据时遇到问题,因此我将片段着色器代码更改为仅通过强制i = 1并循环直到i < 2来执行SSBO阵列中的第二个灯光。 ,但我收到以下错误:

(50) : error C1068: ... or possible array index out of bounds
(50) : error C5025: lvalue in field access too complex
(56) : error C1068: ... or possible array index out of bounds
(56) : error C5025: lvalue in field access too complex

第50行和第56行分别指diffuse += diff * wLight.color;specular += wLight.intensity * spec * wLight.color;。即使我在第一次抽奖之前添加灯光,是否真的有出界限?当我使用lights.length()而不是2?

时,为什么着色器正确编译?

最后,我在for循环中添加了一个简单的if (i == 1),以查看lights.length()是否等于2,但它并没有进入。 然而我的缓冲区的初始大小是0,然后我添加一个灯,将缓冲区大小设置为36个字节,我们可以看到第一个灯工作正常。为什么更新/重新分配第二次不起作用?

1 个答案:

答案 0 :(得分:2)

所以我所做的就是在结构的声明结尾处添加一些填充仅在C ++端。所需的填充是float [3]或12个字节,总计最多48个字节。我仍然不确定为什么这是必需的,因为规范说明(在this post中突出显示)

  
      
  1. 如果成员是结构,则结构的基本对齐为N,其中N是其中任何一个的最大基本对齐值   成员,并向上舍入到vec4的基本对齐方式。该   然后,为该子结构的各个成员分配偏移量   递归地应用这组规则,其中基本偏移量   子结构的第一个成员等于对齐的偏移量   结构。该结构可以在末端具有填充物;基地   子结构后的成员偏移量向上舍入到   下一个结构的基础对齐的多个。   [...]
  2.         

    使用std430存储布局时,着色器存储块将是   缓冲存储中的布局与均匀和着色器存储相同   使用std140布局的块,除了基本对齐和   规则4中的标量和向量数组以及结构中的结构   规则9 未向上舍入为vec4 的基本对齐的倍数。

我的猜测是,当使用std430时,glm定义的vec3和glm :: f32vec3等结构会以递归方式向上舍入为vec4,因此我的结构必须遵循vec4的对齐方式。如果有人能证实这一点,那将会很有趣,因为上面的链接帖子直接处理vec4而不是vec3。

两个灯都工作的图片:

second light working

编辑:

经过进一步调查,结果发现Light结构的最后3个字段(强度,衰减和半径)不可用。我通过将位置和颜色从glm :: f32vec3更改为glm :: vec4来修复此问题。可以在similar post中找到更多信息。由于前面提到的对齐,我还留下了一个用于填充的浮点数。