UnityShader开发之阴影
1.原理
阴影映射相机:放置于会产生阴影的光源处的相机,用于生成阴影映射纹理
光源阴影映射纹理:记录了从某个光源的位置出发,能看到的场景中距离光源最近的表面的位置(深度信息)的深度图。
主相机:用来 渲染 主要可视场景的相机
1.1 传统阴影映射技术
将阴影映射相机放置到产生阴影的光源处,然后生成一张阴影映射纹理。
流程上是
1)当光源投射阴影时,将阴影映射相机放到光源位置上
2)调用LightMode标签为ShadowCaster的Pass,这个Pass主要用来更新光源阴影映射纹理
3) 这个通道通过对顶点的变化得到光源空间下的位置,并将深度信息写入光源阴影映射纹理中
4)使用顶点的xy分量对光源阴影映射纹理进行采样,得到光源阴影映射纹理中该位置的深度信息
5)如果获得的深度值小于该顶点的深度值,则该点位于阴影中(即该顶点的位置在光源阴影映射纹理中存储的表面后面)
1.2 屏幕空间的阴影映射(ScreenspaceShadowMap)
1)当光源投射阴影时,将阴影映射相机放到光源位置上
2)调用LightMode标签为ShadowCaster的Pass,获得光源阴影映射纹理以及主相机的深度纹理
3)如果主相机的深度纹理的深度转换到光源空间中大于阴影映射纹理上的深度,则是处于阴影中的
1.3 对比
类型 | 描述 |
---|---|
传统 | |
屏幕空间 |
我们可以看出,传统阴影映射与屏幕空间的阴影映射的主要区别其实就是前向渲染与延迟渲染的区别,这是因为一开始屏幕阴影映射是延迟渲染中产生阴影的方法。
2.使用
2.1 光源配置
2.2 被投影物体配置
2.2.1 Shader宏定义:
阴影shader中相关的宏定义在AutoLight.cginc中
// ---- Screen space direction light shadows helpers (any version)
#if defined (SHADOWS_SCREEN)
#if defined(UNITY_NO_SCREENSPACE_SHADOWS)
UNITY_DECLARE_SHADOWMAP(_ShadowMapTexture);
#define TRANSFER_SHADOW(a) a._ShadowCoord = mul( unity_WorldToShadow[0], mul( unity_ObjectToWorld, v.vertex ) );
inline fixed unitySampleShadow (unityShadowCoord4 shadowCoord)
{
#if defined(SHADOWS_NATIVE)
fixed shadow = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, shadowCoord.xyz);
shadow = _LightShadowData.r + shadow * (1-_LightShadowData.r);
return shadow;
#else
unityShadowCoord dist = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, shadowCoord.xy);
// tegra is confused if we use _LightShadowData.x directly
// with "ambiguous overloaded function reference max(mediump float, float)"
unityShadowCoord lightShadowDataX = _LightShadowData.x;
unityShadowCoord threshold = shadowCoord.z;
return max(dist > threshold, lightShadowDataX);
#endif
}
#else // UNITY_NO_SCREENSPACE_SHADOWS
UNITY_DECLARE_SCREENSPACE_SHADOWMAP(_ShadowMapTexture);
#define TRANSFER_SHADOW(a) a._ShadowCoord = ComputeScreenPos(a.pos);
inline fixed unitySampleShadow (unityShadowCoord4 shadowCoord)
{
fixed shadow = UNITY_SAMPLE_SCREEN_SHADOW(_ShadowMapTexture, shadowCoord);
return shadow;
}
#endif
#define SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1;
#define SHADOW_ATTENUATION(a) unitySampleShadow(a._ShadowCoord)
#endif
2.2.1 宏定义 TRANSFER_SHADOW(a)
(1)支持屏幕阴影技术
a._ShadowCoord = ComputeScreenPos(a.pos);
(2)不支持屏幕阴影技术
a._ShadowCoord = mul( unity_WorldToShadow[0], mul( unity_ObjectToWorld, v.vertex ) );
将本地坐标转换为阴影空间的坐标
2.2.2 宏定义 SHADOW_COORDS(idx1)
unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1;
定义一个数据
2.2.3 宏定义 SHADOW_ATTENUATION(a)
2.2.2 具体Shader代码:
Shader "Custom/Shadow" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
}
SubShader {
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "AutoLight.cginc"
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
SHADOW_COORDS(2)
};
fixed4 _Color;
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_SHADOW(o);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = _Color;
fixed shadow = SHADOW_ATTENUATION(i);
return fixed4(col.xyz*shadow,col.a);
}
ENDCG
}
Pass
{
Name "ShadowCaster"
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
}
}
// FallBack "Specular"
}
效果