【发布时间】: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 < 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