1. 程式人生 > >【Unity Shader】手遊中高光效果的幾種實現方法

【Unity Shader】手遊中高光效果的幾種實現方法

前言

        由於手機裝置的效能限制,很多效果的計算都得精簡和優化才能達到目地。而在Unity中的高光效果,也給予了多種不同的方案,有用於主機的,用於手機的,有限制一盞畫素燈的,開發者可以根據不同的情況酌情選擇。Unity的Surface Shader提供了很好的封裝性,複雜的燈光處理都不需要開發者去關注,也有提供修改光照函式的介面。但是由於專案特殊性,往往這麼簡單方便的事情會阻礙使用者的自由發揮。所以我們在很多優秀的遊戲作品中都能看到很多自研發Shader都是基於Vert&Frag的,也因此讓我們看到很多不同的實現原理和方式(Unity大法好)。對於次世代遊戲來說,光是必不可少的,尤其是高光和法線效果。在這裡,就介紹在手游上能使用的幾種實現方法,並且採用Vert&Frag Shader。

方案

        高光效果跟法線,視向,光向都脫不開干係,主要是如何利用它們和如何組織計算。本文大致分為如下幾種方式實現:

  • 在頂點函式,世界空間中計算
  • 在頂點函式,切線空間中計算
  • 在片元函式,世界空間中計算
  • 在片元函式,切線空間中計算

分析

  • 在頂點和在片元函式中處理是效率和效果都是不一樣的,由於在頂點函式處理是逐頂點計算的,所以是比較快的,但是計算不精準;而在片元中是逐畫素計算,所以效率較慢(雖然慢不了多少,但是積少成多),計算更精準。
  • 在世界空間和在切線空間中計算也是得出不一樣的效果,在切線空間下效果更加細膩平滑,效果也更好。

實現

  • 在頂點函式,世界空間中計算:
    Shader "Yogi/Specular(Vert-World)"
    {
    	Properties
    	{
    		_MainTex("Base(RGB) Gloss(A)", 2D) = "white" {}
    		_Specular("Specular", Range(0, 10)) = 1
    		_Shininess("Shininess", Range(0.01, 1)) = 0.5
    	}
    
    	SubShader
    	{
    		Pass
    		{
    			CGPROGRAM
    			#include "UnityCG.cginc"
    			#include "Lighting.cginc"
    			#pragma vertex vert
    			#pragma fragment frag
    			#pragma fragmentoption ARB_precision_hint_fastest
    			#pragma multi_compile_fwdbase
    
    			sampler2D _MainTex;
    			fixed4 _MainTex_ST;
    			fixed _Specular;
    			fixed _Shininess;
    
    			struct a2v
    			{
    				fixed4 vertex : POSITION;
    				fixed3 normal : NORMAL;
    				fixed4 texcoord : TEXCOORD0;
    			};
    
    			struct v2f
    			{
    				fixed4 pos : SV_POSITION;
    				fixed2 uv : TEXCOORD0;
    				fixed3 spec : TEXCOORD1;
    			};
    
    			v2f vert(a2v v)
    			{
    				v2f o;
    				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    
    				fixed3 normal = mul((fixed3x3)_Object2World, SCALED_NORMAL);
    				fixed3 viewDir = normalize(WorldSpaceViewDir(v.vertex));
    				fixed3 lightDir = normalize(WorldSpaceLightDir(v.vertex));
    				fixed nh = saturate(dot(normal, normalize(viewDir + lightDir)));
    
    				o.spec = _LightColor0.rgb * pow(nh, _Shininess * 128) * _Specular;
    
    				return o;
    			}
    
    			fixed4 frag(v2f i) : SV_Target
    			{
    				fixed4 c = tex2D(_MainTex, i.uv.xy);
    				fixed4 o = c;
    
    				o.rgb += i.spec * c.a;
    
    				return o;
    			}
    
    			ENDCG
    		}
    	}
    
    	FallBack "Mobile/Diffuse"
    }

  • 在頂點函式,切線空間中計算:
    Shader "Yogi/Specular(Vert-Tangent)"
    {
    	Properties
    	{
    		_MainTex("Base(RGB) Gloss(A)", 2D) = "white" {}
    		_Specular("Specular", Range(0, 10)) = 1
    		_Shininess("Shininess", Range(0.01, 1)) = 0.5
    	}
    
    	SubShader
    	{
    		Pass
    		{
    			CGPROGRAM
    			#include "UnityCG.cginc"
    			#include "Lighting.cginc"
    			#pragma vertex vert
    			#pragma fragment frag
    			#pragma fragmentoption ARB_precision_hint_fastest
    			#pragma multi_compile_fwdbase
    
    			sampler2D _MainTex;
    			fixed4 _MainTex_ST;
    			fixed _Specular;
    			fixed _Shininess;
    
    			struct a2v
    			{
    				fixed4 vertex : POSITION;
    				fixed3 normal : NORMAL;
    				fixed4 tangent : TANGENT;
    				fixed4 texcoord : TEXCOORD0;
    			};
    
    			struct v2f
    			{
    				fixed4 pos : SV_POSITION;
    				fixed2 uv : TEXCOORD0;
    				fixed3 spec : TEXCOORD1;
    			};
    
    			v2f vert(a2v v)
    			{
    				v2f o;
    				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    
    				TANGENT_SPACE_ROTATION;
    				fixed3 normal = mul(rotation, SCALED_NORMAL);
    				float3 viewDir = normalize(mul(rotation, ObjSpaceViewDir(v.vertex)));
    				float3 lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
    				fixed nh = saturate(dot(normal, normalize(viewDir + lightDir)));
    
    				o.spec = _LightColor0.rgb * pow(nh, _Shininess * 128) * _Specular;
    
    				return o;
    			}
    
    			fixed4 frag(v2f i) : SV_Target
    			{
    				fixed4 c = tex2D(_MainTex, i.uv.xy);
    				fixed4 o = c;
    
    				o.rgb += i.spec * c.a;
    
    				return o;
    			}
    
    			ENDCG
    		}
    	}
    
    	FallBack "Mobile/Diffuse"
    }

  • 在片元函式,世界空間中計算:
    Shader "Yogi/Specular(Frag-World)"
    {
    	Properties
    	{
    		_MainTex("Base(RGB) Gloss(A)", 2D) = "white" {}
    		_Specular("Specular", Range(0, 10)) = 1
    		_Shininess("Shininess", Range(0.01, 1)) = 0.5
    	}
    
    	SubShader
    	{
    		Pass
    		{
    			CGPROGRAM
    			#include "UnityCG.cginc"
    			#include "Lighting.cginc"
    			#pragma vertex vert
    			#pragma fragment frag
    			#pragma fragmentoption ARB_precision_hint_fastest
    			#pragma multi_compile_fwdbase
    
    			sampler2D _MainTex;
    			fixed4 _MainTex_ST;
    			fixed _Specular;
    			fixed _Shininess;
    
    			struct a2v
    			{
    				fixed4 vertex : POSITION;
    				fixed3 normal : NORMAL;
    				fixed4 texcoord : TEXCOORD0;
    			};
    
    			struct v2f
    			{
    				fixed4 pos : SV_POSITION;
    				fixed2 uv : TEXCOORD0;
    				fixed3 normal : TEXCOORD1;
    				fixed3 viewDir : TEXCOORD2;
    				fixed3 lightDir : TEXCOORD3;
    			};
    
    			v2f vert(a2v v)
    			{
    				v2f o;
    				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    
    				o.normal = mul((float3x3)_Object2World, SCALED_NORMAL);
    				o.viewDir = normalize(WorldSpaceViewDir(v.vertex));
    				o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
    
    				return o;
    			}
    
    			fixed4 frag(v2f i) : SV_Target
    			{
    				fixed4 c = tex2D(_MainTex, i.uv.xy);
    				fixed4 o = c;
    
    				fixed nh = saturate(dot(i.normal, normalize(i.viewDir + i.lightDir)));
    				fixed3 spec = _LightColor0.rgb * pow(nh, _Shininess * 128) * _Specular;
    				o.rgb += spec * c.a;
    
    				return o;
    			}
    
    			ENDCG
    		}
    	}
    
    	FallBack "Mobile/Diffuse"
    }

  • 在片元函式,切線空間中計算:
    Shader "Yogi/Specular(Frag-Tangent)"
    {
    	Properties
    	{
    		_MainTex("Base(RGB) Gloss(A)", 2D) = "white" {}
    		_Specular("Specular", Range(0, 10)) = 1
    		_Shininess("Shininess", Range(0.01, 1)) = 0.5
    	}
    
    	SubShader
    	{
    		Pass
    		{
    			CGPROGRAM
    			#include "UnityCG.cginc"
    			#include "Lighting.cginc"
    			#pragma vertex vert
    			#pragma fragment frag
    			#pragma fragmentoption ARB_precision_hint_fastest
    			#pragma multi_compile_fwdbase
    
    			sampler2D _MainTex;
    			fixed4 _MainTex_ST;
    			fixed _Specular;
    			fixed _Shininess;
    
    			struct a2v
    			{
    				fixed4 vertex : POSITION;
    				fixed3 normal : NORMAL;
    				fixed4 tangent : TANGENT;
    				fixed4 texcoord : TEXCOORD0;
    			};
    
    			struct v2f
    			{
    				fixed4 pos : SV_POSITION;
    				fixed2 uv : TEXCOORD0;
    				fixed3 normal : TEXCOORD1;
    				fixed3 viewDir : TEXCOORD2;
    				fixed3 lightDir : TEXCOORD3;
    			};
    
    			v2f vert(a2v v)
    			{
    				v2f o;
    				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    
    				TANGENT_SPACE_ROTATION;
    				o.normal = mul(rotation, v.normal);
    				o.viewDir = normalize(mul(rotation, ObjSpaceViewDir(v.vertex)));
    				o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
    
    				return o;
    			}
    
    			fixed4 frag(v2f i) : SV_Target
    			{
    				fixed4 c = tex2D(_MainTex, i.uv.xy);
    				fixed4 o = c;
    
    				fixed nh = saturate(dot(i.normal, normalize(i.viewDir + i.lightDir)));
    				fixed3 spec = _LightColor0.rgb * pow(nh, _Shininess * 128) * _Specular;
    				o.rgb += spec * c.a;
    
    				return o;
    			}
    
    			ENDCG
    		}
    	}
    
    	FallBack "Mobile/Diffuse"
    }

  • 四個高光效果對比:

最後

        開發者可以根據不同的手機配置使用不同的Shader,或者做多個SubShader,通過設定ShaderLod切換。

相關推薦

Unity Shader中高效果實現方法

前言         由於手機裝置的效能限制,很多效果的計算都得精簡和優化才能達到目地。而在Unity中的高光效果,也給予了多種不同的方案,有用於主機的,用於手機的,有限制一盞畫素燈的,開發者可以根據

Unity Shader(五) ------ 透明效果之半透明效果實現及原理

pic sele 不同的 %20 分享圖片 渲染 select fall 就是 筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題 【Unity Shader學習筆記

Unity Shader--入門知識點

一個 個數 精確 option cas 反射 性能 hit nor 著色器聲明(“名字”)Shader "ShaderDiffuseExample" { 一、屬性定義(作用:外部傳入參數) 屬性定義語法:PropName("DisplayName",PropType) =

Unity Shader--- 準確認識SubShader語義塊結構、渲染狀態設定、Tags標簽

strong blend 渲染引擎 引擎 語法 always 加載 setup 使用 一【SubShader】   每個UnityShader文件可以包含多個SubShader語義塊,但至少要有一個。當Unity需要加載這個UnityShader時,Unity會掃描所有的S

Unity Shader---基礎光照

【Unity Shader】---基礎光照

Unity Shader(四) ------ 紋理之法線紋理、單張紋理及遮罩紋理的實現

相對 ctx mali 通過 dir con 真的 dpi spa 筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題。 【Unity Shader】(三) ---

Unity Shader(八) ------ 高階紋理之立方體紋理及光線反射、折射的實現

筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題。      【Unity Shader】(三) ------ 光照模型原理及漫反射和

Unity Shader(八) ------ 高級紋理之立方體紋理及光線反射、折射的實現

int rap 原理 src 靜態 toc lighting ati orm 筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題。 【Unity Shade

Unity Shader(八) ------ 高級紋理(上)

向量 sdn 定義 紋理 天空 get main .com 而是 筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題。 【Unity Shader】(三) -

用程式碼來畫畫 —— Ray-Marching(光線步進)Unity Shader

參考自: http://blog.csdn.net/baidu_26153715/article/details/46510703 http://imgtec.eetrend.com/blog/8845 http://ogldev.atspace.co.uk/www/tutorial1

Unity Shader(九) ------ 高級紋理之渲染紋理及鏡子與玻璃效果實現

vertex 觀察 cli turn src nor sample tar opaque 筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題。 【Unity Shad

Unity Shader在後處理階段使用模板

美式漫畫風格的邊緣描邊,是基於影象檢測的全屏後處理手段,做法是在 OnRenderImage 方法中處理。然後需要做到部分不描邊,這個也可以用來分來做描邊,很自然的想到使用模板來分開處理,但是 OnRenderImage 中按 unity 論壇中有人說的是 stencil 這

Unity Shader 消融效果實現

1.前言 參加騰訊2018遊戲崗校招結果出師未捷身先死,連面試機會都沒有(-_-||),想想筆試自己三道程式設計題0個ac也就釋懷了233,忙著實習實在沒精力複習演算法題,精力有限啊... 吐槽完畢迴歸主題 咱最近在玩wy的神都夜行錄,這款手遊畫面還是挺不錯的,就是

Unity Shader(六) ------ 複雜的光照(上)

 筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題。 目錄 前言  三 .實踐 四 . 總結 前言 本文探討的是場景中存在多種光源時的渲染情況,在本

Unity Shader(七) ------ 複雜的光照(下)

筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題。 目錄 前言 二. 陰影 四. 總結 前言 本文承接上文【Unity Shader】(六) ------ 複雜的光照(上),

Unity Shader(八) ------ 高階紋理(上)

筆者使用的是 Unity 2018.2.0f2 + VS2017,建議讀者使用與 Unity 2018 相近的版本,避免一些因為版本不一致而出現的問題。 前言 關於紋理,之前在 【Unity Shader】(四) ------ 紋理之法線紋理、單張紋理及遮罩紋理的實現 已經解釋過相關原理,不過那些是屬

Unity Shader簡單積雪效果實現

1.前言 公司的專案進入真機除錯階段,體驗了一個月的996模式的正式結束,放假第一天來寫篇部落格  2.實現思路 1.積雪的實現 一般由模型的紋理貼圖和一張積雪的紋理圖混合而成。 //2個取樣結果的差值(1.模型紋理,2.積雪紋理/顏色) color.rgb =

Unity Shader(九) ------ 高階紋理之渲染紋理及鏡子與玻璃效果實現

一. 渲染紋理 渲染紋理是本文的重點介紹物件。如果你使用過 RenderTexture 來實現一些特殊的效果,那麼你會更能理解本文的內容。 1.1 什麼是渲染紋理 在筆者以前的博文中介紹了許多概念,其中大多提到了 緩衝(buffer)這個名詞 ,在之前我們

Unity Shader搖擺的小草——頂點動畫

Shader 動畫的主要點在座標變換,程式碼: Shader "Custom/Grass" { Properties { _MainTex ("Grass Texture", 2D) = "w

Unity Shaderunity海邊波浪效果實現

效果圖如下(GIF因為為了把圖壓小所以刪掉了一些幀導致後面速度突然很快,實際效果並不是這樣~_~)之前在玩很多遊戲的時候,注意到裡面的海水和陸地相交接的地方會產生海浪,比如《海島奇兵》,以及水面會出現一個透明漸隱的過度,而不會在水面和陸地的交界處產生硬切邊。其中海浪的效果考慮