ShadowMap的原理就是在灯光空间渲染出一张RenderTexture 记录物体顶点在灯光空间中的深度值,在渲染地面的时候 使用顶点计算在灯光空间中的深度 ,再与深度图中的值进行比较>则是阴影
1、渲染深度图使用的shader——一定要放在Resources
Shader "ZLY/DeapthTextureShader"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 depth:TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4x4 zly_ProjectionMatrix;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);//模型空间转裁剪空间
//o.depth=o.vertex.zw; 坐标空间转换后会产生深度误差 所以使用相同的深度转换方式
zly_ProjectionMatrix = mul(zly_ProjectionMatrix, unity_ObjectToWorld);
o.depth = mul(zly_ProjectionMatrix,v.vertex).zw;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float depth =i.depth.x/i.depth.y;
#if defined(UNITY_REVERSED_Z)
depth = 1.0f - depth;//转成齐次空间坐标 [1,0]变成[0,1]
#endif
fixed4 col = EncodeFloatRGBA(depth);// float四个字节 存RGB
return col;
}
ENDCG
}
}
}
2、顶点深度比较 放在要渲染的物体上
Shader "ZLY/ShadowOnShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
// sampler2D unity_Lightmap;//若开启光照贴图,系统默认填值
// float4 unity_LightmapST;//与上unity_Lightmap同理
struct v2f {
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
float2 uv2:TEXCOORD1;
float4 proj : TEXCOORD3;
};
float4x4 zly_ProjectionMatrix;//世界空间转灯关空间矩阵
sampler2D zly_DepthTexture;//深度图
v2f vert(appdata_full v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//动态阴影
zly_ProjectionMatrix = mul(zly_ProjectionMatrix, unity_ObjectToWorld);//模型空间转世界空间转灯关空间
o.proj = mul(zly_ProjectionMatrix, v.vertex);
//--------------------------------------------------
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);//偏移压缩后的像素取点位置
o.uv2 = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
return o;
}
fixed4 frag(v2f v) : COLOR
{
//解密光照贴图计算公式
float3 lightmapColor = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap,v.uv2));
fixed4 col = tex2D(_MainTex, v.uv);
col.rgb *= lightmapColor;
float depth = v.proj.z/v.proj.w;//齐次坐标深度值
#if defined(UNITY_REVERSED_Z)
depth = 1.0f - depth;//转成齐次空间坐标 [1,0]变成[0,1]
#endif
fixed4 dcol = tex2Dproj(zly_DepthTexture, v.proj);//转齐次坐标后uv
float d = DecodeFloatRGBA(dcol);//RGB转float
float shadowScale = 1;
if(depth > d)
{
shadowScale = 0.55;
}
return col*shadowScale;
}
ENDCG
}
}
}
3、调用Shader的脚本
using UnityEngine;
using System.Collections;
/// <summary>
/// 创建depth相机
/// by lijia
/// </summary>
public class DepthTextureCamera : MonoBehaviour
{
private static DepthTextureCamera _inst;
public static DepthTextureCamera Instance() { return _inst; }
private Transform _target;
public Camera _camera;
private RenderTexture _rt;
/// <summary>
/// 光照的角度
/// </summary>
//public Transform lightTrans;
Matrix4x4 sm = new Matrix4x4();
private float _xOffset = 0f;
private float _yOffset = 20f;
private float _zOffset = 20f;
void Awake()
{
_inst = this;
}
void Start()
{
//_camera = new GameObject().AddComponent<Camera>();
//_camera.name = "DepthCamera";
//_camera.depth = 0;
//_camera.clearFlags = CameraClearFlags.SolidColor;
//_camera.backgroundColor = new Color(1, 1, 1, 0);
//_camera.cullingMask = LayerMask.GetMask("Player");
//_camera.aspect = 1;
//_camera.transform.parent = lightTrans.transform;
//_camera.transform.localPosition = Vector3.left * 15;
//_camera.transform.localEulerAngles = Vector3.up*30;
//_camera.orthographic = true;
//_camera.orthographicSize = 10;
sm.m00 = 0.5f;
sm.m11 = 0.5f;
sm.m22 = 0.5f;
sm.m03 = 0.5f;
sm.m13 = 0.5f;
sm.m23 = 0.5f;
sm.m33 = 1;
_rt = new RenderTexture(2048, 2048, 0);
_rt.wrapMode = TextureWrapMode.Clamp;
_camera.targetTexture = _rt;
_camera.SetReplacementShader(Shader.Find("ZLY/DeapthTextureShader"), "RenderType");
}
void Update()
{
if(_target==null)
{
return;
}
HELPER_VEC.Set(_target.position.x + _xOffset, _target.position.y + _yOffset,
_target.position.z + _zOffset);
this.transform.position = HELPER_VEC;
//transform.LookAt(_target.position);
//世界顶点坐标转换到_camera裁剪空间坐标矩阵 sm[-1,1]转换到[0,1]转换矩阵
Matrix4x4 tm = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, false) * _camera.worldToCameraMatrix;
tm = sm * tm;
Shader.SetGlobalMatrix("zly_ProjectionMatrix", tm);
}
private static Vector3 HELPER_VEC = new Vector3();
public void SetTarget(Transform target,ushort mapID)
{
_target = target;
HELPER_VEC.Set(60, -123, 0);//设置光照方向
transform.position = _target.position;
transform.eulerAngles = HELPER_VEC;
transform.position -= transform.forward * 40;
_xOffset = transform.position.x - _target.position.x;
_yOffset = transform.position.y - _target.position.y;
_zOffset = transform.position.z - _target.position.z;
Matrix4x4 tm = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, false) * _camera.worldToCameraMatrix;
tm = sm * tm;
Shader.SetGlobalMatrix("zly_ProjectionMatrix", tm);
Shader.SetGlobalTexture("zly_DepthTexture", _rt);
Shader.SetGlobalFloat("zly_Shadow", 1);
}
}
在顶点坐标从模型空间转换到灯光空间时,深度会发生偏移,不同的相机旋转角度offset不同
使用:
o.vertex = UnityObjectToClipPos(v.vertex);
//o.depth=o.vertex.zw;
改为使用:——此方法会产生偏移
//世界顶点坐标转换到_camera裁剪空间坐标矩阵 sm[-1,1]转换到[0,1]转换矩阵
Matrix4x4 tm = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, false) * _camera.worldToCameraMatrix;
tm = sm * tm;
Shader.SetGlobalMatrix("zly_ProjectionMatrix", tm);
zly_ProjectionMatrix = mul(zly_ProjectionMatrix, unity_ObjectToWorld);
o.depth = mul(zly_ProjectionMatrix,v.vertex).zw;
是为了使比较的depth使用相同的空间转换方式 产生相同的差值