在我的应用程序中,我添加两个灯。一个在(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 < 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个字节,我们可以看到第一个灯工作正常。为什么更新/重新分配第二次不起作用?
答案 0 :(得分:2)
所以我所做的就是在结构的声明结尾处添加一些填充仅在C ++端。所需的填充是float [3]或12个字节,总计最多48个字节。我仍然不确定为什么这是必需的,因为规范说明(在this post中突出显示)
- 如果成员是结构,则结构的基本对齐为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中找到更多信息。由于前面提到的对齐,我还留下了一个用于填充的浮点数。