我正在使用全向点光源。我已经使用立方体贴图纹理实现阴影贴图作为6个帧缓冲区的颜色附加,并在其每个像素中编码光到片段的距离。
现在我想,如果可以的话,以这种方式改变我的实现:
将立方体贴图中的深度值转换回距离时出现问题。我使用光到片段向量(在世界空间中)来获取立方体贴图中的深度值。此时,我不知道正在使用六个面中的哪一个,也不知道2D纹理坐标与我正在读取的深度值相匹配。那么如何将该深度值转换为距离?
以下是我的代码片段,用于说明:
深度纹理:
glGenTextures(1, &TextureHandle);
glBindTexture(GL_TEXTURE_CUBE_MAP, TextureHandle);
for (int i = 0; i < 6; ++i)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT,
Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Framebuffers构造:
for (int i = 0; i < 6; ++i)
{
glGenFramebuffers(1, &FBO->FrameBufferID);
glBindFramebuffer(GL_FRAMEBUFFER, FBO->FrameBufferID);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, TextureHandle, 0);
glDrawBuffer(GL_NONE);
}
我正在尝试编写片段着色器来实现我的代码:
float ComputeShadowFactor(samplerCubeShadow ShadowCubeMap, vec3 VertToLightWS)
{
float ShadowVec = texture(ShadowCubeMap, vec4(VertToLightWS, 1.0));
ShadowVec = DepthValueToDistance(ShadowVec);
if (ShadowVec * ShadowVec > dot(VertToLightWS, VertToLightWS))
return 1.0;
return 0.0;
}
DepthValueToDistance函数是我的实际问题。
答案 0 :(得分:10)
因此,解决方案是将光到片段矢量转换为深度值,而不是将立方体图中读取的深度转换为距离。
以下是修改过的着色器代码:
float VectorToDepthValue(vec3 Vec)
{
vec3 AbsVec = abs(Vec);
float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));
const float f = 2048.0;
const float n = 1.0;
float NormZComp = (f+n) / (f-n) - (2*f*n)/(f-n)/LocalZcomp;
return (NormZComp + 1.0) * 0.5;
}
float ComputeShadowFactor(samplerCubeShadow ShadowCubeMap, vec3 VertToLightWS)
{
float ShadowVec = texture(ShadowCubeMap, vec4(VertToLightWS, 1.0));
if (ShadowVec + 0.0001 > VectorToDepthValue(VertToLightWS))
return 1.0;
return 0.0;
}
对VectorToDepthValue(vec3 Vec)
的解释:
LocalZComp
对应于给定Vec
的Z分量到立方体贴图的匹配平截头体中。它实际上是Vec
的最大组件(例如,如果Vec.y是最大的组件,我们将查看立方体贴图的Y +或Y-面)。
如果你看一下这个wikipedia article,你就会理解之后的数学(我把它保存在正式的形式中以便理解),它只是将LocalZComp
转换为一个标准化的Z值(在[-1..1])然后将其映射到[0..1],这是深度缓冲值的实际范围。 (假设你没有改变它)。 n
和f
是用于生成立方体贴图的截头体的近远值。
ComputeShadowFactor
然后只是将立方体贴图的深度值与从片段到光的矢量(此处名为VertToLightWS
)计算的深度值进行比较,同时添加一个小的深度偏差(缺少在问题中),如果片段没有被光遮挡,则返回1.
答案 1 :(得分:4)
我想添加有关推导的更多细节。
设 V 为光到片段方向向量。
正如Benlitz所说,可以通过取 V 的绝对值的最大值来计算相应立方体平截头体/“眼睛空间”中的 Z 值。 s组件。
Z = max(abs(V.x),abs(V.y),abs(V.z))
然后,确切地说,我们应该否定 Z ,因为在OpenGL中,负Z轴指向屏幕/视锥体。
现在我们想要获得 -Z 的深度缓冲区“兼容”值。
查看OpenGL透视矩阵......
http://www.songho.ca/opengl/files/gl_projectionmatrix_eq16.png
...我们看到,对于任何与该矩阵相乘的均匀向量,得到的z值完全独立于向量的x和y分量。
因此我们可以简单地将此矩阵与均匀向量(0,0, -Z ,1)相乘,得到向量(分量):
x = 0
y = 0
z = (-Z * -(f+n) / (f-n)) + (-2*f*n / (f-n))
w = Z
然后我们需要进行透视分割,所以我们将z除以w( Z ),这给了我们:
z' = (f+n) / (f-n) - 2*f*n / (Z* (f-n))
此z'在OpenGL的标准化设备坐标(NDC)范围[-1,1]中,需要转换为深度缓冲区兼容范围[0,1]:
z_depth_buffer_compatible = (z' + 1.0) * 0.5
补充说明:
将(f + n),(f-n)和(f * n)的结果上传为着色器制服可以节省计算量。
V 需要位于世界空间,因为阴影立方体贴图通常在世界空间中轴对齐,因此“ max(abs(Vx),abs(Vy), abs(Vz))“ - 仅当 V 是世界空间方向向量时才有效。