該文章demo下載:http://www.demodashi.com/demo/11999.html
王者榮耀的實時陰影及其原理,關於unity3d通過投影來製造一個跟實時陰影一樣的影子,很多地方都有,但是基本都是利用了unity自帶的一個組件Projector,並且對於其原理其實很少有提到過。這裏就先從理論上解釋投影陰影的實現原理,然後將過程寫出來吧。原理部分就不涉及代碼了,混在一起可能更容易讓人混亂吧。
原理:可能有點複雜,爲了簡單些,轉化爲2d視角來分析。陰影的產生正如下圖所示
由上圖可知,陰影的a點與遮擋物的b點在光照方向的垂直面上其實映射的是同一個點d。這很容易讓我們聯想到MVP當中的投影變換。其實是一樣的。我們將於光照方向當做照相機的視線,而與光照方向垂直的面,就是投影變換最後的盒子的正面。這意味着我們可以製作一個正交照相機,使得它與光照方向一致,再將地面模型的各個頂點投影到該照相機上,這時候照相機的紋理UV座標範圍是0~1,地面投影到了照相機,也將其座標映射到0~1之間,由此與照相機的紋理一一對應,於是將照相機照出來的紋理疊加在地面上,就形成了陰影。
代碼如下:
using UnityEngine;
using System.Collections;
public class Shadow : MonoBehaviour {
[SerializeField]
protected Camera m_Cam; //產生陰影的照相機
[SerializeField]
protected Shader m_ShadowShader;
[SerializeField]
protected GameObject m_ShadowReceiveObj; //陰影接受物
private RenderTexture m_RT;
// Use this for initialization
void Start () {
m_RT = new RenderTexture(256, 256,16);
m_Cam.targetTexture = m_RT;
Material mat = new Material(m_ShadowShader);
mat.SetTexture("_MainTex", m_RT);
mat.SetMatrix("_WorldToCameraMatrix", m_Cam.worldToCameraMatrix);
mat.SetMatrix("_ProjectionMatrix", m_Cam.projectionMatrix);
m_ShadowReceiveObj.GetComponent<Renderer>().material = mat;
}
}
上面c#代碼需要用的shader
Shader "Hidden/Shadow"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Tags
{
"Queue" = "Transparent-100"
"RenderType" = "Transparent"
}
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float4x4 _WorldToCameraMatrix;
uniform float4x4 _ProjectionMatrix;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
float4 worldCoord = mul(_Object2World, v.vertex);
float4 cameraCoord = mul(_WorldToCameraMatrix, worldCoord);
float4 projectionCoord = mul(_ProjectionMatrix, cameraCoord);
o.uv = projectionCoord / projectionCoord.w;
o.uv = 0.5f*o.uv + float2(0.5f, 0.5f);
return o;
}
sampler2D _MainTex;
fixed4 frag(v2f i) : SV_Target
{
float dis = distance(i.uv, float2(0.5f, 0.5f));
fixed4 col = tex2D(_MainTex, i.uv);
if(col.r > 0 || col.g > 0 || col.b > 0)
{
col.a = 1.0f;
}
col.rgb = 0;
col.a = (col.a - dis*1.8f)*0.6f;
if (i.uv.y < 0.0f || i.uv.y > 1.0f || i.uv.x < 0.0f || i.uv.x > 1.0f)
{
discard;
}
return col;
}
ENDCG
}
}
}
爲了加上影子淡化效果,以上shader代碼做了處理(col.a = (col.a - dis*1.8f)*0.6f;)效果如下: