我们目前的着色器在 GPU 上运行的程序。 更多信息
在 词汇表 中查看既不能接收也不能投射阴影。让我们先实现阴影投射。
为了投射阴影,着色器必须在其任何子着色器或任何回退中具有ShadowCaster 通道类型。ShadowCaster 通道用于将物体渲染到阴影贴图中,通常它非常简单 - 顶点着色器在渲染 3D 模型时,在 3D 模型的每个顶点上运行的程序。 更多信息
在 词汇表 中查看只需要评估顶点位置,而片段着色器几乎不做任何事情。阴影贴图只是深度缓冲区存储每个像素的 z 值深度的内存存储器,其中 z 值是每个渲染像素的深度,来自投影平面。 更多信息
在 词汇表 中查看,所以即使片段着色器输出的颜色也不重要。
这意味着对于许多着色器来说,阴影投射通道将几乎完全相同(除非物体具有基于自定义顶点着色器的变形,或具有 alpha 切割/半透明部分)。最简单的拉入方式是通过UsePass 着色器命令
Pass
{
// regular lighting pass
}
// pull in shadow caster from VertexLit built-in shader
UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
但是我们在这里学习,所以让我们“手工”做同样的事情。为了缩短代码,我们用只做无纹理环境的代码替换了灯光通道(“ForwardBase”)。下面,有一个“ShadowCaster”通道,它使物体支持阴影投射。
Shader "Lit/Shadow Casting"
{
SubShader
{
// very simple lighting pass, that only does non-textured ambient
Pass
{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
fixed4 diff : COLOR0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
half3 worldNormal = UnityObjectToWorldNormal(v.normal);
// only evaluate ambient
o.diff.rgb = ShadeSH9(half4(worldNormal,1));
o.diff.a = 1;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return i.diff;
}
ENDCG
}
// shadow caster rendering pass, implemented manually
// using macros from UnityCG.cginc
Pass
{
Tags {"LightMode"="ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
float4 frag(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
}
现在下面有一个平面,使用一个常规的内置漫射着色器,这样我们就可以看到我们的阴影在工作(记住,我们当前的着色器还不支持接收阴影!)。
我们使用了#pragma multi_compile_shadowcaster 指令。这会导致着色器被编译成多个变体,每个变体都为每个变体定义了不同的预处理器宏(有关详细信息,请参阅多个着色器变体页面)。在渲染到阴影贴图时,点光源与其他光源类型的情况需要稍微不同的着色器代码,这就是为什么需要此指令的原因。