目录索引
译文
我们将从生成阴影开始,让我们先创建一个无光照着色器并命名为 USB_shadow_map。在这个过程中,我们需要两个 pass,其中一个用于阴影投射,另一个用于接收它们(阴影贴图)。因此,我们要做的第一件事情就是在着色器中添加第二个 pass 用于投射阴影。
Shader "USB/USB_shadow_map"
{
Properties { ... }
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
// shadow caster Pass
Pass { ... }
// default color pass
Pass { ... }
}
}
颜色 pass 代表每次创建着色器时默认添加的 Pass,而新的 Pass 将用来生成阴影投射。为了区分它们,我们可以将投射 pass 命名为 ShadowCaster:
// shadow caster Pass
Pass
{
Name "Shadow Caster"
Tags
{
"RenderType"="Opaque"
"LightMode"="ShadowCaster"
}
...
}
接着是 Tags 语义块,其中光照模式 LightMode 标签必须设置为 ShadowCaster,这样 Unity 才能识别该 Pass 的性质。
为 Pass 命名可以帮助我们在着色器中动态使用它们,稍后我们将介绍与该概念相关的 UsePass 命令。
值得一提的是,名称(Name)仅在着色器中起到命名的作用,并不影响投影的计算过程。名称的声明可以省略,但这次我们将使用它来区分两个 Pass。
因为 ShadowCaster pass 仅代表阴影投射的部分,我们必须将顶点位置传递到顶点着色器阶段,并在片元着色器阶段返回 [0]。
// shadow caster Pass
Pass {
Name "Shadow Caster"
Tags {
"RenderType"="Opaque"
"LightMode"="ShadowCaster"
}
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
// 在顶点输入结构体中,我们只需要顶点的位置信息
float4 vertex : POSITION;
};
struct v2f
{
// 在顶点输出结构体中,我们只需要顶点的位置信息
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return 0;
}
ENDCG
}
到这里阴影投射就正确工作了,然而该配置还不允许我们调整投影的值,因为我们尚未定义它。
当我们处理阴影时,我们可以确定强度、分辨率、偏差、法线偏差和近平面的值。这些属性可以在光源的配置中找到。
![图片[1]-《Unity着色器圣经》8.0.2 | 阴影投射-软件开发学习笔记](https://gamedevfan.cn/wp-content/uploads/2025/05/image-101-1024x475.jpeg)
在着色器中定义每一个属性将浪费我们大量的时间,不过有别的能节省时间的方法,比如在 UnityCG.cginc 中包含了以下几个宏:
- V2F_SHADOW_CASTER.
- TRANSFER_SHADOW_CASTER_NORMALOFFSET(o).
- SHADOW_CASTER_FRAGMENT(i).
V2F_SHADOW_CASTER 包含了多个用于在顶点位置插值和法线贴图中计算阴影的语义,这意味着该宏有:顶点位置输出(vertex : SV_POSITION)、法线输出(normal_world : TEXCOORD1)、切线输出(tangent_world : TEXCOORD2)和副切线输出(binormal_world : TEXCOORD3)。
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) 负责将顶点位置的坐标空间变换到裁剪空间,还允许我们计算法线偏移,以便将阴影包含在法线贴图中。
最后,SHADOW_CASTER_FRAGMENT 负责阴影投射的颜色输出。要让 Unity 编译这些宏,我们必须确保在程序中包含 UnityCG.cginc 指令,并使用 #pragma multi_compile_shadowcaster 来计算多个变体。
// shadow caster Pass
Pass
{
Name "Shadow Caster"
Tags
{
"RenderType"="Opaque"
"LightMode"="ShadowCaster"
}
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCg.cginc"
CGPROGRAM
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vert (appdata v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
fixed4 frag (v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT (i)
}
ENDCG
}
现在,负责阴影投射的 pass 已经准备就绪,我们可以继续学习如何生成阴影贴图了。为此,我们将使用着色器中默认包含的颜色 pass,并在下一小节中介绍如何实现它。
原文对照
We will start with generating shadows. For this, we will create a new Unlit Shader, which we will call USB_shadow_map. In the process, we need two passes: one to cast shadows (shadow caster) and another to receive them (shadow map), therefore, the first thing we must do is include a second pass, which will be responsible for the projection of the shadows.
Shader "USB/USB_shadow_map"
{
Properties { ... }
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
// shadow caster Pass
Pass { ... }
// default color pass
Pass { ... }
}
}
The color pass corresponds to the default Pass that is included every time we create a shader. The new Pass will be responsible for generating the shadow projection (shadow caster), therefore, the first thing we have to do is declare the projection pass as ShadowCaster.
// shadow caster Pass
Pass
{
Name "Shadow Caster"
Tags
{
"RenderType"="Opaque"
"LightMode"="ShadowCaster"
}
...
}
The Shadow Caster Pass begins with the declaration of its name (Name “Shadow Caster”) and continues with the LightMode Tags, which in this case, must equal ShadowCaster in order for Unity to recognize its nature.
Naming a Pass is a great help when we want to use its functionality dynamically in a shader. Later we will review in detail the UsePass command, which is directly related to this concept.
It is worth mentioning that the Name only fulfills the function of naming in the shader and does not interfere with the process of calculating the projection. The Name declaration can be omitted; however, we will use it this time to differentiate the two passes.
Because the ShadowCaster only corresponds to a shadow projection, we have to pass the vertex position to the vertex shader stage and return “zero” [0] in the fragment shader stage.
// shadow caster Pass
Pass {
Name "Shadow Caster"
Tags {
"RenderType"="Opaque"
"LightMode"="ShadowCaster"
}
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
// we need only the position of vertices as input
float4 vertex : POSITION;
};
struct v2f
{
// we need only the position of vertices as output
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return 0;
}
ENDCG
}
Up to this point, the shadow caster is working correctly, however this configuration will not allow us to adjust the projection values since they have not been defined yet.
When we work with shadows we can determine the values of intensity, resolution, bias, normal bias and near plane. These properties can be found in the lighting configuration.
![图片[1]-《Unity着色器圣经》8.0.2 | 阴影投射-软件开发学习笔记](https://gamedevfan.cn/wp-content/uploads/2025/05/image-101-1024x475.jpeg)
Defining each property in our shader could take a long time, to avoid this we can work with the following macros that are included in UnityCG.cginc:
- V2F_SHADOW_CASTER.
- TRANSFER_SHADOW_CASTER_NORMALOFFSET(o).
- SHADOW_CASTER_FRAGMENT(i).
V2F_SHADOW_CASTER contains several semantics for shadow calculation both in the interpolated vertices position and in the normal maps, this means that this macro has: a vertices position output (vertex : SV_POSITION), a normals output (normal_world : TEXCOORD1), a tangents output (tangent_world : TEXCOORD2) and a binormals output (binormal_world : TEXCOORD3).
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) is responsible for transforming the coordinates of the vertices position to clip-space and also allows us to calculate the offset normal, so we can include shadows in the normal maps.
Finally, SHADOW_CASTER_FRAGMENT is in charge of color output for shadow projection. For Unity to compile these macros, we must make sure that we include the UnityCG.cginc directive and also use the #pragma multi_compile_shadowcaster to calculate multiple variants.
// shadow caster Pass
Pass
{
Name "Shadow Caster"
Tags
{
"RenderType"="Opaque"
"LightMode"="ShadowCaster"
}
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCg.cginc"
CGPROGRAM
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vert (appdata v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
fixed4 frag (v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT (i)
}
ENDCG
}
Now the shadow caster pass is ready, and we can work on the included pass to generate a shadow map. To do this, we will take the default color pass included in the shader and begin the implementation.
暂无评论内容