使用 OpenGL 的阴影贴图的硬件 PCF
Hardware PCF for Shadow Map using OpenGL
我在 GeForce GTX 750 下使用 OpenGL 4.3 创建阴影贴图。现在基本效果,如下所示,似乎是正确的:
为了消除块状效果,我尝试在着色器中手动执行 2x2 PCF。它导致以下结果,这似乎也是正确的:
对于加速,我想利用显卡提供的好处,它一次获取就给出了比较结果的线性过滤器。但是效果和上面的不一样。 更像是OpenGL线性过滤阴影的渲染,而不是Shadow Map上的过滤:
下面是我如何做硬件PCF:
我注意到要使用硬件 PCF,必须完成两件基本的事情,它们是:
- 使用
shadow
类型的采样器,在我的例子中,是 samplerCubeShadow
(我正在使用立方体贴图类型,因为我正在尝试创建点光源场景)。
设置比较模式和过滤类型,在我的例子中,通过以下代码完成:
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
之后我在shader中使用了texture
这个函数(之所以用texture
而不是textureProj
是因为后者好像不支持立方体贴图阴影纹理,因为它需要 vec5
类型,但显然还不支持):
vec4 posInLight4D = positionsInLight / positionsInLight.w; // divided by the 4-th component)
vec3 texCoord = GetCubeMapTexCoord(posInLight4D.xy); // get the texture coordinate in cube map texture. This can be assumed correct
float lightness = texture(shadowMapHardware, vec4(texCoord, posInLight4D.z));
但不幸的是,这给出了第三张图片中显示的结果。
据我了解,通过比较模式和线性滤镜的设置,显卡会在附近的 2x2 区域内进行比较 ,然后 对结果进行线性插值 ,然后通过 texture
函数返回。我想我已经完成了所有必要的部分,但我仍然无法获得第二张图片中显示的确切结果。
任何人都可以就我可能出错的地方给我任何建议吗?非常感谢。
ps:有趣的是:我试过textureGather
函数,只returns比较结果,不做过滤,它给出了准确结果如第二张图所示。但这缺少自动过滤程序,显然它不是硬件PCF的完整版本。
To erase the blocky-effect, I've tried to do a 2x2 PCF manually in the shader. It leads to the following result, which also seems to be correct:
OpenGL 规范并未规定线性插值深度比较时要使用的特定算法。但是,它通常将其描述为:
The details of this are implementation-dependent, but r
should
be a value in the range [0,1] which is proportional to the number of comparison
passes or failures.
这不是很受限制,它当然不需要您看到的输出 "correct"。
的确,actual PCF 与您所建议的有很大不同。您似乎想要的东西仍然非常块状;它只是不是 binary 块。您的算法没有在比较结果之间进行线性插值;您刚刚进行了 4 个最接近的比较并将它们放在一起平均。
NVIDIA 为您提供的是 PCD 实际上应该 的样子:比较结果之间的线性插值,基于您的采样点。
所以错的是你的期望,而不是 NVIDIA。
根据@Nicol的回答,我想我误解了插值的意思。以下是我对着色器级插值的实现,它看起来与问题中的第二张图片完全一样:
我在 GeForce GTX 750 下使用 OpenGL 4.3 创建阴影贴图。现在基本效果,如下所示,似乎是正确的:
- 使用
shadow
类型的采样器,在我的例子中,是samplerCubeShadow
(我正在使用立方体贴图类型,因为我正在尝试创建点光源场景)。 设置比较模式和过滤类型,在我的例子中,通过以下代码完成:
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
之后我在shader中使用了texture
这个函数(之所以用texture
而不是textureProj
是因为后者好像不支持立方体贴图阴影纹理,因为它需要 vec5
类型,但显然还不支持):
vec4 posInLight4D = positionsInLight / positionsInLight.w; // divided by the 4-th component)
vec3 texCoord = GetCubeMapTexCoord(posInLight4D.xy); // get the texture coordinate in cube map texture. This can be assumed correct
float lightness = texture(shadowMapHardware, vec4(texCoord, posInLight4D.z));
但不幸的是,这给出了第三张图片中显示的结果。
据我了解,通过比较模式和线性滤镜的设置,显卡会在附近的 2x2 区域内进行比较 ,然后 对结果进行线性插值 ,然后通过 texture
函数返回。我想我已经完成了所有必要的部分,但我仍然无法获得第二张图片中显示的确切结果。
任何人都可以就我可能出错的地方给我任何建议吗?非常感谢。
ps:有趣的是:我试过textureGather
函数,只returns比较结果,不做过滤,它给出了准确结果如第二张图所示。但这缺少自动过滤程序,显然它不是硬件PCF的完整版本。
To erase the blocky-effect, I've tried to do a 2x2 PCF manually in the shader. It leads to the following result, which also seems to be correct:
OpenGL 规范并未规定线性插值深度比较时要使用的特定算法。但是,它通常将其描述为:
The details of this are implementation-dependent, but
r
should be a value in the range [0,1] which is proportional to the number of comparison passes or failures.
这不是很受限制,它当然不需要您看到的输出 "correct"。
的确,actual PCF 与您所建议的有很大不同。您似乎想要的东西仍然非常块状;它只是不是 binary 块。您的算法没有在比较结果之间进行线性插值;您刚刚进行了 4 个最接近的比较并将它们放在一起平均。
NVIDIA 为您提供的是 PCD 实际上应该 的样子:比较结果之间的线性插值,基于您的采样点。
所以错的是你的期望,而不是 NVIDIA。
根据@Nicol的回答,我想我误解了插值的意思。以下是我对着色器级插值的实现,它看起来与问题中的第二张图片完全一样: