【Shader進階】SubShader塊標籤Tags——ForceNoShadowCasting屬性和陰影投射ShadowCaster

 

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Unlit/SubShaderTagsTest"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_Color("Color", Color) = (1,0,0,1)
	}
		SubShader
		{
			Tags
			{
				//我希望下面的這些字符串,全部變成宏定義,並且能夠由IDE智能提示 如何做?
				//因爲可能由於拼寫錯誤導致無效,而又不會報錯...
				"Queue" = "Geometry"
				"RenderType" = "Opaque"
				"DisableBatching" = "False"
				"ForceNoShadowCasting" = "True" //無陰影測試(成功)
				"IgnoreProjector" = "True"
				"CanUseSpriteAtlas" = "True"
				"PreviewType" = "Plane"
			}
			LOD 100

			//陰影投射Pass
			Pass
			{
				Tags
				{
					"LightMode" = "ShadowCaster" //1.
				}
				CGPROGRAM
			
				#pragma vertex vert
				#pragma fragment frag
				#pragma multi_compile_shadowcaster //2.
				
				#include "UnityCG.cginc"
				//appdata根據TRANSFER_SHADOW_CASTER_NORMALOFFSET宏來設置,我的情況需要下面3種輸入,具體看UnityCG.cginc的TRANSFER_SHADOW_CASTER_NORMALOFFSET宏需求
				//若發現陰影投射無效或報錯,可能是這裏出問題了,具體信息博客說明
				struct appdata
				{
					float4 vertex : POSITION;					
					float4 texcoord :TEXCOORD0; //vert時需要
					float3 normal : NORMAL;
				};
				struct v2f {
					V2F_SHADOW_CASTER;//3.
				};
				v2f vert(appdata v) {
					v2f o;
					TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) //4.
					return o;
				}
				fixed4 frag(v2f i) :SV_Target {
					SHADOW_CASTER_FRAGMENT(i) //5.
				}
				ENDCG
			}

			Pass
			{
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag

				#include "UnityCG.cginc"

				struct appdata
				{
					float4 vertex : POSITION;
					float2 uv : TEXCOORD0;
					float3 normal : NORMAL;
				};

				struct v2f
				{
					float2 uv : TEXCOORD0;		
					float4 vertex : SV_POSITION;
					float3 normal : NORMAL;
					float4 pos : TEXCOORD1;
				};

				fixed4 _Color;
				sampler2D _MainTex;
				float4 _MainTex_ST;

				v2f vert(appdata v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.uv = TRANSFORM_TEX(v.uv, _MainTex);					
					o.normal = UnityObjectToWorldNormal(v.normal);
					o.pos = mul(unity_ObjectToWorld, v.vertex);
					return o;
				}
				//漫反射
				fixed4 frag(v2f i) : SV_Target
				{
					// sample the texture
					fixed4 col = tex2D(_MainTex, i.uv);
					fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.pos));
					fixed3 normal = normalize(i.normal);
					col = col * dot(lightDir, normal);
					return col * _Color;
				}
			ENDCG
		}
	}
		//FallBack "Diffuse" //這個Diffuse自帶ShadowCaster的Pass 先註釋掉自己實現了一個ShadowCaster(純屬玩玩)
}

關於代碼中標註的陰影投射說明:
1、Tags { "LightMode" = "ShadowCaster" }  標明該Pass塊用於陰影投射功能,專屬Pass塊的標籤屬性
2、 #pragma multi_compile_shadowcaster (尚未清楚,但必要)
3、v2f結構體中的V2F_SHADOW_CASTER宏,具體情況如下,其中UNITY_POSITION(pos)宏還未知在啥.cginc中可自行查找

// Declare all data needed for shadow caster pass output (any shadow directions/depths/distances as needed),
// plus clip space position.
#define V2F_SHADOW_CASTER V2F_SHADOW_CASTER_NOPOS UNITY_POSITION(pos)

#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    // Rendering into point light (cubemap) shadows
    #define V2F_SHADOW_CASTER_NOPOS float3 vec : TEXCOORD0;
#else
    // Rendering into directional or spot light shadows
    #define V2F_SHADOW_CASTER_NOPOS
    // Let embedding code know that V2F_SHADOW_CASTER_NOPOS is empty; so that it can workaround
    // empty structs that could possibly be produced.
#endif

4. TRANSFER_SHADOW_CASTER_NORMALOFFSET宏(我的情況恰好屬於else情況所以會有normal,若你的情況滿足if條件則不需要normal)

// Vertex shader part, with support for normal offset shadows. Requires
// position and normal to be present in the vertex input.
#define TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) TRANSFER_SHADOW_CASTER_NOPOS(o,o.pos)

#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
#else
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) \
        opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); \
        opos = UnityApplyLinearShadowBias(opos);
#endif

5. SHADOW_CASTER_FRAGMENT宏

#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    #define SHADOW_CASTER_FRAGMENT(i) return UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);
#else
    #define SHADOW_CASTER_FRAGMENT(i) return 0;
#endif

具體這些宏在幹什麼,目前還未了解,根據已瞭解知識來看是在進行如下操作。(注意UnityShader並不完整,有很多操作都在編譯成真正的Shader代碼,如 OpenGL的GLSL(OpenGL Shading Language) 或 微軟DirectX的HLSL(High Level Shading Language) 或 NVIDIA與微軟合作製作的CG(C for Graphic)

https://blog.csdn.net/qq_39574690/article/details/99766585
Shader從陰影圖根據陰影紋理座標採樣陰影值,再使用該陰影值對輸出顏色進行相乘,最終表現出陰影效果。

陰影圖是一張二維紋理,存儲着場景上物體的陰影部分。

陰影圖有如下兩種方法生成:

①傳統方法:Unity首先把攝像機放到光源位置上,調用LightMode爲ShadowCaster的Pass來進行渲染出光源空間下的陰影映射紋理(實際上是一個表面深度紋理),之後在正常渲染的Pass中將頂點位置變換到光源空間下,將這個光源空間下的頂點座標xy分量作爲紋理座標xy分量從光源空間下的陰影映射紋理採樣出深度值,再與該Pass處理的頂點或片元深度值進行比較,如果取出來的深度值比自身深度值更小,說明當前處理的頂點或片元處於陰影中,至於陰影值是多少,書上沒解釋明白,我也沒弄明白,具體還要看源碼看它是怎麼的算法。(問題1)

②屏幕空間的陰影映射技術:Unity5及之後,需顯卡支持MRT。也是在LightMode爲ShadowCaster的Pass中進行渲染出屏幕空間下的陰影投射紋理,同時Unity會計算出攝像機的表面深度紋理,同一紋理座標上,若陰影映射紋理的深度值比攝像機的表面深度紋理的深度值更小,說明該Pass處理的頂點或片元處於陰影中,將該紋理座標上的陰影映射紋理的深度值賦值到陰影圖相應座標上。所以攝像機的表面深度紋理實際上是從陰影映射紋理中選出那些真正的陰影部分,成爲所謂的“陰影圖”。最終,我們在正常的Pass中,通過將頂點座標轉屏幕空間下,然後根據這個座標來從陰影圖採樣陰影值,之後你都懂了。

在總結到這裏,不妨猜想一下,問題1中的陰影值是多少?應該是陰影映射紋理的深度值,因爲實際上無論①和②真正的陰影都是從這個陰影映射紋理而來的,②只不過是多了一個過濾,①的情況有點複雜,因爲我們看的視角是屏幕空間的,而陰影映射紋理是光源空間的,假設光源和攝像機是在同一個位置那還好理解,如果是不同位置,就有點難以理解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章