1. 程式人生 > >【Unity Shaders】ShadowGun系列之二——霧和體積光

【Unity Shaders】ShadowGun系列之二——霧和體積光

依靠 action 圖形學 取值 線性 數學 viewer https 是否


寫在前面


體積光,這個名稱是God Rays的中文翻譯,感覺不是非常形象。God Rays事實上是Crepuscular rays在圖形學中的說法,而Crepuscular rays的意思是雲隙光、曙光、曙暮輝的意思。在現實生活中,它的樣子大概是以下這樣:

技術分享


體積光的翻譯大概就是因為這樣的光可見好像有體積似的。

這些光通常是因為強烈的陽光從一些縫隙。如雲間縫隙、窗戶的縫隙中。透到較暗的環境中所造成的。假設要真實模擬體積光。可能須要非常復雜的粒子渲染。但這在移動設備上基本是不可能實現的。


ShadowGun中把體積光歸於是霧的一種應用。霧和體積光有非常多類似的地方,一個大面積的體積光從視覺上來看和霧非常像,它們有一個共性的感性認識,就是其可見度和距離視角的遠近有關。因此。ShadowGun

用簡單網格+Alpha Blending的方法來模擬霧和體積光。因為ShadowGun中體積光是霧的一種應用,因此以下統一稱為霧效。



ShadowGun


ShadowGun事實上最開始是2011年的一個移動平臺的第三人稱射擊遊戲。當然,也是用Unity開發的。當年,因為在畫面上的出色表現贏得了非常多眼球~更難能可貴的是,在2012的時候。它的開發人員放出了演示樣例場景,來讓很多其它的開發人員學習怎樣優化移動平臺上的shader。下載地址請戳官方博客。看不懂英文的能夠看這篇(寫得非常不錯)。

項目裏共包括了將近20個優化後的shader。關於使用許可的問題,項目裏的shader都是能夠免費使用的。而貼圖和模型是不可用於商業用途的呦~


盡管ShadowGun的出場時間有點久遠了,但非常多技術還是能夠借鑒滴~並且它如今仍然在更新,並且價格為高昂的¥30。可見其對自信程度。


ShadowGun裏包括了幾個比較重要的shader,比如非常有名的旗幟飄動的shader,動態效果的天空盒子的shader。環境高光紋理映射等等。



ShadowGun中的霧效



這裏的霧效不是指那種真的全局環境都受影響的大霧。而是一種現象:在視角逐漸接近它的時候。視野逐漸清晰。

比如對於體積光,從遠處看它可能會感覺非常亮。但越接近亮度越小,越能看清後面的物體。

這樣的效果能夠非常好地讓玩家感覺到深度的變化。ShadowGun的解決方法是使用一個簡單的網格(Fog planes)+透明紋理來模擬。一旦玩家靠近時,通過減淡顏色+使網格頂點移開(須要移開的原因是因為,即使是全然透明的alpha面也會消耗非常多渲染時間,而這裏的移開通常是把網格收縮變小。降低透明區域)的方法來模擬這個效果。


而假設要使用這個Shader,就須要在三維軟件中處理那麽Fog planes:

  • 頂點的透明度用於決定頂點能否夠移動(透明度為0表示不可移動,1為可移動)
  • 頂點法線決定移動的方向
  • 然後Shader通過計算與觀察者的距離來控制霧面的淡入/淡出。

這樣的技術非常easy,並且能夠用於光射線、光錐等各種透明效果。


在ShadowGun中。有三個shaders使用了這個技術:GodRaysBlinking GodRaysBlinking GodRays Billboarded


當中GodRays用於模擬體積光。


技術分享


Blinking GodRays用於各種blingbling的光效。它也是這三個中應用最廣的一個shader。包括了光錐的閃爍、水面反光、儀表盤的燈光閃爍(圖中的綠色發光部分)、金屬表面的反光閃爍、頂棚的陽光閃爍、火焰及火光的閃爍(地面上的火光閃爍和飛船後發射器的火焰)、光霧效果(背景的藍綠色光霧)等等


技術分享 技術分享 技術分享 技術分享 技術分享 技術分享


Blinking GodRays Billboarded用於水箱中的blingbling燈光效果(下圖中罐體周圍的綠色發光部分)。


技術分享


能夠看出來。場景裏差點兒不論什麽看起來會發光的物體都是靠這樣的技術模擬的。



GodRays


GodRays是當中最簡單、最主要的shader。


代碼例如以下:

Shader "MADFINGER/Transparent/GodRays" {

Properties {
	_MainTex ("Base texture", 2D) = "white" {}
	_FadeOutDistNear ("Near fadeout dist", float) = 10	
	_FadeOutDistFar ("Far fadeout dist", float) = 10000	
	_Multiplier("Multiplier", float) = 1
	_ContractionAmount("Near contraction amount", float) = 5
}

	
SubShader {
	Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
	
	Blend One One
//	Blend One OneMinusSrcColor
	Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
	
	LOD 100
	
	CGINCLUDE	
	#include "UnityCG.cginc"
	sampler2D _MainTex;
	
	float _FadeOutDistNear;
	float _FadeOutDistFar;
	float _Multiplier;
	float _ContractionAmount;
	
	
	struct v2f {
		float4	pos	: SV_POSITION;
		float2	uv		: TEXCOORD0;
		fixed4	color	: TEXCOORD1;
	};
	
	v2f vert (appdata_full v)
	{
		v2f 		o;
		float3	viewPos	= mul(UNITY_MATRIX_MV,v.vertex);
		float		dist		= length(viewPos);
		float		nfadeout	= saturate(dist / _FadeOutDistNear);
		float		ffadeout	= 1 - saturate(max(dist - _FadeOutDistFar,0) * 0.2);
		
		ffadeout *= ffadeout;
		
		nfadeout *= nfadeout;
		nfadeout *= nfadeout;
		
		nfadeout *= ffadeout;
		
		float4 vpos = v.vertex;
		
		vpos.xyz -=   v.normal * saturate(1 - nfadeout) * v.color.a * _ContractionAmount;
						
		o.uv		= v.texcoord.xy;
		o.pos	= mul(UNITY_MATRIX_MVP, vpos);
		o.color	= nfadeout * v.color * _Multiplier;
						
		return o;
	}
	ENDCG


	Pass {
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#pragma fragmentoption ARB_precision_hint_fastest		
		fixed4 frag (v2f i) : COLOR
		{			
			return tex2D (_MainTex, i.uv.xy) * i.color;
		}
		ENDCG 
	}	
}
}

能夠看出來,frag函數非常easy。事實上ShadowGun中非常多就是都是通過網格來模擬光照效果,它們的frag函數一般非常easy,而大部分計算都在vert函數中。這是能夠理解的。因為逐頂點永遠比逐像素的效率更高。vert裏負責計算三個部分:一個是頂點位置,一個是紋理坐標。一個是傳遞給fragment的顏色信息(這裏還包括了重要的透明度信息)。frag函數裏就能夠通過簡單的紋理採樣和顏色相乘來得到最終的效果


我們來看最重要的vert函數。這個shader中沒有對紋理坐標做什麽更改,因此。這個vert函數的關鍵僅僅有兩個部分,一個是頂點位置,一個的顏色信息。


我們先來看顏色的計算過程。vert在輸入的頂點顏色的基礎(這意味著在建模時就要給頂點賦予合適的體積光顏色)上。對其還乘以了一個乘數_Multiplier和一個衰減值nfadeout。乘數_Multiplier非常好理解,就是用於改變亮度而已。關鍵在於nfadeoutnfadeout是一個範圍在(0。1)之間的淡化系數。它用於模擬淡入或淡出效果。和它相關的有兩個屬性:_FadeOutDistNear_FadeOutDistFar

玩家由無限遠開始接近這個物體的過程中,一開始是遠大於_FadeOutDistFar,那麽是看不到這個體積光的;然後逐漸接近_FadeOutDistFar後,開始出現淡入效果;假設小於了_FadeOutDistNear,那麽就會開始模擬淡出的效果。

與其相關的是以下兩句:

		float		nfadeout	= saturate(dist / _FadeOutDistNear);
		float		ffadeout	= 1 - saturate(max(dist - _FadeOutDistFar,0) * 0.2);

當中dist是在View Space中距離原點的遠近,也就是距離視角的遠近。nfadeout負責計算“假設小於了_FadeOutDistNear,那麽就會開始模擬淡出的效果”這一效果。

能夠看出,當dist大於_FadeOutDistNear時,總是返回1,從而不會產生不論什麽影響;而一旦小於_FadeOutDistNear後,就會產生一個線性的衰減。

ffadeout的計算看起來復雜也難懂一點。我們希望ffadeout的結果是。在dist遠大於_FadeOutDistFar時返回0。在dist逐漸接近_FadeOutDistFar時,逐漸從0添加到1;在dist小於_FadeOutDistFar時,返回1。從函數圖像來看,事實上就是個分段函數。

上面的寫法僅僅是通過max和saturate函數來實現這樣的分段的目的。當中0.2是模擬了淡入的速率。以下就是這句計算表達式的函數圖像:

技術分享


對於一般的射燈模擬,_FadeOutDistNear的值都比較大。在計算完nfadeoutffadeout後。並沒有直接相乘,而是各自進行了指數操作。

這裏感覺是感性的計算。即希望淡入/淡出的速率更快或者更慢等。


以下是頂點位置的計算。與其相關的語句是:

		float4 vpos = v.vertex;
		vpos.xyz -=   v.normal * saturate(1 - nfadeout) * v.color.a * _ContractionAmount;

這裏的目的是為了在淡出時移開(也能夠為收縮)頂點。當nfadeout值越接近0時,表明正在淡出,那麽頂點就須要朝著其法線方向的反方向進行收縮

當中,頂點的透明通道決定了這個頂點能否夠移動(這是因為,體積光往往有一邊是不能夠移動的,想象一下從窗戶投進來的光,起點用於在和窗戶的銜接處是不會動的)。而_ContractionAmount表示收縮的程度。


剩下的部分就沒什麽難的了。



Blinking GodRays


Blinking GodRays僅僅更改了vert部分,並且涉及到很多其它的參數和變量。

代碼例如以下:

	v2f vert (appdata_full v)
	{
		v2f 		o;
		
		float		time 			= _Time.y + _BlinkingTimeOffsScale * v.color.b;		
		float3	viewPos		= mul(UNITY_MATRIX_MV,v.vertex);
		float		dist			= length(viewPos);
		float		nfadeout	= saturate(dist / _FadeOutDistNear);
		float		ffadeout	= 1 - saturate(max(dist - _FadeOutDistFar,0) * 0.2);
		float		fracTime	= fmod(time,_TimeOnDuration + _TimeOffDuration);
		float		wave			= smoothstep(0,_TimeOnDuration * 0.25,fracTime)  * (1 - smoothstep(_TimeOnDuration * 0.75,_TimeOnDuration,fracTime));
		float		noiseTime	= time *  (6.2831853f / _TimeOnDuration);
		float		noise			= sin(noiseTime) * (0.5f * cos(noiseTime * 0.6366f + 56.7272f) + 0.5f);
		float		noiseWave	= _NoiseAmount * noise + (1 - _NoiseAmount);
		float		distScale	= min(max(dist - _SizeGrowStartDist,0) / _SizeGrowEndDist,1);
		
			
		wave = _NoiseAmount < 0.01f ? wave : noiseWave;
		
		distScale = distScale * distScale * _MaxGrowSize * v.color.a;
		
		wave += _Bias;
		
		ffadeout *= ffadeout;
		
		nfadeout *= nfadeout;
		nfadeout *= nfadeout;
		
		nfadeout *= ffadeout;
		
		float4	mdlPos = v.vertex;
		
		mdlPos.xyz += distScale * v.normal;
				
		o.uv		= v.texcoord.xy;
		o.pos	= mul(UNITY_MATRIX_MVP, mdlPos);
		o.color	= nfadeout * _Color * _Multiplier * wave;
						
		return o;
	}

相同。這個shader也是僅僅改動了顏色信息和頂點位置。


我們先來看頂點位置

頂點位置的計算例如以下:

float	distScale = min(max(dist - _SizeGrowStartDist,0) / _SizeGrowEndDist,1);
distScale = distScale * distScale * _MaxGrowSize * v.color.a;
float4	mdlPos = v.vertex;
mdlPos.xyz += distScale * v.normal;
o.pos	= mul(UNITY_MATRIX_MVP, mdlPos);

先來理解為什麽要改動頂點位置。

這裏並非和上面一樣是為了收縮頂點,相反是為了擴大頂點區域。

這主要是為了模擬射燈的效果,我們在遠離光源的過程中會感覺好像光照範圍範圍變大了。

distScale和上面的ffadeout類似,它相同是一個範圍在(0,1)之間的值,有兩個參數控制頂點增長的起始和終止位置,_SizeGrowStartDist_SizeGrowEndDist

在dist小於_SizeGrowStartDist時,distScale返回0;在dist逐漸大於_SizeGrowStartDist時,逐漸從0增大。當dist(實際是dist-_SizeGrowStartDist,但_SizeGrowStartDist通常都非常小)大於_SizeGrowEndDist時。返回1。表示已經達到了最大的擴張大小。當中,_MaxGrowSize用於控制擴張的大小,而頂點的透明度決定了該點是否會移動(和上面的類似的)。

因為這裏是擴張和非收縮,因此移動方向是朝著頂點法線的方向正向移動


比較復雜的是頂點顏色的計算

o.color	= nfadeout * _Color * _Multiplier * wave

首先。這裏沒有使用原來的頂點顏色進行計算,而是同意用戶在面板中調整_Color參數。這是能夠理解的,因為體積光的顏色基本不變,通常都是偏黃的,因此在上一個shader中能夠直接使用原來的頂點顏色進行計算。節約空間。而這裏的用途非常廣泛,讓用戶自己定義顏色是更好的選擇。

上面的nfadeout_Multiplier與之前的計算無異,不再贅述。復雜的是wave的計算:

		float		fracTime	= fmod(time,_TimeOnDuration + _TimeOffDuration);
		float		wave			= smoothstep(0,_TimeOnDuration * 0.25,fracTime)  * (1 - smoothstep(_TimeOnDuration * 0.75,_TimeOnDuration,fracTime));
		float		noiseTime	= time *  (6.2831853f / _TimeOnDuration);
		float		noise			= sin(noiseTime) * (0.5f * cos(noiseTime * 0.6366f + 56.7272f) + 0.5f);
		float		noiseWave	= _NoiseAmount * noise + (1 - _NoiseAmount);
			
		wave = _NoiseAmount < 0.01f ?

wave : noiseWave; wave += _Bias;


我們先來理解要達到的效果。

主要的淡出效果已經通過上面的nfadeout實現了,因此這裏主要是為了模擬閃爍的效果。閃爍本質上就是一種動畫效果。這裏給出了兩種動畫模擬的方式:一種是均勻跳躍的脈沖波模擬。一種是非均勻的噪聲模擬。這是用過_NoiseAmount參數實現的,它的面板凝視中也說明了這一點,“Noise amount (when zero, pulse wave is used)”,即當噪聲非常小時,就會使用均勻跳躍的脈沖波模擬。否則就使用_NoiseAmount模擬非均勻的閃爍動畫。

我們首先來看怎樣模擬均勻的脈沖波閃爍。其計算式例如以下:

float time = _Time.y + _BlinkingTimeOffsScale * v.color.b; 
float fracTime = fmod(time,_TimeOnDuration + _TimeOffDuration);         
float wave = smoothstep(0, _TimeOnDuration * 0.25, fracTime)  * (1 - smoothstep(_TimeOnDuration * 0.75, _TimeOnDuration, fracTime));

這裏面涉及了三個參數:_BlinkingTimeOffsScale,_TimeOnDuration。_TimeOffDuration。我們還是從wave的函數圖像出發來看這裏面的貓膩(註意當中的參數):

技術分享 技術分享

能夠看出來_TimeOnDuration_TimeOffDuration兩個參數負責控制脈沖波的高頻區域的時間長度和低頻區域的時間長度。而_BlinkingTimeOffsScale代碼裏的說明是。“Blinking time offset scale (seconds)”,從第二幅圖像上能夠看出來事實上就是制定從哪個位置開始模擬脈沖閃爍。須要註意的是。_BlinkingTimeOffsScale的取值範圍在(0, _TimeOnDuration + _TimeOffDuratio),假設大於這個範圍也會相當於對_TimeOnDuration + _TimeOffDuratio取模。


圖像直觀了解後,我們再來看代碼。fracTime反應了當前處於一個周期中的那個階段,因此須要使用當前的時間time對整個循環周期_TimeOnDuration + _TimeOffDuratio取模。smoothstep函數將返回一個範圍在(0, 1)之間的值,這個值由第三個參數相對於前兩個參數的位置來平滑插值決定的。

Nvidia文檔裏是這樣給出它的參考代碼的:

float smoothstep(float a, float b, float x)
{
    float t = saturate((x - a)/(b - a));
    return t*t*(3.0 - (2.0*t));
}

能夠看出,當第三個參數小於第一個參數時,結果返回0;大於第二個參數時,結果返回1;否則進行平滑插值。

這個函數決定了圖像中平滑上升的區域。代碼中的系數決定,左右兩邊平滑上升(下降)區域的長度占總體高頻區域的25%。當fracTime全然大於_TimeOnDuration後,就進入低頻區,即輸出是0。


以下分析非均勻的噪聲閃爍模擬

主要代碼例如以下:

		float		noiseTime	= time *  (6.2831853f / _TimeOnDuration);
		float		noise			= sin(noiseTime) * (0.5f * cos(noiseTime * 0.6366f + 56.7272f) + 0.5f);
		float		noiseWave	= _NoiseAmount * noise + (1 - _NoiseAmount);

函數圖像如圖所看到的:

技術分享 技術分享

技術分享

從代碼看。噪聲模擬主要依靠一個正弦函數和余弦函數的乘積來實現。


Blinking GodRays Billboarded


最終到了最後了,真累。Blinking GodRays Billboarded和上一篇有相通的地方。它們都是blingbling的!

但差別在於。Billboarded的意思是它總是會面朝著觀察者的方向,它的網格事實上是一些平板(像廣告板一樣),可是因為它總是會依據我們的觀察方向來隨時旋轉,讓我們感覺它是立體的一樣。

Billboarded的行為就跟向日葵總是會朝著太陽一樣。它在ShadowGun中用於模擬水箱中的燈光效果。

技術分享


主要代碼例如以下:

	v2f vert (appdata_full v)
	{
		v2f 		o;
		
				
		#if 0
		// cheap view space billboarding
		float3	centerOffs		= float3(float(0.5).xx - v.color.rg,0) * v.texcoord1.xyy;
		float3	BBCenter		= v.vertex + centerOffs.xyz;	
		float3	viewPos			= mul(UNITY_MATRIX_MV,float4(BBCenter,1)) - centerOffs;
		#else
		
		float3	centerOffs		= float3(float(0.5).xx - v.color.rg,0) * v.texcoord1.xyy;
		float3	centerLocal	= v.vertex.xyz + centerOffs.xyz;
		float3	viewerLocal	= mul(_World2Object,float4(_WorldSpaceCameraPos,1));			
		float3	localDir			= viewerLocal - centerLocal;
				
		localDir[1] = lerp(0,localDir[1],_VerticalBillboarding);
		
		float		localDirLength=length(localDir);
		float3	rightLocal;
		float3	upLocal;
		
		CalcOrthonormalBasis(localDir / localDirLength,rightLocal,upLocal);

		float		distScale		= CalcDistScale(localDirLength) * v.color.a;		
		float3	BBNormal		= rightLocal * v.normal.x + upLocal * v.normal.y;
		float3	BBLocalPos	= centerLocal - (rightLocal * centerOffs.x + upLocal * centerOffs.y) + BBNormal * distScale;

		BBLocalPos += _ViewerOffset * localDir;

		#endif
		
		float		time 			= _Time.y + _BlinkingTimeOffsScale * v.color.b;				
		float		fracTime	= fmod(time,_TimeOnDuration + _TimeOffDuration);
		float		wave			= smoothstep(0,_TimeOnDuration * 0.25,fracTime)  * (1 - smoothstep(_TimeOnDuration * 0.75,_TimeOnDuration,fracTime));
		float		noiseTime	= time *  (6.2831853f / _TimeOnDuration);
		float		noise			= sin(noiseTime) * (0.5f * cos(noiseTime * 0.6366f + 56.7272f) + 0.5f);
		float		noiseWave	= _NoiseAmount * noise + (1 - _NoiseAmount);
	
		wave = _NoiseAmount < 0.01f ?

wave : noiseWave; wave += _Bias; o.uv = v.texcoord.xy; o.pos = mul(UNITY_MATRIX_MVP, float4(BBLocalPos,1)); o.color = CalcFadeOutFactor(localDirLength) * _Color * _Multiplier * wave; return o; }


盡管代碼好像多了非常多,但我們僅僅要從頂點位置和顏色雙方面入手就能夠理清它的思路。


我們先來看頂點顏色的計算

這一部分和上一個shader差點兒全然一樣。稍有不同的是,它把計算淡入淡出的工作封裝到了一個函數中CalcFadeOutFactor。對於上一個shader來說,CalcFadeOutFactor函數的輸入是在View Space中頂點的距離。但在這裏不能夠直接使用頂點的原始位置v.vertex。而是改動後的網格中心位置距離localDirLength


以下是最關鍵的頂點位置的計算。它的相關代碼例如以下:

		float3	centerOffs		= float3(float(0.5).xx - v.color.rg,0) * v.texcoord1.xyy;
		float3	centerLocal	= v.vertex.xyz + centerOffs.xyz;
		float3	viewerLocal	= mul(_World2Object,float4(_WorldSpaceCameraPos,1));			
		float3	localDir			= viewerLocal - centerLocal;
				
		localDir[1] = lerp(0,localDir[1],_VerticalBillboarding);
		
		float		localDirLength=length(localDir);
		float3	rightLocal;
		float3	upLocal;
		
		CalcOrthonormalBasis(localDir / localDirLength,rightLocal,upLocal);

		float		distScale		= CalcDistScale(localDirLength) * v.color.a;		
		float3	BBNormal		= rightLocal * v.normal.x + upLocal * v.normal.y;
		float3	BBLocalPos	= centerLocal - (rightLocal * centerOffs.x + upLocal * centerOffs.y) + BBNormal * distScale;

		BBLocalPos += _ViewerOffset * localDir;

		#endif
		o.pos	= mul(UNITY_MATRIX_MVP, float4(BBLocalPos,1));

當中須要註意的地方是,上面使用的坐標都是轉換到Object Space下的結果。上述代碼首先計算在Object Space下、網格中心到觀察點的方向向量localDir

有時。我們並不希望讓平板全然垂直與視角的觀察方向。而是僅想得到在XZ平面上的方向(想象一個僅僅能夠左右擺頭,但不能夠向上仰頭或向下低頭的向日葵)。因此,這裏能夠使用_VerticalBillboarding參數對這樣的垂直程度進行調整,_VerticalBillboarding為0表示全然舍棄Y方向上的信息,僅能夠左右擺頭;而_VerticalBillboarding為1則表示平板要全然垂直與觀察視角的方向。對於水箱這樣的精巧的對象,一般_VerticalBillboarding為0。

然後將其正則化後的結果作為CalcOrthonormalBasis的輸入。輸出的是該網格中心點面對視角的右手方向rightLocal和正上方向upLocal。這兩個方向決定了該shader像向日葵一樣的行為。

得到當前的右手方向和正上方向後。distScale和之前的shader一樣。相同是依據距離觀察點的距離來計算頂點的擴張程度的。BBNormal則是為了更新旋轉後頂點的法線方向,它的計算簡單明了,就是使用新的右手/正上方向又一次定義法線,這裏z方向是不須要考慮的。應該它就是個平板。

BBLocalPos一行,首先使用新的右手/正上方向還原該角度下該頂點的相應位置,然後再使用新的法線方向BBNormaldistScale進行擴張。最後。使用參數_ViewerOffsetBBLocalPos進行最後的偏移。_ViewerOffset表示將平板像視角方向進行偏移的量。一般設為0就可以。


寫在最後


這篇有點長,數學公式也非常多,我盡量用函數圖像來解釋了。

這裏總結一下上面的各種技術:

  • 首先這三篇shader都是為了模擬“偽光源”,使用的技術就是簡單網格+透明紋理。它們的frag函數都非常easy,僅僅是依據vert的輸出進行紋理採樣和顏色乘積。因此。它們的計算重點都在vert函數中。而vert函數也非常類似,都是對頂點位置和頂點顏色進行了相應的改動。

  • 這樣的技術效率比較高,但要預處理網格的頂點顏色、透明度等值。

    比如,在對頂點進行收縮和擴張時。須要使用頂點顏色的透明度來決定該點能否夠移動;在最後一篇計算網格中心點的位置時,每一個頂點相對於網格中心點的偏移也提前存儲在了頂點的顏色和紋理坐標中。

  • GodRays是當中簡單基礎的shader,它的效果就是使用一張透明紋理來模擬光照。並依據和視角的距離進行淡入淡出。



  • Blinking GodRays是當中應用最廣的shader。它的效果除了上面的淡入淡出外,還加上了閃爍的效果。閃爍的波形能夠選擇均勻的脈沖波,或者是有噪聲的波形。

  • Blinking GodRays Billboarded是當中最復雜的shader,它的效果除了淡入淡出+閃爍外。還能夠依據視角方向、實時讓網格面向視角。就像向日葵總是會面朝太陽一樣。

  • 以下對主要參數進行說明:
    _MainTex:用於模擬光照的透明紋理。
    _FadeOutDistNear:小於這個距離時,會出現淡出效果。在GodRays中,淡出的同一時候還會收縮頂點。
    _FadeOutDistFar:大於這個距離時,會出現淡出效果。

    GodRays中,淡出的同一時候還會收縮頂點。
    _Multiplier:光照顏色的乘數。能夠用來調亮/調暗最後的模擬光照。
    _Bias:模擬閃爍時。波形的偏移,能夠理解成把波形圖像向Y方向的移動量。
    _TimeOnDuration:模擬閃爍時。波形中高頻區域的長度。能夠理解為閃爍時亮著的時間。


    _TimeOffDuration:模擬閃爍時,波形中低頻區域的長度,能夠理解為閃爍時暗著的時間。
    _BlinkingTimeOffsScale:模擬閃爍時,指定閃爍在波形中的開始位置。


    _SizeGrowStartDist:大於這個距離時,會開始對頂點進行擴展。即從0開始增長。


    _SizeGrowEndDist:達到這個距離時,擴張達到最大程度,即擴展程度為1。
    _MaxGrowSize:擴張的最大大小。
    _NoiseAmount:模擬閃爍時。噪聲的程度。用於混合均勻的脈沖波和噪聲波。
    _VerticalBillboarding:在Blinking GodRays Billboarded中。平板的垂直程度。返回為(0, 1)。0表示不須要垂直與視角方向,僅僅在XZ平面旋轉。
    _ViewerOffset:在Blinking GodRays Billboarded中。將網格向視角方向移動的偏移量。
    _Color:用於改變光照顏色。



寫這一篇心好累。

。。


【Unity Shaders】ShadowGun系列之二——霧和體積光