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