在Unity5.x某个版本升级后,_LightMatrix0变为unity_worldToLight。
按官方文档,unity_worldToLight是从世界空间到光源空间的变换矩阵,变换得到的坐标用于采样光强衰减纹理,具体使用方法:
float3 lightCoord = mul(_LightMatrix0, float4(i.worldPos, 1)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
疑问:tex2D函数应该使用uv坐标采样,也就是坐标的范围应该是[0,1],而lightCoord的点积得到的标量是距离的平方,范围明显不是[0,1],这如何采样呢?
《UnityShader入门精要》https://github.com/candycat1992/unity_shaders_book/issues/47 对此的解释:经过unity_worldToLight变换得到的坐标模范围会在[0,1]。下面来看看unity_worldToLight是怎么变换世界坐标的。
-
在场景中增加一个cube,并在cube的shader上增加一个AdditionPass,在该Pass中使用unity_worldToLight
-
在unity场景中放置一个点光源,设置光源的Range属性为5,设置光源的坐标(0,0,0)
-
打开Frame Debug工具
-
设置光源的坐标为(1,2,3),并查看Frame Debug
设光源的坐标为
L
(
L
x
,
L
y
,
L
z
)
L(L_{x},L_{y},L_{z})
L(Lx,Ly,Lz),根据第3第4点,可推导得
u
n
i
t
y
_
w
o
r
l
d
T
o
L
i
g
h
t
unity\_worldToLight
unity_worldToLight 矩阵如下:
[
1
R
a
n
g
e
0
0
−
L
x
R
a
n
g
e
0
1
R
a
n
g
e
0
−
L
y
R
a
n
g
e
0
0
1
R
a
n
g
e
−
L
z
R
a
n
g
e
0
0
0
1
]
\left[ \begin{matrix} \frac{1}{Range} & 0 & 0 & -\frac{L_{x}}{Range} \\ 0 & \frac{1}{Range} & 0 & -\frac{L_{y}}{Range} \\ 0 & 0 & \frac{1}{Range} & -\frac{L_{z}}{Range} \\ 0 & 0 & 0 & 1 \\ \end{matrix} \right]
⎣⎢⎢⎢⎡Range10000Range10000Range10−RangeLx−RangeLy−RangeLz1⎦⎥⎥⎥⎤
接下来看一下这个矩阵变换的本质,设顶点
P
(
x
,
y
,
z
,
1
)
P(x,y,z,1)
P(x,y,z,1)经过变换:
[
1
R
a
n
g
e
0
0
−
L
x
R
a
n
g
e
0
1
R
a
n
g
e
0
−
L
y
R
a
n
g
e
0
0
1
R
a
n
g
e
−
L
z
R
a
n
g
e
0
0
0
1
]
⋅
[
x
y
z
1
]
=
[
x
−
L
x
R
a
n
g
e
y
−
L
y
R
a
n
g
e
z
−
L
z
R
a
n
g
e
1
]
\left[ \begin{matrix} \frac{1}{Range} & 0 & 0 & -\frac{L_{x}}{Range} \\ 0 & \frac{1}{Range} & 0 & -\frac{L_{y}}{Range} \\ 0 & 0 & \frac{1}{Range} & -\frac{L_{z}}{Range} \\ 0 & 0 & 0 & 1 \\ \end{matrix} \right] \cdot \left[\begin{matrix} x \\ y \\ z \\ 1 \end{matrix}\right] = \left[\begin{matrix} \frac{x-L_{x}}{Range} \\ \frac{y-L_{y}}{Range} \\ \frac{z-L_{z}}{Range} \\ 1 \end{matrix} \right]
⎣⎢⎢⎢⎡Range10000Range10000Range10−RangeLx−RangeLy−RangeLz1⎦⎥⎥⎥⎤⋅⎣⎢⎢⎡xyz1⎦⎥⎥⎤=⎣⎢⎢⎢⎡Rangex−LxRangey−LyRangez−Lz1⎦⎥⎥⎥⎤
现在很明显了,该矩阵变换其实就是:
l
i
g
h
t
C
o
o
r
d
=
(
顶
点
的
世
界
坐
标
−
光
源
坐
标
)
/
R
a
n
g
e
lightCoord =(顶点的世界坐标-光源坐标)/Range
lightCoord=(顶点的世界坐标−光源坐标)/Range
∣
l
i
g
h
t
C
o
o
r
d
∣
=
(
x
−
L
x
)
2
+
(
y
−
L
y
)
2
+
(
z
−
L
z
)
2
R
a
n
g
e
2
|lightCoord|=\sqrt{\frac{(x-L_{x})^2+(y-L_{y})^2+(z-L_{z})^2}{Range^2}}
∣lightCoord∣=Range2(x−Lx)2+(y−Ly)2+(z−Lz)2
证实了
l
i
g
h
t
C
o
o
r
d
lightCoord
lightCoord的模范围是[0,1],注意:模大于1的时候意味着超出了光照范围,Unity不会调用Pass计算光照,因此模的范围必定是[0,1]。
还有一点需要注意的,我们使用的是
∣
l
i
g
h
t
C
o
o
r
d
∣
2
|lightCoord|^2
∣lightCoord∣2进行采样,平方意味着_LightTexture0里保存的光照衰减是非线性的。