光照衰减
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中阴影
1,Shadow Map:它会首先把摄像机位置放在与光源重合的位置上,那么场景中该光源的阴影区域就是摄像机看不到的地方。
2,Screenspace Shadow Map:Unity首先会通过调用LightMode 为 ShadowCaster的Pass来得到可投射阴影是光源的阴影映射纹理以及摄像机的深度纹理。然后,根据光源的阴影映射纹理和摄像机的深度纹理来得到屏幕空间的阴影图。如果摄像机的深度图中记录的表面深度大于转换到阴影映射纹理中的深度值,就说明该表面虽然是可见的,但是却出于该光源的阴影中。通过这样的方式,阴影图就包含了屏幕空间中所有阴影的区域。如果我们想要一个物体接收来自其他物体的阴影,只需要在Shader中对阴影图进行采样。
一个物体接收来自其他物体的阴影,以及它向其他物体投射阴影是两个过程。
1,如果我们想要一个物体接收来自其他物体的阴影,就必须在Shader中对阴影映射纹理(包括屏幕空间的阴影图)进行采样,把采样结果和最后的光照结果相乘来产生阴影效果。
2,如果我们想要一个物体向其他物体投射阴影,就必须把该物体加入到光源的阴影映射纹理的计算中,从而让其他物体在对阴影映射纹理采样时可以得到该物体的相关信息。在Unity 中,这个过程通过为该物体执行LightMode 为ShadowCaster 的 Pass 来实现的。如果使用了屏幕空间的投射映射技术,Unity还会使用这个Pass 产生一张摄像机的深度纹理。
接收两层阴影
主要代码在第一个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,半透明物体也能接收阴影了。