原文链接
版权声明:本文为CSDN博主「Unity小林」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
正文
光照衰减
采样内置纹理得到光照衰减
Unity 在内部使用一张名为 _LightTexture0 的纹理来计算光源衰减。我们通常只关心 _LightTexture0 对角线上的纹理颜色值,这些值表明了在 光源空间 中不同位置的点的衰减值。
例如
(0,0)点表明了与 光源位置重合 的点的衰减值,而(1,1)点表明了在 光源空间 中所关心的 距离最远 的点的衰减。
float3 lightCoord = mul(_LightMatrix0,float4(i.worldPos,1)).xyz;
fixed atten = tex2D(_LightTexture0,dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
现将世界坐标与 _LightMatrix0 相乘得到在 光源空间 中的位置,用 光源空间 中顶点距离的平方来对纹理采样,然后,使用宏 UINITY_ATTEN_CHANNEL 来得到衰减纹理中的衰减值所在的分量,以得到最终的衰减值。
数学公式计算光照衰减
float distance = length(_WorldSpaceLightPos0.xyz - i.worldPosition.xyz);
atten = 1.0/distance;
Unity 中的阴影贴图
Shadow Map
它会首先把摄像机位置放在与 光源重合 的位置上,那么场景中该光源的阴影区域就是摄像机看不到的地方。
Screenspace Shadow Map
Unity 首先会通过调用 LightMode 为 ShadowCaster 的 Pass 来得到(光源)投射阴影 是 光源 的 ShadowMap 纹理以及 摄像机 的 Depth 纹理。然后,根据 光源 的 ShadowMap 纹理和 摄像机 的 Depth 纹理来得到 屏幕空间 的 阴影图。如果 摄像机 的 Depth 纹理中记录的表面深度 大于 其 转换 到 光源 的 ShadowMap 纹理中的深度值,就说明该表面 虽然是可见的,但是却处于该光源的阴影中。通过这样的方式,阴影图 就包含了 屏幕空间 中所有阴影的区域。如果我们想要一个物体 接收来自其他物体 的阴影,只需要在 Shader 中对该 阴影图 进行 采样。
接收阴影+投射阴影
一个物体接收来自其他物体的阴影,以及它向其他物体投射阴影是两个过程
- 如果我们想要 一个物体接收来自其他物体的阴影,就必须在 Shader 中对
ShadowMap纹理(包括屏幕空间的 阴影图)进行 采样,把采样结果和最后的光照结果 相乘 来产生阴影效果。 - 如果我们想要 一个物体向其他物体投射阴影,就必须把该物体 加入 到 光源 的
ShadowMap纹理的计算中,从而让其他物体在对ShadowMap纹理采样时可以得到该物体的相关信息。在 Unity 中,这个过程通过为该物体执行 LightMode 为ShadowCaster的 Pass 来实现的。如果使用了 屏幕空间的投射映射技术,Unity 还会使用这个 Pass 产生一张 摄像机的深度纹理。
- 借用一张 URP 的图
实例
接收两层阴影
效果图
主要代码在 第一个 Pass 中
代码如下
Shader "MyShader/shadow"
{
Properties
{
_Diffuse("diffuse",Color) = (1,1,1,1)
_Specular("specular",Color) = (1,1,1,1)
_Gloss("gloss",Range(1,256)) = 20
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass//ForwardBase
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct appdata
{
float4 vertex : POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float3 vertexLight : TEXCOORD2;
SHADOW_COORDS(3)//仅仅是阴影
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
#ifdef LIGHTMAP_OFF
float3 shLight = ShadeSH9(float4(v.normal, 1.0));
o.vertexLight = shLight;
#ifdef VERTEXLIGHT_ON
float3 vertexLight = Shade4PointLights(
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, o.worldPos, o.worldNormal
);
o.vertexLight += vertexLight;
#endif
#endif
TRANSFER_SHADOW(o);//仅仅是阴影
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLightDir));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(worldNormal, halfDir)), _Gloss);
//fixed shadow = SHADOW_ATTENUATION(i);
//return fixed4(ambient + (diffuse + specular)*shadow + i.vertexLight, 1);
//这个函数计算包含了光照衰减以及阴影,因为ForwardBase逐像素光源一般是方向光,衰减为1,atten在这里实际是阴影值
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(ambient + (diffuse + specular)*atten +i.vertexLight, 1);
}
ENDCG
}
Pass//ForwardAdd
{
Tags{"LightMode"="ForwardAdd"}
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd_fullshadows
#pragma vertex vert
#pragma fragment frag
#include"UnityCG.cginc"
#include"Lighting.cginc"
#include"AutoLight.cginc"
float4 _Diffuse;
float4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
float3 worldPos:TEXCOORD0;
float3 worldNormal:TEXCOORD1;
LIGHTING_COORDS(2, 3)//包含光照衰减以及阴影
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_VERTEX_TO_FRAGMENT(o);//包含光照衰减以及阴影
return o;
}
fixed4 frag(v2f i):SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLightDir));
float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(worldNormal, halfDir)), _Gloss);
//fixed atten = LIGHT_ATTENUATION(i);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);//包含光照衰减以及阴影
return fixed4((diffuse + specular)*atten, 1);
}
ENDCG
}
Pass//产生阴影的通道(物体透明也产生阴影)
{
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_shadowcaster
#pragma multi_compile_instancing // allow instanced shadow pass for most of the shaders
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert(appdata_base v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
float4 frag(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
}
如果没能接收两层阴影,
Editor–>Project Settings–>Player, 在 Other Settings 中找到 Color Space,将Gamma改为Linear就可以了。
半透半不透物体产生阴影
效果图
代码如下
Shader "MyShader/shadow2"
{
Properties
{
_Cutoff("cutoff",Range(0,1))=0.5//控制透明
_MainTex("main texture",2D)="white"{}
_Color("diffuse",Color) = (1,1,1,1)
_Specular("specular",Color) = (1,1,1,1)
_Gloss("gloss",Range(1,256)) = 20
}
SubShader
{
Tags { "RenderType" = "TransparentCutOut" "Queue"="AlphaTest" "IgnoreProjector"="True" }
LOD 100
Pass//ForwardBase
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
fixed4 _Specular;
float _Gloss;
sampler2D _MainTex;
float4 _MainTex_ST;
float _Cutoff;
struct appdata
{
float4 vertex : POSITION;
float3 normal:NORMAL;
float2 uv:TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
SHADOW_COORDS(3)//仅仅是阴影
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
TRANSFER_SHADOW(o);//仅仅是阴影
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
fixed4 texColor = tex2D(_MainTex, i.uv);
clip(texColor.a - _Cutoff);//控制透明
fixed3 diffuse = texColor.rgb*_LightColor0.rgb*_Color.rgb*saturate(dot(worldNormal, worldLightDir));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(worldNormal, halfDir)), _Gloss);
//fixed shadow = SHADOW_ATTENUATION(i);
//return fixed4(ambient + (diffuse + specular)*shadow + i.vertexLight, 1);
//这个函数计算包含了光照衰减以及阴影,因为ForwardBase逐像素光源一般是方向光,衰减为1,atten在这里实际是阴影值
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(ambient + (diffuse + specular)*atten, 1);
}
ENDCG
}
Pass//ForwardAdd
{
Tags{"LightMode" = "ForwardAdd"}
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd_fullshadows
#pragma vertex vert
#pragma fragment frag
#include"UnityCG.cginc"
#include"Lighting.cginc"
#include"AutoLight.cginc"
float4 _Diffuse;
float4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
float3 worldPos:TEXCOORD0;
float3 worldNormal:TEXCOORD1;
LIGHTING_COORDS(2, 3)//包含光照衰减以及阴影
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_VERTEX_TO_FRAGMENT(o);//包含光照衰减以及阴影
return o;
}
fixed4 frag(v2f i) :SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLightDir));
float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(worldNormal, halfDir)), _Gloss);
//fixed atten = LIGHT_ATTENUATION(i);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);//包含光照衰减以及阴影
return fixed4((diffuse + specular)*atten, 1);
}
ENDCG
}
Pass//产生阴影的通道(物体透明了就不会产生阴影)
{
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_shadowcaster
#pragma multi_compile_instancing // allow instanced shadow pass for most of the shaders
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
uniform float4 _MainTex_ST;
v2f vert(appdata_base v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
uniform fixed4 _Color;
float4 frag(v2f i) : SV_Target
{
fixed4 texcol = tex2D(_MainTex, i.uv);
clip(texcol.a*_Color.a - _Cutoff);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
//FallBack "Diffuse"
//FallBack "Transparent/Cutout/VertexLit"
}
透明或者半透明或者不透明物体都能产生和接收阴影
效果图
代码如下
Shader "MyShader/shadow3"
{
Properties
{
_AlphaScale("cutoff",Range(0,1)) = 0.5//透明程度
_MainTex("main texture",2D) = "white"{}
_Color("diffuse",Color) = (1,1,1,1)
_Specular("specular",Color) = (1,1,1,1)
_Gloss("gloss",Range(1,256)) = 20
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "IgnoreProjector" = "True" }
LOD 100
Pass//ForwardBase
{
Tags{"LightMode" = "ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
fixed4 _Specular;
float _Gloss;
sampler2D _MainTex;
float4 _MainTex_ST;
float _AlphaScale;
struct appdata
{
float4 vertex : POSITION;
float3 normal:NORMAL;
float2 uv:TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
SHADOW_COORDS(3)//仅仅是阴影
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
TRANSFER_SHADOW(o);//仅仅是阴影
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 diffuse = texColor.rgb*_LightColor0.rgb*_Color.rgb*saturate(dot(worldNormal, worldLightDir));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(worldNormal, halfDir)), _Gloss);
//fixed shadow = SHADOW_ATTENUATION(i);
//return fixed4(ambient + (diffuse + specular)*shadow + i.vertexLight, 1);
//这个函数计算包含了光照衰减以及阴影,因为ForwardBase逐像素光源一般是方向光,衰减为1,atten在这里实际是阴影值
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(ambient + (diffuse + specular)*atten, texColor.a*_AlphaScale);
}
ENDCG
}
//Pass//ForwardAdd
//{
// Tags{"LightMode" = "ForwardAdd"}
// Blend One One
// CGPROGRAM
// #pragma multi_compile_fwdadd_fullshadows
// #pragma vertex vert
// #pragma fragment frag
// #include"UnityCG.cginc"
// #include"Lighting.cginc"
// #include"AutoLight.cginc"
// float4 _Diffuse;
// float4 _Specular;
// float _Gloss;
// struct a2v
// {
// float4 vertex:POSITION;
// float3 normal:NORMAL;
// };
// struct v2f
// {
// float4 pos:SV_POSITION;
// float3 worldPos:TEXCOORD0;
// float3 worldNormal:TEXCOORD1;
// LIGHTING_COORDS(2, 3)//包含光照衰减以及阴影
// };
// v2f vert(a2v v)
// {
// v2f o;
// o.pos = UnityObjectToClipPos(v.vertex);
// o.worldNormal = UnityObjectToWorldNormal(v.normal);
// o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// TRANSFER_VERTEX_TO_FRAGMENT(o);//包含光照衰减以及阴影
// return o;
// }
// fixed4 frag(v2f i) :SV_Target
// {
// float3 worldNormal = normalize(i.worldNormal);
// float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
// fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLightDir));
// float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
// float3 halfDir = normalize(viewDir + worldLightDir);
// fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(worldNormal, halfDir)), _Gloss);
// //fixed atten = LIGHT_ATTENUATION(i);
// UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);//包含光照衰减以及阴影
// return fixed4((diffuse + specular)*atten, 1);
// }
// ENDCG
//}
}
FallBack "Diffuse"
//FallBack "Transparent/Cutout/VertexLit"
}
把 Render Queue 改为 Transparent 以下的值,比如改成 AlphaTest,半透明物体也能接收阴影了。