US3S6L4——让物体接收阴影
US3S6L4——让物体接收阴影
本章代码关键字
1 | SHADOW_COORDS // 阴影坐标宏,主要用于存储阴影纹理坐标 |
让物体接收阴影的思路
目前我们已经能够让物体投射阴影了,所谓的投射阴影,
其实就是让物体参与到光源的阴影映射纹理计算中,最终才能影响其他物体在接收阴影时的采样结果
由此可见让物体接收阴影的主要思路,其实就是要从阴影隐射纹理中进行采样,然后将采样结果用于最终的颜色计算中
总体的流程就是:
- 在顶点着色器中进行顶点坐标转换(将顶点坐标 转换为 阴影映射纹理坐标)
- 在片元着色器中使用阴影映射纹理坐标在阴影映射纹理中进行采样,通过得到的深度值判断片元(像素)是否在阴影中,以计算出阴影衰减值
- 将采样结果参与到最终的颜色计算中
实现物体接收阴影效果
-
创建一个新的 Shader,复用 让物体投射阴影的Shader 到新 Shader 中,在此基础上实现接受阴影的效果
这里使用
FallBack "Specular"
来实现投射阴影的效果 -
接受阴影的三剑客(三个宏)
这里只修改 Bass Pass 中的代码,来感受下接受阴影的流程
首先我们需要在 Bass Pass 当中引用包含内置文件#include "AutoLight.cginc"
该内置文件中,有用于计算阴影时需要使用的三剑客1
2
3
4
5
6
7
8
9
10
11CGPROGRAM
// 帮助我们编译所有光照变体,并确保光照衰减相关的变量能够正确复制到对应的变量中
// ...
ENDCG-
SHADOW_COORDS
(阴影坐标宏)该宏在
v2f
结构体(顶点着色器返回值)中使用,本质上就是声明了一个用于对阴影纹理进行采样的坐标
在内部实际上就是声明了一个名为_ShadowCoord
的阴影纹理坐标变量,需要注意的是:
在使用时SHADOW_COORDS(2)
传入参数2
,表示需要时下一个可用的插值寄存器的索引值使用此宏时,句末不需要加
;
分号1
2
3
4
5
6
7struct v2f
{
float4 pos: SV_POSITION; //裁剪空间下的顶点坐标
float3 wNormal: NORMAL; //世界空间下的法线
float3 wPos: TEXCOORD0; //世界空间下的顶点坐标
SHADOW_COORDS(2) //阴影坐标宏,主要用于存储阴影纹理坐标
}; -
TRANSFER_SHADOW
(转移阴影宏)该宏在顶点着色器函数中调用,传入对应的
v2f
结构体对象,该宏会在内部自己判断应该使用哪种阴影映射技术(SM、SSSM)
最终的目的就是将顶点进行坐标转换并存储到_ShadowCoord
阴影纹理坐标变量中使用此宏时,句末不需要加
;
分号1
2
3
4
5
6
7
8
9
10
11
12// appdata_base 包含的顶点成员必须命名为 vertex
// v2f 包含的顶点位置成员必须命名为 pos
v2f vert (appdata_base v)
{
v2f v2fData;
v2fData.pos = UnityObjectToClipPos(v.vertex); //顶点转换到裁剪空间
v2fData.wNormal = UnityObjectToWorldNormal(v.normal); //法线转换到世界空间
v2fData.wPos = mul(unity_ObjectToWorld, v.vertex).xyz; //顶点转换到世界空间
TRANSFER_SHADOW(v2fData) //计算阴影映射纹理坐标,它会在内部去进行计算,并存储到v2f结构体的SHADOW_COORDS(2)内部
return v2fData;
}需要注意的是:
-
该宏会在内部使用顶点着色器中传入的结构体,该结构体中顶点的命名必须是
vertex
上面的示例代码中参数 appdata_base 内的顶点就必须命名为
vertex
-
该宏会在内部使用顶点着色器的返回结构体,其中的顶点位置命名必须是
pos
上面的示例代码中
v2f
结构体内的顶点位置就必须命名为pos
-
-
SHADOW_ATTENUATION
(阴影衰减宏)该宏在片元着色器中调用,传入对应的
v2f
结构体对象,
该宏会在内部利用v2f
中的 阴影纹理坐标变量(ShadowCoord
)对相关纹理进行采样
将采样得到的深度值进行比较,以计算出一个fixed3
的阴影衰减值,
我们只需要使用它返回的结果和(漫反射+高光反射)的结果相乘即可使用此宏时,句末必须加
;
分号1
2
3
4
5
6
7
8
9
10
11
12fixed4 frag (v2f i) : SV_Target
{
//计算Blinn-Phong式光照模型需要的各种颜色
fixed3 lambertColor = getFragLambertColor(i.wNormal); //计算漫反射
fixed3 specularColor = getFragSpecularColor(i.wPos, i.wNormal); //计算高光反射颜色
fixed3 shadow = SHADOW_ATTENUATION(i); //得到阴影衰减值
fixed atten = 1; //衰减值,由于Bass Pass只处理平行光,因此衰减值默认为1
//计算Blinn-Phong式光照模型颜色,衰减值 需要乘以 漫反射颜色 和 高光反射颜色 的和,还要再乘以阴影衰减值
fixed3 blinnPhongColor = UNITY_LIGHTMODEL_AMBIENT.rgb + (lambertColor + specularColor) * atten * shadow;
return fixed4(blinnPhongColor.rgb, 1); //因为传递过来的颜色变量不包括透明度,因此这里需要手动指定透明度
}
-
-
注意
目前处理的方式只是大致了解接受阴影的流程,我们还没有对 Additional Pass 附加渲染通道进行处理
后续再统一处理光照衰减和阴影,得到最终效果
显示效果(左为使用 Unity 默认 Shader,中为使用未实现接收阴影的 Shader,右为使用实现接收阴影的 Shader):
可见,按照按照上述步骤实现了接收阴影逻辑后,物体就可以受到阴影的影响
完整 Shader 代码如下:
1 | Shader "TeachShader/Lesson67_ForwardLighting" |