Opengl 具有深度立方贴图的全向阴影贴图

Opengl 具有深度立方贴图的全向阴影贴图,opengl,glsl,shader,shadow,Opengl,Glsl,Shader,Shadow,我正在使用全向点光源。我已经使用一个立方体贴图纹理作为6个帧缓冲区的颜色附加实现了阴影映射,并对每个像素中的光到片段的距离进行了编码 现在,如果可能的话,我想以这种方式更改我的实现: 1) 将深度立方体贴图纹理附加到帧缓冲区的深度缓冲区,而不是颜色 2) 仅渲染深度,请勿在此过程中写入颜色 3) 在主过程中,从立方体贴图纹理读取深度,将其转换为距离,并检查当前片段是否被灯光遮挡 我的问题出现在将立方体贴图的深度值转换回距离时。我使用光片段向量(在世界空间中)来获取立方体贴图中的深度值。此时,

我正在使用全向点光源。我已经使用一个立方体贴图纹理作为6个帧缓冲区的颜色附加实现了阴影映射,并对每个像素中的光到片段的距离进行了编码

现在,如果可能的话,我想以这种方式更改我的实现:

  • 1) 将深度立方体贴图纹理附加到帧缓冲区的深度缓冲区,而不是颜色
  • 2) 仅渲染深度,请勿在此过程中写入颜色
  • 3) 在主过程中,从立方体贴图纹理读取深度,将其转换为距离,并检查当前片段是否被灯光遮挡
我的问题出现在将立方体贴图的深度值转换回距离时。我使用光片段向量(在世界空间中)来获取立方体贴图中的深度值。此时,我不知道这六个面中的哪一个正在使用,也不知道哪个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);

DepthValueToDistance函数是我的实际问题。

因此,解决方案是将灯光到碎片向量转换为深度值,而不是将从立方体贴图读取的深度转换为距离

以下是修改后的着色器代码:

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;
}
关于
vectordepthvalue(vec3-Vec)
的解释:

LocalZComp
对应于给定的
Vec
的Z分量进入立方体映射的匹配平截头体。它实际上是
Vec
的最大组件(例如,如果Vec.y是最大组件,我们将查看立方体贴图的y+或y-面)

如果你看这个,你会理解后面的数学(为了理解,我把它保存在一个正式的形式中),它只是将
LocalZComp
转换成一个标准化的Z值(在[-1..1]之间),然后将它映射到[0..1],这是深度缓冲区值的实际范围。(假设你没有改变它)
n
f
是用于生成立方体贴图的平截头体的近值和远值


计算阴影因子
然后将立方体贴图中的深度值与从碎片到光向量(此处命名为
垂直光向量
计算的深度值进行比较,同时添加一个小的深度偏差(在问题中缺失),如果片段未被灯光遮挡,则返回1。

我想添加有关派生的更多详细信息

V为光到碎片方向向量

正如Benlitz已经说过的,可以通过取V分量绝对值的最大值来计算相应立方体侧视锥体/“眼睛空间”中的Z

Z = max(abs(V.x),abs(V.y),abs(V.z))
然后,准确地说,我们应该否定Z,因为在OpenGL中,负Z轴指向屏幕/视图截锥体

现在我们想得到-Z的深度缓冲区“兼容”值

查看OpenGL透视矩阵

(备份链接)

…我们看到,对于与该矩阵相乘的任何齐次向量,得到的z值完全独立于向量的x和y分量

Z = max(abs(V.x),abs(V.y),abs(V.z))
因此,我们可以简单地将这个矩阵与齐次向量(0,0,-Z,1)相乘,得到向量(分量):

然后我们需要做透视图除法,所以我们用w(z)来除法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(V.x)、abs(V.y)、abs(V.z))-部分仅在V是世界空间方向向量时才起作用


“我的问题有解决方案吗?”将距离转换为深度,并与立方体地图中存储的深度进行比较。“不适用于立方体贴图阴影”我想5..7年前,一个叫“ron frazier”的人写了一篇关于使用立方体贴图进行阴影贴图的文章。@SigTerm是我想你说的。@JohnRiselvato:是的。我想那是一篇文章。显然可以用现代OpenGL做一些类似的事情/更高的精度(有浮点纹理、GLSL、
*阴影
采样器等)-我只是附近没有工作片段,也不想写一个。虽然这个页面很旧,它给出了为什么我应该进行距离到深度转换而不是反向转换的原因:“不幸的是,深度缓冲区使用摄影机空间z距离作为深度值,而不是笛卡尔距离”。从一个世界空间向量到一个立方体映射面中找到匹配的“摄影机z距离”很容易。我现在设法让我的影子工作了。当我清理了一点代码后,我会清理问题并发布答案。谢谢你的链接
z' = (f+n) / (f-n) - 2*f*n / (Z* (f-n))
z_depth_buffer_compatible = (z' + 1.0) * 0.5