C++ GLSL-无法访问多个灯光的SSBO阵列的第二个索引
在我的应用程序中,我添加了两个灯光。一个在(0,0,2)处,第二个在(2,0,0)处。以下是我得到的结果(x、y、z轴分别由红色、绿色和蓝色线表示): 请注意,只有第一个灯光工作,而第二个灯光不工作。我使我的应用程序核心配置文件兼容,以使用各种工具(如RenderDoc和NSight)检查缓冲区,这两种工具都向我显示第二个灯光的数据存在于缓冲区中(运行NSight时拍摄的图片): 这些位置似乎被正确地转移到gpu内存缓冲区。下面是我的片段着色器的实现,它使用SSBO处理我的应用程序中的多个灯光:C++ GLSL-无法访问多个灯光的SSBO阵列的第二个索引,c++,opengl,glsl,nsight,opengl-4,C++,Opengl,Glsl,Nsight,Opengl 4,在我的应用程序中,我添加了两个灯光。一个在(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);
}
第50行和第56行表示diffuse+=diff*wLight.color代码>和镜面反射+=wLight.intensity*spec*wLight.color代码>分别为。即使我在第一次抽签前添加灯光,是否真的存在越界访问?为什么在我使用lights.length()而不是2时着色器编译正确
最后,我在for循环中添加了一个简单的if(I==1)
,以查看lights.length()是否等于2,但它不在其中但是我的缓冲区的初始大小是0,然后我添加了一个灯光,将缓冲区大小设置为36字节,我们可以看到第一个灯光工作正常。为什么更新/重新分配不在第二次工作?< /强> ,所以我做的是在C++结构的声明结束时添加一些填充,仅在C++边> < /强>。所需的填充是float[3]或12个字节,总计48个字节。我仍然不确定为什么需要这样做,因为规范说明(如中突出显示的)
如果构件是结构,则结构的基准对齐为N,其中N是其任何构件的最大基准对齐值
成员,并四舍五入到vec4的基本对齐方式。这个
然后,此子结构的各个构件将按偏移指定
递归地应用这组规则,其中
子结构的第一个构件等于
结构。该结构的末端可能有衬垫;基地
子结构后面的杆件偏移向上舍入到
结构的基础对齐的下一个倍数。
[……]
使用std430存储布局时,着色器存储块将
在缓冲存储器中的布局与均匀和着色存储器相同
块使用std140布局,但基础对齐和
规则4中标量和向量数组的步长和规则4中结构的步长
规则9不向上舍入vec4的基本对齐倍数
我的猜测是,glm定义的vec3和glm::f32vec3等结构在使用std430时会递归地向上取整为vec4,因此我的结构必须遵循vec4的对齐方式。如果有人能证实这一点,这将是有趣的,因为上面的链接帖子直接涉及vec4而不是vec3
两个灯都亮的图片:
编辑:
经过进一步调查,结果表明,光结构的最后3个场(强度、衰减和半径)不可用。我通过将位置和颜色从glm::f32vec3改为glm::vec4来修复此问题。有关更多信息,请参阅。由于前面提到的对齐方式,我还留下了一个用于填充的浮动
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);
}
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...
}
(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