立方体阴影映射算法如何工作?

时间:2011-10-15 16:05:46

标签: opengl-es 3d opengl-es-2.0 shadow

我在网上搜索过,但找不到合适的说明。我想在 OpenGL ES 2.0 中执行以下操作:

在一个简单的场景中,点光源会四处移动,我想渲染穿孔阴影。为此,我必须使用立方体阴影贴图。

我理解基本算法,即:

1。)从灯光的POV渲染6倍于场景。将深度值存储在立方体贴图的相应面中。 (如果光看+ X然后立方体面+ X,如果看-X然后立方体面-X等等)

2.。)从相机的POV渲染场景,并使用立方体贴图中存储的深度值进行比较:

如果深度< 与光的距离,然后片段处于阴影中......

我有一些问题,也有一些想法。 我想对我的想法进行一些确认或更正。

我的问题:

如何从立方体贴图中获取?我知道我必须使用vec3,但是如何计算这个fetcher向量?我只有一个想法:使用 vertexPosition - lightPosition 向量,但我不确定这是否合适。 :(

另一个问题如下: 与光的距离:它是世界坐标,所以它只是一个浮动值...... 深度值在[0.0,1.0]范围内......

如何在[0.0,1.0]范围内创建距离? 我的想法是我将所有6个光的视图矩阵和光的投影矩阵也传递给顶点着色器。我计算顶点的位置2次:一个用于摄像机的MVP(正常),一个用于光的MVP(用于阴影计算,使用适当的视图矩阵)。通过这种方式,我将再次获得片段在轻POV中的位置,并且由于w分割,它的z值可以用作偏差后距离光[0.0,1.0]的距离,因此我可以将其与从中获取的深度值进行比较我的立方体阴影贴图......我是对的吗?

请帮帮我。提前谢谢。

修改

好吧,影子立方体贴图开始起作用。但是,我目前正在尝试解决一些错误。

起初,阴影是“愚蠢的”。不是他们应该去过的地方,或者是完全“愚蠢”的形状阴影......

设置为:摄像头(0,4.9,0),(0,0,0),(0,1,0)。 光的观点:

+ X:LookAt(eyeX,eyeY,eyeZ,eyeX + 1,eyeY,eyeZ,0,1,0,矩阵); //自己的实现与gluLookAt

相同

-X:LookAt(eyeX,eyeY,eyeZ,eyeX -1,eyeY,eyeZ,0,1,0,矩阵);

+ Y:LookAt(eyeX,eyeY,eyeZ,eyeX,eyeY +1,eyeZ,-1,0,0,矩阵);

依旧......

这不起作用。好吧,但是我给它制作了动画,看到假影与lightsource一起移动:lightsource向上移动,阴影也移动了,ls向下移动,影子也是......

(光源位于(0,0,0)并在Y轴上移动,盒子留在灯光下。)

因此,对于测试,我在片段着色器中执行了以下操作:

vec3 fetcher = v_posWorld.xyz - u_light_pos;

fetcher.y * = -1.0;

这个想法的出现是因为我认为问题可能是在渲染纹理时,渲染的图像会被颠倒渲染。这个测试有效,但当然只适用于-X和+ X面......

所以我注释掉了“fetcher.y * = -1.0;”线,并改变了光的意见:

+ X:LookAt(eyeX,eyeY,eyeZ,eyeX +1,eyeY,eyeZ,0,-1,0,矩阵);

-X:LookAt(eyeX,eyeY,eyeZ,eyeX -1,eyeY,eyeZ,0,-1,0,矩阵);

+ Y:LookAt(eyeX,eyeY,eyeZ,eyeX,eyeY + 1,eyeZ,0,0,-1,矩阵);

-Y:LookAt(eyeX,eyeY,eyeZ,eyeX,eyeY-1,eyeZ,0,0,1,矩阵);

+ Z:LookAt(eyeX,eyeY,eyeZ,eyeX,eyeY,eyeZ +1,0,-1,0,矩阵);

-Z:LookAt(eyeX,eyeY,eyeZ,eyeX,eyeY,eyeZ -1,0,-1,0,矩阵);

有效!几乎,因为+ Y和-Y的视图设置不能像我预期的那样工作:(稍微玩了一下之后,我将相机的位置改为(0,-4.9,0),一切正常,变得不好:阴影再次“愚蠢”。

我完全迷失在这里。我不知道我的算法失败了。可能是,为了在纹理上渲染,我应该使用左手规则视图(我的意思是在为光生成视图矩阵时)......?

无论如何,我继续工作,但也许我不理解立方体贴图。 :(

(抱歉长时间编辑)

1 个答案:

答案 0 :(得分:1)

  1. 假设使用光到顶点矢量确实应该是用作纹理坐标的正确矢量。

  2. 您还可以将线性深度存储到深度纹理中,方法是将顶点的距离写入光源(除以某个已知的最大光影响距离,将其转换为[0,1])在前6次传球中进入gl_FragDepth,而不是顶点的投射深度。这样,您可以在深度比较中使用顶点到灯光的距离,而无需将任何内容投影到光照空间中。这样,您就不需要跟踪6个不同的矩阵,并选择正确的矩阵用于每个顶点或片段。

  3. 编辑:您似乎无法在ES中写入gl_FragDepth,这会使您自己的深度变得更复杂一些。只渲染到普通纹理不会这样做,因为每个通道的8位精度实际上太小了。

    但是你应该能够在顶点着色器中线性化深度,只需在顶点的z分量中存储顶点到光的距离(变换为[-1,1])乘以它的w分量,然后将其除以w并通过光栅化器转换为[0,1]以获得片段的深度:

    uniform mat4 lightModelView;
    uniform mat4 lightProjection;
    unfiorm float maxLightDistance;
    
    attribute vec4 vertex;
    
    void main()
    {
        vec4 lightSpaceVertex = lightModelView * vertex;
        float lightDistance = length(lightSpaceVertex.xyz) / maxLightDistance;
    
        gl_Position = lightProjection * lightSpaceVertex;
        gl_Position.z = (2.0*lightDistance-1.0) * gl_Position.w;
    }
    

    可以通过相应地改变光的投影矩阵来优化它,但是这个代码(与简单的直通片段着色器结合)应该将线性光到顶点的距离存储到深度缓冲区中,如果我是这里没有完全错误的轨道。它只是将顶点的z与它的w相乘,因此应该反对透视分割步骤,这将产生非线性深度值(当然它也会对抗从[-1,1]到[0,1]的变换,当然)。

    编辑:根据您的新问题:首先,我希望您的光线位于(eyeX,eyeY,eyeZ),因为生成阴影贴图的相机必须位于当然,光的位置。如果(eyeX,eyeY,eyeZ)实际上是你(普通)场景摄像机的位置,那么这当然是错误的,你应该使用(lightX,lightY,lightZ)。

    接下来,您当然应该使用精确90度的FoV(视野)作为灯光的视图,因此应该以某种方式生成投影矩阵:

    glFrustum(-near, near, -near, near, near, far);
    

    或者这个:

    gluPerspective(90, 1, near, far);