[Unity3D]Unity3D遊戲開發之複雜模型的選取與輪廓高亮顯示

來源:互聯網
上載者:User

       大家好,我是秦元培,歡迎大家關注我的部落格,我的部落格地址是blog.csdn.net/qinyuanpei。

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


       那麼解決了上一篇文章中的問題後,我們就來開始學習今天的內容——《Unity3D遊戲開發複雜模型的選取與輪廓高亮顯示》。首先,我們今天的內容是基於邊緣光(RimLight)的方法來實現的,在Unity3D的官方樣本中,我們可以找到這個演算法,其核心演算法是:

half rim = 1.0 - saturate(dot (normalize(IN.viewDir), IN.worldNormal));o.Emission = _RimColor.rgb * pow (rim, _RimPower);
其中,IN.viewDir是當前視角向量,IN.worldNormal是物體的法線。dot是計算視角和法線的點積,等於視角和法線夾角的cos值,如:


       由於Cos的範圍是1到0,所以1-cos就成了0到1,在夾角90度時達到最大值,正好用來類比側光的強度(與視角成90度的部分光線最強,就是邊緣光了),把這個值的變動率用一個pow函數(rim的_rimPower次方)進行放大,就能強化邊緣發亮的效果。但是當物體表面比較平直時(例如立方體),由於各個面上的法向量都是一個方向上的,因此無法體現出變化和輪廓。此外,這種方法在描繪凹的幾何體時,凹的部分(包括法線貼圖造成的凹凸)的邊緣也都會被畫出來,並不是真正意義上的邊緣輪廓,就是一種側光效果。這一部分,博主並不是很懂,因為博主並沒有系統的學習過電腦圖形學的東西,如果大家想瞭解更多的內容,可以參考這裡:http://game.ceeger.com/forum/read.php?tid=3592。好了,下面我們開始具體地來講通過這種方法來實現物體輪廓高光顯示。首先編寫Shader:

Shader "Custom/Outline" {Properties {_Color ("Main Color", Color) = (1,1,1,1)_SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)_Shininess ("Shininess", Range (0.03, 1)) = 1_MainTex ("Base (RGB) Gloss (A)", 2D) = "black" {}_BumpMap ("Normalmap", 2D) = "bump" {}//邊緣光顏色_RimColor ("Rim Color", Color) = (0,0,0,0.0)//放大倍數    _RimPower ("Rim Power", Range(0.5,8.0)) = 2.0}SubShader { Tags { "RenderType"="Opaque" }LOD 400CGPROGRAM#pragma surface surf BlinnPhong#pragma target 3.0sampler2D _MainTex;sampler2D _BumpMap;fixed4 _Color;half _Shininess;float4 _RimColor;float _RimPower;struct Input {float2 uv_MainTex;float2 uv_BumpMap;float3 viewDir;};void surf (Input IN, inout SurfaceOutput o) {fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);o.Albedo = tex.rgb * _Color.rgb;o.Gloss = tex.a;o.Alpha = tex.a * _Color.a;o.Specular = _Shininess;o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));//核心演算法half rim = 1 - saturate(dot (normalize(IN.viewDir), o.Normal));    o.Emission = _RimColor.rgb * pow (rim, _RimPower);}ENDCG}FallBack "Specular"}
      在這個著色器指令碼中,最重要的一個屬性是_RimColor,這個值將決定我們最終渲染的效果。在上一篇文章中,我們是通過一個使用了自訂著色器的材質來實現輪廓顯示的,今天我們換一種方法,怎麼做呢?我們這裡通過Shader來實現,在Material中有一個Shader屬性,一旦改變了該屬性的值,那麼所有材質都將按照新的渲染方式進行渲染。我們在上一篇文章中的指令碼的基礎上,擴充得到下面的指令碼:

using UnityEngine;using System.Collections;public class ShowBoundry : MonoBehaviour {//使用顯示輪廓的簡單材質public Material mSimpleMat;//預設材質public Material mDefaultMat;//我們今天使用Shader來直接改變模型的渲染效果,這樣可以避免使用一個材質public Shader RimLightShader;public Color RimColor = new Color(0.2F,0.8F,10.6F,1);//定義私人變數以儲存模型的原始資訊private SkinnedMeshRenderer mSkin;private Color mColor;private Shader mShader;void Start () {//擷取模型的SkinnedMeshRenderermSkin=GameObject.Find("Person").GetComponentInChildren<SkinnedMeshRenderer>();//擷取預設顏色mColor=mSkin.material.color;//擷取預設ShadermShader=mSkin.material.shader;}void Update () {   //擷取滑鼠位置   Vector3 mPos=Input.mousePosition;   //向物體發射射線   Ray mRay=Camera.main.ScreenPointToRay(Input.mousePosition);   RaycastHit mHit;   //射線檢驗   if(Physics.Raycast(mRay,out mHit))   {  //Cube  if(mHit.collider.gameObject.tag=="Cube")  { //將當前選中的對象材質換成帶輪廓線的材質 mHit.collider.gameObject.renderer.material=mSimpleMat; //將未選中的對象材質換成預設材質 GameObject.Find("Sphere").renderer.material=mDefaultMat; //將模型恢複到初始狀態 mSkin.material.shader=mShader;     mSkin.material.SetColor("_RimColor",mColor); //設定提示資訊 GameObject.Find("GUIText").guiText.text="當前選擇的對象是:Cube";  }  //Sphere  if(mHit.collider.gameObject.tag=="Sphere")  { //將當前選中的對象材質換成帶輪廓線的材質 mHit.collider.gameObject.renderer.material=mSimpleMat; //將未選中的對象材質換成預設材質 GameObject.Find("Cube").renderer.material=mDefaultMat; //將模型恢複到初始狀態 mSkin.material.shader=mShader; mSkin.material.SetColor("_RimColor",mColor); //設定提示資訊 GameObject.Find("GUIText").guiText.text="當前選擇的對象是:Sphere";  }  //Person  if(mHit.collider.gameObject.tag=="Person")  { //更換Shader     mSkin.material.shader=RimLightShader; mSkin.material.SetColor("_RimColor",RimColor); //將未選中的對象材質換成預設材質 GameObject.Find("Cube").renderer.material=mDefaultMat; GameObject.Find("Sphere").renderer.material=mDefaultMat; //設定提示資訊 GameObject.Find("GUIText").guiText.text="當前選擇的對象是:Person";  }   }}}
       在今天的指令碼中,我們增加了一個RimLightShader和RimColor,它們的作用是指定著色器和邊緣光的顏色,我們可以通過外部來引用我們剛才定義好的自訂Shader,同時,為了儲存模型的原始狀態,我們定義了兩個私人變數mShader和mColor,以便我們可以在適當的時候將模型的狀態還原到原始狀態。好了,我們來運行下今天的程式,效果如下:



       可以注意到,當角色被選中時,角色以高亮顯示的形式被渲染出來,博主這裡使用的是金黃色的邊緣光,所以得到了這樣的結果。感覺效果還不錯啊。博主在做今天的內容的時候,經常出現著色器無效的情況,後來發現是著色器定義的名稱和檔案名稱不符的緣故,所以大家在編寫著色器的時候一定要注意啊。

      好了,今天的內容就是這樣了,希望大家喜歡啊。呵呵。

     

     每日箴言:曾經以為生命中最糟糕的事,就是孤獨終老。其實不是。最糟糕的是與那些讓你感到孤獨的人一起終老。




    喜歡我的部落格請記住我的名字:秦元培,我的部落格地址是blog.csdn.net/qinyuanpei

    轉載請註明出處,本文作者:秦元培,本文出處:http://blog.csdn.net/qinyuanpei/article/details/26862743



.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.