【Unity Shader入门精要学习】复杂的光照(四)
2019-10-21 本文已影响0人
小王子称号发放NPC
Unity的阴影——通用ShadowMap的实现
其实还有很多其他阴影的实现方式,详见其他文章。
一、不透明物体阴影
一般ShadowMap的实现方式分为三步:
1、获取ShadowMap(也就是某个光源下的深度图,即将某个摄像机放在光源下,并调整朝向与光源一致,所绘制出的图像),目前只使用自定义获取深度图。其实这一步就是对投射阴影的物体进行深度图绘制

2、将主摄像机绘制的物体装换到光源空间下,然后用转换之后的XY对ShadowMap采样,将采样得到的深度值S_Z与转换到光源空间之后的Z值进行比较,如果Z大于S_Z则说明此点处于阴影中。
3、绘制阴影

获取深度图Shader
所谓深度就是在裁剪坐标下的Z值,因为只有在裁剪坐标下才有深度比较才有意义,最终的深度值会除以W,变成NDC下的值,而NDC下的Z值才是真正的深度值
Shader "Unlit/DepthTexture"
{
SubShader{
Tags{"RenderType"="Opaque"}
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v
{
float4 vertex:POSITION;
};
struct v2f
{
float4 pos:SV_POSITION;
float2 depth : TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.depth = o.pos.zw;
return o;
}
fixed4 frag(v2f i):SV_TARGET
{
float depth = i.depth.x/i.depth.y;
fixed4 color = EncodeFloatRGBA(depth);
return color;
}
ENDCG
}
}
}
获取某个摄像机深度图与转换到裁剪矩阵的CS
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DepthCapture : MonoBehaviour
{
// Start is called before the first frame update
private Camera _childCamera;
private RenderTexture _renderTexture;
void Start()
{
CameraInit();
}
// Update is called once per frame
void Update()
{
SetLightDirection();
CameraUpdate();
}
void CameraInit()
{
_childCamera = new GameObject().AddComponent<Camera>();
_childCamera.name = "DepthCamera";
_childCamera.depth = 2;
_childCamera.clearFlags = CameraClearFlags.SolidColor;
_childCamera.backgroundColor = new Color(1,1,1,0);
_childCamera.aspect = 1;
_childCamera.orthographic = true;
_childCamera.orthographicSize = 10;
_renderTexture = new RenderTexture(1024, 1024, 0);
_renderTexture.wrapMode = TextureWrapMode.Clamp;
_childCamera.targetTexture = _renderTexture;
_childCamera.SetReplacementShader(Shader.Find("Unlit/DepthTexture"),"RenderType" );
Shader.SetGlobalTexture("_CameraRT",_renderTexture);
}
void CameraUpdate()
{
Matrix4x4 lightClipProjectMatrix = GetLightProjectMatrix();
Shader.SetGlobalMatrix("_LightProjection",lightClipProjectMatrix);
}
private Matrix4x4 GetLightProjectMatrix()
{
//这个矩阵其实是世界坐标系转换到相机坐标系的
Matrix4x4 toCameraMatrix = _childCamera.worldToCameraMatrix;
Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(_childCamera.projectionMatrix, false);
Matrix4x4 lightClipMatrix = projectionMatrix * toCameraMatrix;
return lightClipMatrix;
}
void SetLightDirection()
{
Vector3 dir = Vector3.zero - this.transform.position;
this.transform.rotation = Quaternion.LookRotation(dir);
_childCamera.transform.position = this.transform.position;
_childCamera.transform.rotation = this.transform.rotation;
_childCamera.transform.parent = this.transform;
}
}
绘制阴影的Shader
Shader "Unlit/BaseShadow"
{
Properties
{
_DepthTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v
{
float4 vertex : POSITION;
};
struct v2f
{
float4 pos:SV_POSITION;
float4 worldPos:TEXCOORD0;
};
sampler2D _DepthTex;
float4x4 _LightProjection;
sampler2D _CameraRT;
v2f vert (a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld,v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float4 lightClipPos = mul(_LightProjection,i.worldPos);
lightClipPos.xyz = lightClipPos.xyz/lightClipPos.w;
float2 uv = lightClipPos * 0.5 + 0.5;
fixed4 depthColor = tex2D(_CameraRT, uv);
float depth = DecodeFloatRGBA(depthColor);
if(depth < lightClipPos.z + 0.0005){
return fixed4(1,0,0,1);
}
else{
return fixed4(1,1,1,1);
}
}
ENDCG
}
}
}