我在网上搜索过,但找不到合适的说明。我想在 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),一切正常,变得不好:阴影再次“愚蠢”。
我完全迷失在这里。我不知道我的算法失败了。可能是,为了在纹理上渲染,我应该使用左手规则视图(我的意思是在为光生成视图矩阵时)......?
无论如何,我继续工作,但也许我不理解立方体贴图。 :(
(抱歉长时间编辑)
答案 0 :(得分:1)
假设使用光到顶点矢量确实应该是用作纹理坐标的正确矢量。
您还可以将线性深度存储到深度纹理中,方法是将顶点的距离写入光源(除以某个已知的最大光影响距离,将其转换为[0,1])在前6次传球中进入gl_FragDepth
,而不是顶点的投射深度。这样,您可以在深度比较中使用顶点到灯光的距离,而无需将任何内容投影到光照空间中。这样,您就不需要跟踪6个不同的矩阵,并选择正确的矩阵用于每个顶点或片段。
编辑:您似乎无法在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);