对于Spot Light,先根据投射的方向进行投影可以得到这个Spot Light的光晕。
比如:
光晕图:
那么对于他的光照可以基于光晕使用Per-Pixel Light技术。
对于空间中的物体,使用Spot Light的Project Matrix进行投影,投影过后就得到了这个物体在光晕中坐标。这样这个物体在该Spot Light中的光照就确定了(物体和光源在同一个View Plane上,物体的投影和光晕投影的交集确定了这个物体上的Spot Light光照)。
Project Matrix = Word(Obj)*View(Light)*Proj(Light)*MatToTexcoord
Word(Obj) : 这个物体的World Matrix,将物体变到世界坐标系中
View(Light) : Spot Light的View Matrix,Spot Light类似于Eye Position,Spot Light的方向类似于View Direction
Proj(Light) : Spot Light的Perspective Matrix,将Spot Light的"观察视域"投影为一个平面
MatToTexcoord : 变换得到光晕Texture上的坐标。该矩阵为
D3DMATRIX matToTexCoord =
{
0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f
};
因为在Porj过后得到的Clip Space坐标(Xp, Yp, Zp, Wp):
Xp/Wp = (-1, 1)
Yp/Wp = (1, -1)
而Texture中的坐标为:u = (0, 1), v = (0,1)
所以
(Xp/Wp)*0.5+0.5 = (0, 1)
(Yp/Wp)*(-0.5)+0.5 = (0,1)
这样可得到对应的Texture上的坐标
效果如图所示:
问题:
1:对于Spot Light的背后因该是没有光照的,但是上面的过程仅仅使用了x和y,而x,y在Spot Light的两侧经过上面的变换时对称的,所以前后都有光照。
2:没有光照的衰减
解决问题1:
在DX中Projection Matrix为:
也就是经过投影变换过后,得到的Zp = Zf*(Zv-Zn)/(Zf-Zn), Wp = Zv
Zp/Wp = Zf*(Zv-Zn)/[(Zf-Zn)*Zv]
Zf: The z value of the far plane
Zn: The z value of the near plane
Zv: The z value of the pixel in the view space
Zn and Zf are specified while constructing the project matrix
对于Zp/Wp值的讨论:
Zn<=Zv<=Zf ---->Zp/Wp = [0, 1]
Zv>Zf ---->Zp/Wp > 1
0Zp/Wp < 0
Zv<0 ---->Zp/Wp > 1
那么也就是说:在Spot Light 到 Far Plane的Zp/Wp <= 1,这个空间也是这个Spot Light会产生光照的地方。
而对于Zv<0 (表示在Light之后的区域)和Zv>Zf(表示在Light不能照射的区域)Zp/Wp都是>1的,因此在Pixel Shader中添加这个判断,如果>1,则光照值为0,可去除太远和背后不应该产生的光源。
解决问题2:
使用Clamp(1-Zp/Wp)作为光照衰减值。这里Clamp会将>1的值设定为1,<0的值设定为0;而对于在Zn之后的Zp/Wp为负值,那么Clamp值将一直为1,这可以作为在近端的无衰减区域.另外因为Zp/Wp的值会变化的很快,可以使用Clamp(Weigth*(1-Zp/Wp))为光照的衰减值,这里Weight为一个权值,
效果:
PS:
1:可以在构造View和Project 矩阵时使用参数来控制Spot Light的光源性质,比如张角,方向,光源有效距离。
2:光晕图的周围必须为0,对于采样的时候使用Clamp状态采样,用来将光照周围的值都设定为0.
Code:
Vertex Shader
- vs_3_0
-
-
; Input
-
dcl_position v0
-
dcl_color0 v1
-
-
; Output
-
dcl_position o0
-
dcl_color0 o1
-
dcl_texcoord0 o2
-
-
; c0~c3 geometry matrix
-
; c4~c7 light project matrix
-
-
m4x4 o0, v0, c0
-
mov o1, v1
-
m4x4 o2, v0, c4
Pixel Shader
- ps_3_0
-
-
; input
-
dcl_color0 v0
-
dcl_texcoord0 v1
-
dcl_2d s0
-
-
def c0, 0.2, 1.0, 30.0, 0.0
-
; Compute the texcoord
-
rcp r0, v1.w
-
mul r0, r0, v1
-
; if Zp/Wp >= 1, p0 will be true
-
setp_ge p0, r0.z, c0.y
-
-
; Sample the project light texture
-
texld r1, r0, s0
-
-
; if the Zp/Wp >=1, set r1 0
-
(p0.x)mov r1, c0.z
-
-
; Compute the attenuation
-
sub_sat r0, c0.y, r0.z
-
mul_sat r0, r0, c0.z
-
-
mul r1, r1, r0
-
; Add the ambient
-
add r0, r1, c0.x
-
mul oC0, r0, v0
阅读(872) | 评论(0) | 转发(0) |