Unity3D遊戲製作(二)——如何渲染3D角色

本系列文章由 Amazonzx 編寫,歡迎轉載,轉載請註明出處。

http://blog.csdn.net/amazonzx/article/details/7935341


本文主要介紹一下如何利用Shader來渲染遊戲中的3D角色,以及如何利用Unity提供的Surface Shader來書寫自定義Shader。


一、       從Shader開始

1、通過Assets->Create->Shader來創建一個默認的Shader,並取名“MyShader”。


2、將MyShader打開即可看見Unity默認的Shader代碼

Shader "Custom/MyShader" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert

		sampler2D _MainTex;

		struct Input {
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D (_MainTex, IN.uv_MainTex);
			o.Albedo = c.rgb;
			o.Alpha = c.a;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}

3、將該Shader賦給一個角色,就可以看到該Shader所能表達出的Diffuse渲染效果。



4、接下來我們將以此默認Shader作爲藍本,編寫出自定義的Shader。另外,該Shader所用到的參數,我們將在下一章節進行說明。


二、       實現多種自定義渲染效果

1、  BumpMap效果

如果想實現Bump Map效果,可對上述的Shader做如下修改:

1.1  在屬性Properties中加入:

Properties {
	_MainTex ("Base (RGB)", 2D) = "white" {}
	_BumpMap("Bumpmap", 2D) = "bump" {}
}

1.2  在SubShader的變量中也進行相應修改:

sampler2D _MainTex;
sampler2D _BumpMap;

struct Input {
	float2 uv_MainTex;
	float2 uv_BumpMap;	
};

1.3  最後修改surf函數,加入對Normal分量的計算:

void surf (Input IN, inout SurfaceOutput o) {
	half4 c = tex2D (_MainTex, IN.uv_MainTex);
	o.Albedo = c.rgb;
	o.Alpha = c.a;
	o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
}

這樣,角色的材質部分即可變爲如下形式(暫定BumpMap的Shader名爲“MyShader1”):


然後,根據Base圖來創建其Normal Map圖,並拖入到BumpMap中即可。BumpMap的效果顯示如下:

說明:

(1)首先是title的解釋

Shader "Custom/MyShader1" 
這種表示表明了該Shader在編輯器中的顯示位置,例如我們可在如下地方找到該Shader。



(2)其次是Properties

Properties {
	_MainTex ("Base (RGB)", 2D) = "white" {}
	_BumpMap("Bumpmap", 2D) = "bump" {}
}

Properties可通過如下語義進行聲明:

name ("displayname", property type) = default value

l  “name” 是與Shader腳本中對應的名字

l  “display name”是在材質視圖中所顯示的名字

l  “propertytype”是指該property的類型,一般可有如下幾種類型:Range,Color,2D,Rect,Cube,Float和Vector

l  “defaultvalue”是指該property的默認值

這裏需要注意的是,如果你在Properties中加入了新的屬性,那麼你需要在CGPROGRAM中的SubShader中加入同樣名字的參數。


(3)接下來是“LOD”語義詞的解釋。

這裏的“LOD”主要是指Shader的LOD程度,即對於超出該範圍的物體將不再通過該Shader進行渲染,具體的Shader LOD說明可以參見:http://blog.csdn.net/amazonzx/article/details/7614399


(4)我們在SubShader中還加入了

sampler2D _BumpMap;
float2 uv_BumpMap;
			

其中,_BumpMap是爲了關聯Properties中的_BumpMap屬性。

而uv_BumpMap,是爲了獲取BumpMap圖中的uv座標。


(5)最後,我們在surf函數中獲取每個頂點的紋理信息以及法線信息,這些信息將被應用於接下來的Vertex Fragment和Pixel Fragment。

void surf (Input IN, inout SurfaceOutput o) {
	half4 c = tex2D (_MainTex, IN.uv_MainTex);
	o.Albedo = c.rgb;
	o.Alpha = c.a;
	o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
}

其中,tex2D函數可以讀取紋理_MainTex中的IN.uv_MainTex座標位置的像素顏色值。

Albedo和Alpha分別獲取該像素的RGB值和Alpha值,其中“Albedo”是一個漫反射參數,它表示一個表面的漫反射能力,即一個表面上出射光強與入射光強的比值。具體介紹可見:http://en.wikipedia.org/wiki/Albedo


2、  Blinn-Phong效果

如果想實現Blinn-Phong效果,可對上述的Shader做如下修改:

2.1  在屬性Properties中加入:

_AmbientColor ("Ambient Color", Color) = (0.1, 0.1, 0.1, 1.0)
_SpecularColor ("Specular Color", Color) = (0.12, 0.31, 0.47, 1.0)
_Glossiness ("Gloss", Range(1.0,512.0)) = 80.0


2.2  在SubShader的變量中也加入相應修改:

fixed4 _AmbientColor;
fixed4 _SpecularColor;
half _Glossiness;


2.3  最後修改surf函數,進行如下修改:

fixed4 c = tex2D (_MainTex, IN.uv_MainTex);

這裏將原有的half4替換爲fixed4,這樣做是爲了提高渲染的性能,因爲fixed的精度較之half要低,更高的精度意味着更大的計算量,而這裏fixed的精度已經足夠,所以使用fixed替代half4,從而來降低計算消耗,增加渲染性能。


2.4  將“#pragma surface surf Lamber”改成“#pragma surfacesurf CustomBlinnPhong”,同時加入與其對應的LightingCustomBlinnPhong函數來計算頂點光照。

inline fixed4 LightingCustomBlinnPhong (SurfaceOutput s, fixed3 lightDir, fixed3 viewDir, fixed atten) 
{
	fixed3 ambient = s.Albedo * _AmbientColor.rgb;

	fixed NdotL = saturate(dot (s.Normal, lightDir)); 
	fixed3 diffuse = s.Albedo * _LightColor0.rgb * NdotL;
			
	fixed3 h = normalize (lightDir + viewDir); 
	float nh = saturate(dot (s.Normal, h)); 
	float specPower = pow (nh, _Glossiness);
	fixed3 specular = _LightColor0.rgb * specPower * _SpecularColor.rgb;

	fixed4 c;
	c.rgb = (ambient + diffuse + specular) * (atten * 2);
	c.a = s.Alpha + (_LightColor0.a * _SpecularColor.a * specPower * atten);
	return c;
}

該函數的名稱爲什麼不是“CustomBlinnPhong”呢?這是因爲該函數雖然是由“#pragma surface surf CustomBlinnPhong”來調用,但是爲了讓該函數可以正常工作,我們需要在其名稱前加入“Lighting”關鍵字,這樣Unity才能識別出這是一個自定義的光照函數。

 

通過以上設置,角色的材質部分即可變爲如下形式(暫定該Shader名爲“MyShader2”):


其顯示效果如下:



3、  邊緣光照(Rim Light)和卡通渲染(Toon Shading)

可以通過對上述Shader做以下改進,來達到這種效果:

3.1  在屬性Properties中加入:

_RimColor ("Rim Color", Color) = (0.12, 0.31, 0.47, 1.0)
_RimPower ("Rim Power", Range(0.5, 8.0)) = 3.0
_Ramp ("Shading Ramp", 2D) = "gray" {}

3.2  在SubShader的變量中也加入相應修改:

sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _Ramp;
		
fixed4 _AmbientColor;
fixed4 _SpecularColor;
half _Glossiness;

fixed4 _RimColor;
half _RimPower;

struct Input {
	float2 uv_MainTex;
	float2 uv_BumpMap;
	half3 viewDir;	
};

3.3  修改surf函數,進行如下修改:

void surf (Input IN, inout SurfaceOutput o) {
	fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
	o.Albedo = c.rgb;
	o.Alpha = c.a;
	o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
	fixed rim = 1.0 - saturate (dot (normalize(IN.viewDir), o.Normal));
	o.Emission = (_RimColor.rgb * pow (rim, _RimPower));
}

這裏主要是用來計算邊緣光照的,首先通過視線與法線的夾角來找到模型的邊緣,然後再根據距離的遠近來控制發射光的強度。

3.4  將“#pragma surface surf CustomBlinnPhong”改成“#pragma surfacesurf CustomBlinnPhong exclude_path:prepass”,同時在LightingCustomBlinnPhong函數來修改漫反射光的計算,來達到卡通渲染的效果。

fixed NdotL = saturate(dot (s.Normal, lightDir)); 
fixed diff = NdotL * 0.5 + 0.5;
fixed3 ramp = tex2D (_Ramp, float2(diff, diff)).rgb;
fixed diffuse = s.Albedo * LightColor0.rgb * ramp;

通過以上設置,角色的材質部分即可變爲如下形式(暫定該Shader名爲“MyShader3”):


其顯示效果如下:


可以看出邊緣光照的效果,同時還可以看出明顯的明暗變化的卡通渲染效果。


三、       小結

綜上所述,本文已經給出了人物的幾種基本渲染方法及其Shader實現,在這裏我並沒有去分析每種渲染效果的原理,而僅是從實際出發,直接給出對應的簡單實現方法。如果想要對光照模型進行深入理解,可以Google搜索其原理進行了解。最後,給出各種渲染方法的對比圖,顯示如下:







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章