Unity FlatShadow阴影技术

虽然不是什么新技术,但是是老技术啊,对啊,就是这么驴唇不对马嘴,就是这么的无耻,先发个招聘:

坐标山东青岛市北,招聘日系画师,spine动画师。


然后再说一下QQ群,山东手游研发聚集地335772557,进群先看公告,要是抱着我要学习的心态进群的话,那你就失望了,因为是个死群。

偶尔心情好的是时候会发个技术链接。


扯的虽然不多,但是要进入话题了。

测试来源于互联网,若侵权请告知删除。入群请不要跟我要模型贴图

由于是边写边做技术记录,所以就不先放效果图了


先说一下思路,就是在shader中重开一个pass做shadow的渲染,思路就是这么简单

第一个pass就是原来模型的渲染,这个就不说了,这个效果跟个人需求相关,我这里用了surfaceshader的standard来做测试了。

先上一下这个此事开头的shader吧 

效果图:


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);

这样操作之后得到的结果是


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的平面上,跟灯光方向无关。新需求就是希望影子投影与灯光的方向有关

(这篇博文是不是很流水账,呵呵呵,主要的意图就是招聘)


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;
}

然后新问题:影子是不是要透明一些?



		//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



//ShadowFlatOnPlane
		Pass
		{
			stencil
			{
				Ref 1
				Comp NotEqual
				Pass Replace
			}

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

			CGPROGRAM
看似基本的效果也差不多了,我高兴的复制了一个,错了下位置,然后又有了新的问题,或许对于有些项目来说也不是个问题


怎么解决呢?还是stencil吧,不同的材质给个不同的ref



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,呵呵。。


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

.....我突然卡壳。。。这个需求过会再说吧。。。新需求,阴影从脚底到头顶有渐变过渡



		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。。。



这个操作免不了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的计算上可以加入其他光源的判定。。

有合适的人,请看博文的人给推荐推荐?







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值