不透明物体的阴影
一、物体没有阴影的原因
1.不投射阴影
该物体材质的 Shader 中没有 LightMode 为 ShaderCaster 的 Pass,无法进行阴影映射纹理的计算
2.不接收阴影的原因
该物体材质的 Shader 中没有对阴影映射相关纹理采样,没有进行阴影相关颜色的计算
二、物体投射阴影
1.主要思路
Unity 会寻找 LightMode 为 ShaderCaster 的 Pass 来进行处理,如果该 Shader 没有该 Pass,会在它 FallBack 指定的 Shader 中寻找,直到找到为止 注意: 由于投射阴影相关的代码较为通用 因此建议大家不用自己去实现相关 Shader 代码 直接通过 FallBack "Specular" 调用 Unity 中默认 Shader 中的相关代码即可
2.三个关键宏
这三个宏存储于#include "UnityCG.cginc"中
- V2F_ShADOW_CASTER 顶点到片元着色器阴影投射结构体数据宏 这个宏定义了一些标准的成员变量 这些变量用于在阴影投射路径中传递顶点数据到片元着色器 主要在结构体中使用
- TRANSFAR_SHADOW_CASTER_NORMALOFFSET 转移阴影投射器法线偏移宏 用于在顶点着色器中计算和传递阴影投射所需的变量 主要做了:
- 将对象空间的顶点位置转换到裁剪空间中
- 考虑法线偏移,以减轻阴影失真问题,尤其指在处理子阴影时
- 传递顶点的投影空间位置,用于后续的阴影计算 主要在顶点着色器中使用
- SHADOW_CASTER_FRAGMENT 阴影投射片元宏 将深度值写入到阴影映射纹理中 主要在片元着色器中使用
3.示例代码
注意:
- 这里只提供对应的 Pass,其他 Pass 可以自行添加到 SubShader 中使用
- 需要使用编译指令
#pragma multi_compile_shadowcaster
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 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 data; TRANSFER_SHADOW_CASTER_NORMALOFFSET(data); return data; } float4 frag(v2f i):SV_TARGET { SHADOW_CASTER_FRAGMENT(i); } ENDCG }
|
三、物体接收阴影
1.主要思路
- 在顶点着色器中进行顶点坐标转换(将顶点坐标 转换为 阴影映射纹理坐标)
- 在片元着色器中使用阴影映射纹理坐标在阴影映射纹理中进行采样 通过得到的深度值判断片元(像素)是否在阴影中,以计算出阴影衰减值
- 将采样结果参与到最终的颜色计算中
2.三个关键宏
1.)Base Pass 中
引用内置文件#include "AutoLight.cginc" 该内置文件中,有用于计算时所需要的三剑客
需要使用编译指令#pragma multi_compile_fwdbase
SHADOW_COORDS————阴影坐标宏 该宏在 v2f 结构体中(即:顶点着色器返回值)中使用 本质上是声明了一个用于对阴影纹理进行采样的坐标 内部实际上是声明了一个名为_ShadowCoord 的阴影纹理 注意点: 使用时 SHADOW_COORDS(2) 传入参数 2 表示需要时下一个可用的插值寄存的索引值
TRANSFER_SHADOW 该宏在顶点着色器中使用,传入对应的 v2f 结构体 该宏会在内部自己判断该使用的 SM、还是 SMM 阴影映射技术 最终目的就是将顶点进行坐标转化并存储到_ShadowCoord 阴影纹理坐标变量中 注意点:
- 该宏所使用的 顶点着色器中传入的结构体 该结构体中顶点的命名必须是 vertex
- 该宏所的 v2f 结构体 该结构体中顶点位置的命名必须是 pos
SHADOW_ATTENUATION 该宏在片元着色器中调用,传入对应的 v2f 结构体对象 该宏会在内部利用 v2f 中的 阴影纹理坐标变量(ShadowCoord)对相关纹理进行采样 将采样得到的深度值进行比较,以计算出一个 fixed3 的阴影衰减值 我们只需要使用它返回的结果和 (漫反射+高光反射) 的结果相乘即可
3.实例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| Pass { Tags {"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct v2f { float4 pos:SV_POSITION; float3 normal:NORMAL; float3 wPos:TEXCOORD0; SHADOW_COORDS(2) }; v2f vert (appdata_base v) { v2f data; data.pos = UnityObjectToClipPos(v.vertex); data.normal = UnityObjectToWorldNormal(v.normal); data.wPos = mul(UNITY_MATRIX_M,v.vertex).xyz; TRANSFER_SHADOW(data); return data; }
float3 _MainColor; fixed3 GetLambertFColor(in float3 normal) { float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 color = _MainColor.rgb * _LightColor0.rgb * max(0,dot(lightDir,normal)); return color; }
float3 _SpecularColor; float _SpecularNum; fixed3 GetBlinnSpecularColor(in float3 wPos,in float3 normal) { float3 viewDir = normalize(_WorldSpaceCameraPos.xyz-wPos);
float3 lightDir = normalize(_WorldSpaceLightPos0).xyz;
float3 halfA = normalize(viewDir + lightDir);
fixed3 color = _LightColor0.rgb * _SpecularColor.rgb *pow(max(0,dot(halfA,normal)),_SpecularNum); return color; } fixed4 frag (v2f i) : SV_Target { fixed3 lambertColor = GetLambertFColor(i.normal); fixed3 SpecularColor = GetBlinnSpecularColor(i.wPos,i.normal);
fixed3 shadowAtten = SHADOW_ATTENUATION(i);
fixed attenuation = 1; fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + (lambertColor + SpecularColor)* attenuation* shadowAtten; return fixed4(color.rgb , 1); } ENDCG }
|
四、光照衰减和阴影的综合实现
1.主要原因
光照值衰减和接受阴影相关计算是类似的 都是通过计算出一个衰减值,参与到颜色计算 都是用(漫反射+高光反射)的结果乘以对应的衰减值
所以 Unity 中专门处理这两个的宏 来专门处理光照衰减和阴影衰减的计算
2.实现思路
1.)Base Pass 中
引用内置文件#include "AutoLight.cginc" 该内置文件中,有用于计算时所需要的三剑客
需要使用编译指令#pragma multi_compile_fwdbase
SHADOW_COORDS————阴影坐标宏 该宏在 v2f 结构体中(即:顶点着色器返回值)中使用 本质上是声明了一个用于对阴影纹理进行采样的坐标 内部实际上是声明了一个名为_ShadowCoord 的阴影纹理 注意点: 使用时 SHADOW_COORDS(2) 传入参数 2 表示需要时下一个可用的插值寄存的索引值
TRANSFER_SHADOW 该宏在顶点着色器中使用,传入对应的 v2f 结构体 该宏会在内部自己判断该使用的 SM、还是 SMM 阴影映射技术 最终目的就是将顶点进行坐标转化并存储到_ShadowCoord 阴影纹理坐标变量中 注意点:
- 该宏所使用的 顶点着色器中传入的结构体 该结构体中顶点的命名必须是 vertex
- 该宏所的 v2f 结构体 该结构体中顶点位置的命名必须是 pos
UNITY_LIGHT_ATTENUATION 该宏在顶点着色器中使用,需要传入三个参数, 参数一:最终计算出的衰减值的变量名 参数二:片元着色器中的 v2f 的结构体对象 参数三:顶点相对世界坐标系的位置
2.)Addtional Pass 中
引用内置文件#include "AutoLight.cginc" 该内置文件中,有用于计算时所需要的三剑客
需要使用编译指令#pragma multi_compile_fwdbase_fullshadows
SHADOW_COORDS————阴影坐标宏 该宏在 v2f 结构体中(即:顶点着色器返回值)中使用 本质上是声明了一个用于对阴影纹理进行采样的坐标 内部实际上是声明了一个名为_ShadowCoord 的阴影纹理 注意点: 使用时 SHADOW_COORDS(2) 传入参数 2 表示需要时下一个可用的插值寄存的索引值
TRANSFER_SHADOW 该宏在顶点着色器中使用,传入对应的 v2f 结构体 该宏会在内部自己判断该使用的 SM、还是 SMM 阴影映射技术 最终目的就是将顶点进行坐标转化并存储到_ShadowCoord 阴影纹理坐标变量中 注意点:
- 该宏所使用的 顶点着色器中传入的结构体 该结构体中顶点的命名必须是 vertex
- 该宏所的 v2f 结构体 该结构体中顶点位置的命名必须是 pos
UNITY_LIGHT_ATTENUATION 该宏在顶点着色器中使用,需要传入三个参数, 参数一:最终计算出的衰减值的变量名 参数二:片元着色器中的 v2f 的结构体对象 参数三:顶点相对世界坐标系的位置
3.实例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
| Shader "Unlit/ShadowAttenuation" { Properties { _MainColor("MainColor",Color) = (1,1,1,1) _SpecularColor("SpecularColor",Color) = (1,1,1,1) _SpecularNum("SpecularNum",Range(0,20)) = 0.5 } SubShader { Pass { Tags {"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct v2f { float4 pos:SV_POSITION; float3 normal:NORMAL; float3 wPos:TEXCOORD0; SHADOW_COORDS(2) }; v2f vert (appdata_base v) { v2f data; data.pos = UnityObjectToClipPos(v.vertex); data.normal = UnityObjectToWorldNormal(v.normal); data.wPos = mul(UNITY_MATRIX_M,v.vertex).xyz; TRANSFER_SHADOW(data); return data; }
float3 _MainColor; fixed3 GetLambertFColor(in float3 normal) { float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 color = _MainColor.rgb * _LightColor0.rgb * max(0,dot(lightDir,normal)); return color; }
float3 _SpecularColor; float _SpecularNum; fixed3 GetBlinnSpecularColor(in float3 wPos,in float3 normal) { float3 viewDir = normalize(_WorldSpaceCameraPos.xyz-wPos);
float3 lightDir = normalize(_WorldSpaceLightPos0).xyz;
float3 halfA = normalize(viewDir + lightDir);
fixed3 color = _LightColor0.rgb * _SpecularColor.rgb *pow(max(0,dot(halfA,normal)),_SpecularNum); return color; } fixed4 frag (v2f i) : SV_Target { fixed3 lambertColor = GetLambertFColor(i.normal); fixed3 SpecularColor = GetBlinnSpecularColor(i.wPos,i.normal);
UNITY_LIGHT_ATTENUATION(attenuation,i,i.wPos)
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + (lambertColor + SpecularColor)* attenuation; return fixed4(color.rgb , 1); } ENDCG } Pass { Tags {"LightMode"="ForwardAdd"} Blend One One CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdadd_fullshadows
#include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct v2f { float4 pos:SV_POSITION; float3 normal:NORMAL; float3 wPos:TEXCOORD0; SHADOW_COORDS(2) }; float3 _MainColor; float3 _SpecularColor; float _SpecularNum; v2f vert (appdata_base v) { v2f data; data.pos = UnityObjectToClipPos(v.vertex); data.normal = UnityObjectToWorldNormal(v.normal); data.wPos = mul(UNITY_MATRIX_M,v.vertex).xyz; TRANSFER_SHADOW(data); return data; }
fixed4 frag (v2f i) : SV_Target { fixed3 worldNormal = normalize(i.normal); #if defined(_DIRECTIONAL_LIGHT) fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); #else fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.wPos.xyz); #endif fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb * max(0,dot(worldNormal,worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.wPos.xyz); fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0,dot(worldNormal,halfDir)),_SpecularNum); UNITY_LIGHT_ATTENUATION(attenuation,i,i.wPos)
return fixed4((diffuse + specular) * attenuation, 1); } ENDCG }
}
Fallback "Specular" }
|