天天看點

Unity FlatShadow陰影技術

雖然不是什麼新技術,但是是老技術啊,對啊,就是這麼驢唇不對馬嘴,就是這麼的無恥,先發個招聘:

坐标山東青島市北,招聘日系畫師,spine動畫師。

然後再說一下QQ群,山東手遊研發聚集地335772557,進群先看公告,要是抱着我要學習的心态進群的話,那你就失望了,因為是個死群。

偶爾心情好的是時候會發個技術連結。

扯的雖然不多,但是要進入話題了。

測試來源于網際網路,若侵權請告知删除。入群請不要跟我要模型貼圖

由于是邊寫邊做技術記錄,是以就不先放效果圖了

先說一下思路,就是在shader中重開一個pass做shadow的渲染,思路就是這麼簡單

第一個pass就是原來模型的渲染,這個就不說了,這個效果跟個人需求相關,我這裡用了surfaceshader的standard來做測試了。

先上一下這個此事開頭的shader吧 

效果圖:

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術
Unity FlatShadow陰影技術
Shader "ShaderStore/Shadow/ShadowFlatOnPlane"
{
	Properties
	{
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" "ForceNoShadowCasting"="True"}
		LOD 100
		CGPROGRAM
		#pragma surface surf Standard noshadow
		#pragma target 3.0
		sampler2D _MainTex;
		struct Input 
		{
			float2 uv_MainTex;
		};

		half _Glossiness;
		half _Metallic;
		fixed4 _Color;

		void surf (Input IN, inout SurfaceOutputStandard o) 
		{
			fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Metallic = _Metallic;
			o.Smoothness = _Glossiness;
			o.Alpha = c.a;
		}
		ENDCG

	}
}
           

接下來我們提個需求,shadow的顔色可以修改

首先需要在Properties裡貼圖對應的參數,友善調節

Properties
	{
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0

		_ShadowColor("Shadow Color",Color) = (1,0,0,1)
	}
           

然後我們繼續,新增一個pass去修改模型的頂點,讓模型的頂點投影到世界中,首先,shadow是平的,也就是worldposition的y要設定為0

将模型空間的頂點轉到世界中去,然後把y=0,在轉回模型空間。

float4 worldPos = mul(_Object2World,v.vertex);

worldPos.y = 0;

float4 localPos = mul(_World2Object,worldPos);

這樣操作之後得到的結果是

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術
Shader "ShaderStore/Shadow/ShadowFlatOnPlane"
{
	Properties
	{
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0

		_ShadowColor("Shadow Color",Color) = (1,0,0,1)
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" "ForceNoShadowCasting"="True"}
		LOD 100
		CGPROGRAM
		#pragma surface surf Standard noshadow
		#pragma target 3.0
		sampler2D _MainTex;
		struct Input 
		{
			float2 uv_MainTex;
		};

		half _Glossiness;
		half _Metallic;
		fixed4 _Color;

		void surf (Input IN, inout SurfaceOutputStandard o) 
		{
			fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Metallic = _Metallic;
			o.Smoothness = _Glossiness;
			o.Alpha = c.a;
		}
		ENDCG
		

		//ShadowFlatOnPlane
		Pass
		{
			OffSet -1,-1
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex:POSITION;
			};

			struct v2f 
			{
				float4 pos:SV_POSITION;
			};

			fixed4 _ShadowColor;

			v2f vert(appdata v)
			{
				v2f o;
				float4 worldPos = mul(unity_ObjectToWorld,v.vertex);
				worldPos.y = 0;
				float4 localPos = mul(unity_WorldToObject,worldPos);
				o.pos = UnityObjectToClipPos(localPos);
				return o;
			}

			fixed4 frag(v2f i):SV_Target
			{
				return _ShadowColor;
			}

			ENDCG
		}
	}
}
           

然後新的問題出現了,陰影着實的在時間y=0的平面上,跟燈光方向無關。新需求就是希望影子投影與燈光的方向有關

(這篇博文是不是很流水賬,呵呵呵,主要的意圖就是招聘)

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術
v2f vert(appdata v)
           
{
	v2f o;

	float3 lightDir = -normalize(_WorldSpaceLightPos0.xyz);

	loat4 worldPos = mul(unity_ObjectToWorld,v.vertex);
				
	worldPos.x -= worldPos.y/lightDir.y*lightDir.x;
        worldPos.z -= worldPos.y/lightDir.y*lightDir.z;
	worldPos.y = 0;

	o.pos = mul(UNITY_MATRIX_VP,worldPos);
	return o;
}
           

然後新問題:影子是不是要透明一些?

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術
//ShadowFlatOnPlane
		Pass
		{
			Tags{"RenderType"="Transparent" "Queue"="Transparent"}
			OffSet -1,-1
			Blend SrcAlpha OneMinusSrcAlpha
			ZWrite Off

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
           

新問題帶來的新需求,解決方案stencil

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術
//ShadowFlatOnPlane
		Pass
		{
			stencil
			{
				Ref 1
				Comp NotEqual
				Pass Replace
			}

			Tags{"RenderType"="Transparent" "Queue"="Transparent"}
			OffSet -1,-1
			Blend SrcAlpha OneMinusSrcAlpha
			ZWrite Off

			CGPROGRAM
           

看似基本的效果也差不多了,我高興的複制了一個,錯了下位置,然後又有了新的問題,或許對于有些項目來說也不是個問題

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術

怎麼解決呢?還是stencil吧,不同的材質給個不同的ref

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術
Shader "ShaderStore/Shadow/ShadowFlatOnPlane"
{
	Properties
	{
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0

		_ShadowColor("Shadow Color",Color) = (1,0,0,1)
		_Ref("Stencil Ref",Int) = 1
	}
......

//ShadowFlatOnPlane
		Pass
		{
			stencil
			{
				Ref [_Ref]
				Comp NotEqual
				Pass Replace
			}
......
           

現在基本上的問題都搞定了,我有興緻使然,轉了下地面的plane,呵呵。。

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術

新需求,然影子透到指定的plane平面吧,看到了哈,是平面,曲面的話我也不知道,shadowmap去幹吧

.....我突然卡殼。。。這個需求過會再說吧。。。新需求,陰影從腳底到頭頂有漸變過渡

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術
struct v2f 
			{
				float4 pos:SV_POSITION;
				float Value:TEXCOORD0;
			};

			fixed4 _ShadowColor;

			v2f vert(appdata v)
			{
				v2f o;

				float3 lightDir = -normalize(_WorldSpaceLightPos0.xyz);
				float4 worldPos = mul(unity_ObjectToWorld,v.vertex);

				worldPos.x -= worldPos.y/lightDir.y*lightDir.x;
                		worldPos.z -= worldPos.y/lightDir.y*lightDir.z;
				worldPos.y = 0;
				o.pos = mul(UNITY_MATRIX_VP,worldPos);
				

				o.Value = 1-worldPos.z/14.0;

				return o;
			}

			fixed4 frag(v2f i):SV_Target
			{
				return _ShadowColor*i.Value;
			}
           

再來說上面遺留的問題,如何投影到custom的plane上呢,把頂點轉到planer,然後處理,在轉回world,再mvp。。。

Unity FlatShadow陰影技術
Unity FlatShadow陰影技術

這個操作免不了C#的參數傳入

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class ShadowFlatOnPlane : MonoBehaviour {
    public Transform ShadowPlane;
    public Material[] ShadowMats;
	// Use this for initialization
	void Start () {
        foreach (Material mat in ShadowMats)
        {
            mat.SetMatrix("_World2Ground", ShadowPlane.GetComponent<MeshRenderer>().worldToLocalMatrix);
            mat.SetMatrix("_Ground2World", ShadowPlane.GetComponent<MeshRenderer>().localToWorldMatrix);
        }
    }
    private void Update()
    {
        Start();
    }
}
           
struct v2f 
			{
				float4 pos:SV_POSITION;
				float Value:TEXCOORD0;
			};

			fixed4 _ShadowColor;
			float4x4 _World2Ground;
			float4x4 _Ground2World;

			v2f vert(appdata v)
			{
				v2f o;
				float3 lightDir = -normalize(_WorldSpaceLightPos0.xyz);
				lightDir = normalize(mul(_World2Ground,float4(lightDir,0)).xyz);
				float4 worldPos = mul(unity_ObjectToWorld,v.vertex);
				worldPos = mul(_World2Ground,worldPos);
				worldPos.xz -= (worldPos.y/lightDir.y)*lightDir.xz;
				worldPos.y = 0;

				worldPos = mul(_Ground2World,worldPos);
				worldPos = mul(unity_WorldToObject,worldPos);
				o.pos = UnityObjectToClipPos(worldPos);

				o.Value = 1-worldPos.z/14.0;

				return o;
			}
           

這個方案也有弊端,在效果上也有個小問題,就是過度旋轉地面plane之後,影子會出現一些問題,還有就是目前寫的隻有Directional Light的效果,在lightDir的計算上可以加入其他光源的判定。。

有合适的人,請看博文的人給推薦推薦?

繼續閱讀