【问题标题】:GLSL - unable to access second index of a SSBO array for multiple lightsGLSL - 无法访问多个灯的 SSBO 数组的第二个索引
【发布时间】:2017-10-31 20:35:19
【问题描述】:

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

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

位置似乎已正确传输到 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 &lt; 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 字节的灯,我们可以看到第一个灯工作正常。为什么第二次更新/重新分配不起作用?

【问题讨论】:

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


    【解决方案1】:

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

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

    当使用 std430 存储布局时,着色器存储块将 在缓冲存储中布局与统一和着色器存储相同 使用 std140 布局的块,除了基本对齐和 规则 4 中的标量和向量数组以及 规则 9 不向上舍入 vec4 基本对齐的倍数。

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

    两个灯都工作的图片:

    编辑:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-07
      相关资源
      最近更新 更多