使用 GLSL 时,多灯阴影映射无法正常工作
Posted
技术标签:
【中文标题】使用 GLSL 时,多灯阴影映射无法正常工作【英文标题】:Multi lights shadow mapping does not work correctly using GLSL 【发布时间】:2014-01-11 21:20:09 【问题描述】:我已经实现了基本的阴影映射算法,但它只适用于一盏灯。
我想用以下两个点光源渲染一个场景:
Light_1 - 位置:vec3(-8.0f, 5.0f, 8.0f),方向:vec3(1.3f, -1.0f, -1.0f) Light_2 - 位置:vec3(8.0f, 5.0f, 8.0f),方向:vec3(1.3f, -1.0f, -1.0f)如果我分别渲染两个灯,我会得到以下结果:
使用 Light_1 进行渲染:
使用 Light_2 进行渲染:
但是两个灯放在一起看起来是这样的:
如您所见,第一个阴影似乎已正确渲染,但它位于不正确的 light_2 阴影下方。总结一下情况,我的盒子的纹理绑定到纹理单元 0。阴影深度纹理从纹理单元 1 绑定,如果有多个深度纹理(所以至少有两个光照,比如这个例子),有绑定到纹理单元 1 + 1 (GL_TEXTURE1 + 1)。这是代表我所说的代码:
for (int idy = 0; idy < this->m_pScene->getLightList().size(); idy++)
[...]
Light *light = this->m_pScene->getLightList()[idy];
FrameBuffer *frameBuffer = light->getFrameBuffer();
glActiveTexture(GL_TEXTURE1 + idy);
glBindTexture(GL_TEXTURE_2D, frameBuffer->getTexture()->getTextureId()); //To unbind
shaderProgram->setUniform(std::string("ShadowMatrix[").append(Convertor::toString<int> (idy)).append("]").c_str(), this->m_pScene->getLightList()[idy]->getBiasViewPerspectiveMatrix() * modelMatrix);
shaderProgram->setUniform(std::string("ShadowMap[").append(Convertor::toString<int>(idy)).append("]").c_str(), (int)idy + 1);
在我们的例子中它对应于:
shaderProgram->setUniform("ShadowMatrix[0]", <shadow_matrix_light_1>);
shaderProgram->setUniform("ShadowMap[0]", 1); (GL_TEXTURE1)
shaderProgram->setUniform("ShadowMatrix[1]", <shadow_matrix_light_2>);
shaderProgram->setUniform("ShadowMap[1]", 2); (GL_TEXTURE2)
顶点着色器如下(仅适用于 2 个光源):
#version 400
#define MAX_SHADOW_MATRIX 10
#define MAX_SHADOW_COORDS 10
layout (location = 0) in vec4 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
layout (location = 2) in vec2 VertexTexture;
uniform mat3 NormalMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 ShadowMatrix[MAX_SHADOW_MATRIX];
uniform mat4 MVP;
uniform int lightCount;
out vec3 Position;
out vec3 Normal;
out vec2 TexCoords;
out vec4 ShadowCoords[MAX_SHADOW_COORDS];
void main(void)
TexCoords = VertexTexture;
Normal = normalize(NormalMatrix * VertexNormal);
Position = vec3(ModelViewMatrix * VertexPosition);
for (int idx = 0; idx < lightCount; idx++)
ShadowCoords[idx] = ShadowMatrix[idx] * VertexPosition;
gl_Position = MVP * VertexPosition;
以及片段着色器的一段代码:
[...]
vec3 evalBasicFragmentShadow(vec3 LightIntensity, int idx)
vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
if (ShadowCoords[idx].w > 0.0f)
vec4 tmp_shadow_coords = ShadowCoords[idx];
tmp_shadow_coords.z -= SHADOW_OFFSET;
float shadow = textureProj(ShadowMap[idx], tmp_shadow_coords);
LightIntensity = LightIntensity * shadow + Ambient;
else
LightIntensity = LightIntensity + MaterialInfos.Ka;
return (LightIntensity);
vec3 getLightIntensity(vec3 TexColor)
vec3 LightIntensity = vec3(0.0f);
for (int idx = 0; idx < lightCount; idx++)
vec3 tnorm = (gl_FrontFacing ? -normalize(Normal) : normalize(Normal));
vec3 lightDir = vec3(LightInfos[idx].Position) - Position;
vec3 lightDirNorm = normalize(lightDir);
float lightAtt = getLightAttenuation(lightDir, LightInfos[idx]);
LightIntensity += Point_ADS_Shading(lightAtt, -tnorm, lightDirNorm, TexColor, idx);
LightIntensity = evalBasicFragmentShadow(LightIntensity, idx);
return (LightIntensity);
[...]
这看起来像是一个纹理单元问题,因为两个阴影分别被完美渲染,并且我正确使用了 glActiveTexture(我认为是这样)。另外,我注意到如果我改变灯光的加载顺序,不好的阴影是由“另一盏灯”引起的(恰恰相反)。所以它似乎来自纹理单元 2,但我不明白为什么。有人可以帮助我吗?非常感谢您的帮助。
【问题讨论】:
我希望我有时间将我的 OpenGL 知识更新到 OpenGL 4,这看起来真漂亮! 您不必一直写:vec4(VertexPosition, 1.0f)
,只需将 VertexPosition 声明为 vec4
即可。如果您的 glVertexAttribPointer (...)
调用只为 XYZ 提供了足够的数据,则 GLSL 会自动为 W
填写 1.0。此外,您正在做的这整个复杂的事情是将idy
转换为一个字符并从中构建一个字符串(例如“ShadowCoords[”idy“]”)是不必要的 - 您可以获得ShadowCoords
的统一位置,然后添加idy
到那个值,因为统一数组中的每个元素都保证分配一个连续的位置。
谢谢。我根据你的建议更新了我的顶点着色器代码(我同意你的观点,它好多了)。但是,我发现您的第二条建议非常有趣,并将其应用于 ShadowCoords。但是是否可以将它与 'LightInfos['idy'].position' 之类的结构一起应用?提前致谢。
【参考方案1】:
我解决了我的问题。实际上,我只是填充了第一个深度纹理(用于第一个加载的光)。所以对于第二盏灯,阴影贴图没有被填充,这就是上面第三张图片中黑色区域的原因。
这是最终结果:
我希望这篇文章对某人有用。感谢您的关注。
【讨论】:
以上是关于使用 GLSL 时,多灯阴影映射无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章