Unity3D遊戲開發之在3D場景中選擇物體並顯示輪廓效果強化版

 在上一篇文章中,我們通過自定義着色器實現了一個簡單的在3D遊戲中選取、顯示物體輪廓的實例。更多精彩請關注【狗刨學習網】在文章最後,給大家留下了一個問題,就是我們的這種方法存在一定的問題,無法運用到複雜的模型上。原因是什麼呢?這要從這種方法的原理上來說,其實這種方法類似於攝像機的視角方向上對物體進行了一個投影。這樣的話,如果模型被其它物體遮擋的話,就會出現渲染不完全的問題,如圖所示,有一位朋友在評論中提出了這個問題。那麼,怎麼解決這個問題呢?對於遮擋的問題,我們一般採取的方法是拉近攝像機,因此,我們這裏依然採取這種方法,即如果被渲染的物體前面有遮擋物體,則將攝像機拉近,這樣就可以解決這個問題了。

       

       那麼解決了上一篇文章中的問題後,我們就來開始學習今天的內容——《Unity3D遊戲開發複雜模型的選取與輪廓高亮顯示》。首先,我們今天的內容是基於邊緣光(RimLight)的方法來實現的,在Unity3D的官方示例中,我們可以找到這個算法,其核心算法是:
  
  1. <font face="新宋體" size="2">half rim = 1.0 - saturate(dot (normalize(IN.viewDir), IN.worldNormal));  
  2. o.Emission = _RimColor.rgb * pow (rim, _RimPower);  </font>
複製代碼

其中,IN.viewDir是當前視角向量,IN.worldNormal是物體的法線。dot是計算視角和法線的點積,等於視角和法線夾角的cos值,如下圖:

          

       由於Cos的值域是1到0,所以1-cos就成了0到1,在夾角90度時達到最大值,正好用來模擬側光的強度(與視角成90度的部分光線最強,就是邊緣光了),把這個值的變化率用一個pow函數(rim的_rimPower次方)進行放大,就能強化邊緣發亮的效果。但是當物體表面比較平直時(例如立方體),由於各個面上的法向量都是一個方向上的,因此無法體現出變化和輪廓。此外,這種方法在描繪凹的幾何體時,凹的部分(包括法線貼圖造成的凹凸)的邊緣也都會被畫出來,並不是真正意義上的邊緣輪廓,就是一種側光效果。好了,下面我們開始具體地來講通過這種方法來實現物體輪廓高光顯示。首先編寫Shader:
  
  1. <font face="新宋體" size="2">Shader "Custom/Outline" {  
  2. Properties {  
  3.     _Color ("Main Color", Color) = (1,1,1,1)  
  4.     _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)  
  5.     _Shininess ("Shininess", Range (0.03, 1)) = 1  
  6.     _MainTex ("Base (RGB) Gloss (A)", 2D) = "black" {}  
  7.     _BumpMap ("Normalmap", 2D) = "bump" {}  
  8.     //邊緣光顏色  
  9.     _RimColor ("Rim Color", Color) = (0,0,0,0.0)  
  10.     //放大倍數  
  11.     _RimPower ("Rim Power", Range(0.5,8.0)) = 2.0  
  12. }  
  13. SubShader {   
  14.     Tags { "RenderType"="Opaque" }  
  15.     LOD 400  
  16.   
  17. CGPROGRAM  
  18. #pragma surface surf BlinnPhong  
  19. #pragma target 3.0  
  20.   
  21. sampler2D _MainTex;  
  22. sampler2D _BumpMap;  
  23. fixed4 _Color;  
  24. half _Shininess;  
  25. float4 _RimColor;  
  26. float _RimPower;  
  27.   
  28. struct Input {  
  29.     float2 uv_MainTex;  
  30.     float2 uv_BumpMap;  
  31.     float3 viewDir;  
  32. };  
  33.   
  34. void surf (Input IN, inout SurfaceOutput o) {  
  35.       
  36.     fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);  
  37.     o.Albedo = tex.rgb * _Color.rgb;  
  38.     o.Gloss = tex.a;  
  39.     o.Alpha = tex.a * _Color.a;  
  40.     o.Specular = _Shininess;  
  41.     o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));  
  42.     //核心算法  
  43.     half rim = 1 - saturate(dot (normalize(IN.viewDir), o.Normal));  
  44.     o.Emission = _RimColor.rgb * pow (rim, _RimPower);  
  45. }  
  46. ENDCG  
  47.       
  48. }  
  49.   
  50. FallBack "Specular"  
  51. }</font>
複製代碼

      在這個着色器腳本中,最重要的一個屬性是_RimColor,這個值將決定我們最終渲染的效果。在上一篇文章中,我們是通過一個使用了自定義着色器的材質來實現輪廓顯示的,今天我們換一種方法,怎麼做呢?我們這裏通過Shader來實現,在Material中有一個Shader屬性,一旦改變了該屬性的值,那麼所有材質都將按照新的渲染方式進行渲染。我們在上一篇文章中的腳本的基礎上,擴展得到下面的腳本:

  1. <font face="新宋體" size="2">using UnityEngine;  
  2. using System.Collections;  
  3. public class ShowBoundry : MonoBehaviour {  
  4.     //使用顯示輪廓的簡單材質  
  5.     public Material mSimpleMat;  
  6.     //默認材質  
  7.     public Material mDefaultMat;  
  8.     //我們今天使用Shader來直接改變模型的渲染效果,這樣可以避免使用一個材質  
  9.     public Shader RimLightShader;  
  10.     public Color RimColor = new Color(0.2F,0.8F,10.6F,1);  
  11.     //定義私有變量以存儲模型的原始信息  
  12.     private SkinnedMeshRenderer mSkin;  
  13.     private Color mColor;  
  14.     private Shader mShader;  
  15.     void Start ()   
  16.     {
  17.         //獲取模型的SkinnedMeshRenderer  
  18.         mSkin=GameObject.Find("Person").  
  19.             GetComponentInChildren();
  20.         //獲取默認顏色  
  21.         mColor=mSkin.material.color;
  22.         //獲取默認Shader  
  23.         mShader=mSkin.material.shader;
  24.     }
  25.     void Update ()   
  26.     {
  27.        //獲取鼠標位置  
  28.        Vector3 mPos=Input.mousePosition;
  29.        //向物體發射射線  
  30.        Ray mRay=Camera.main.ScreenPointToRay(Input.mousePosition);
  31.        RaycastHit mHit;
  32.        //射線檢驗  
  33.        if(Physics.Raycast(mRay,out mHit))  
  34.        {
  35.           //Cube  
  36.           if(mHit.collider.gameObject.tag=="Cube")  
  37.           {
  38.              //將當前選中的對象材質換成帶輪廓線的材質  
  39.              mHit.collider.gameObject.renderer.material=mSimpleMat;
  40.              //將未選中的對象材質換成默認材質  
  41.              GameObject.Find("Sphere").renderer.material=mDefaultMat;  
  42.              //將模型恢復到初始狀態  
  43.              mSkin.material.shader=mShader;
  44.              mSkin.material.SetColor("_RimColor",mColor);  
  45.              //設置提示信息  
  46.              GameObject.Find("GUIText").guiText.text="當前選擇的對象是:Cube";  
  47.           }
  48.           //Sphere  
  49.           if(mHit.collider.gameObject.tag=="Sphere")  
  50.           {
  51.              //將當前選中的對象材質換成帶輪廓線的材質  
  52.              mHit.collider.gameObject.renderer.material=mSimpleMat;
  53.              //將未選中的對象材質換成默認材質  
  54.              GameObject.Find("Cube").renderer.material=mDefaultMat;  
  55.              //將模型恢復到初始狀態  
  56.              mSkin.material.shader=mShader;
  57.              mSkin.material.SetColor("_RimColor",mColor);  
  58.              //設置提示信息  
  59.              GameObject.Find("GUIText").guiText.text="當前選擇的對象是:Sphere";  
  60.           }
  61.           //Person  
  62.           if(mHit.collider.gameObject.tag=="Person")  
  63.           {
  64.              //更換Shader  
  65.              mSkin.material.shader=RimLightShader;
  66.              mSkin.material.SetColor("_RimColor",RimColor);  
  67.              //將未選中的對象材質換成默認材質  
  68.              GameObject.Find("Cube").renderer.material=mDefaultMat;  
  69.              GameObject.Find("Sphere").renderer.material=mDefaultMat;  
  70.              //設置提示信息  
  71.              GameObject.Find("GUIText").guiText.text="當前選擇的對象是:Person";  
  72.           }
  73.        }
  74.     }
  75. }</font>
複製代碼


       在今天的腳本中,我們增加了一個RimLightShader和RimColor,它們的作用是指定着色器和邊緣光的顏色,我們可以通過外部來引用我們剛纔定義好的自定義Shader,同時,爲了保存模型的原始狀態,我們定義了兩個私有變量mShader和mColor,以便我們可以在適當的時候將模型的狀態還原到原始狀態。好了,我們來運行下今天的程序,效果如下:

20140524222959265.gif (819.82 KB, 下載次數: 0)

下載附件  保存到相冊

6 小時前 上傳


      可以注意到,當角色被選中時,角色以高亮顯示的形式被渲染出來,這裏使用的是金黃色的邊緣光,所以得到了這樣的結果。感覺效果還不錯啊。在做今天的內容的時候,經常出現着色器無效的情況,後來發現是着色器定義的名稱和文件名稱不符的緣故,所以大家在編寫着色器的時候一定要注意啊。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章