unity雙面材質攻略

轉載:http://game.ceeger.com/forum/read.php?tid=3024


Unity內置的Shader,都是單面效果,想必導入Mesh的同學都碰到過這樣的痛苦,布料飄起的背面部分看起來是空氣,汽車透過車窗看到是路面...各種蛋疼。
有些文章教導大家 把模型做出厚度來吧,這種做法實在太那個啥了......
 
其實用改寫Shader的方法可以很方便的實現雙面材質。
Unity裏有3種Shader方式:
1.Fixed Function Shaders 
2.Vertex and Fragment Shaders
3. Surface Shaders
關於這部分的詳細介紹,請參考官方的教程。
這三種方式裏,都可以通過直接在Shader代碼頭部添加一個Cull off 語句,實現強制雙面渲染。
但是直接用Cull off的方式 有個重大的缺陷,這材質從兩面看無論貼圖、顏色、反光、照明情況,都是一模一樣的,這並不符合大多數實際情況的常識。
在第1和第2種Shader裏,是可以通過在一個渲染子程序裏用兩個渲染Pass來實現雙面不同效果的,這部分網上的資料也很多,寫起來也很簡單直接。
 
這裏主要討論的是第三種也是最常用的Surface Shader的雙面不同效果的實現。
Surface Shader是不能寫在Pass裏的,所以要實現它的雙面不同效果就要用其他變通的辦法。
 
首先去Unity官方網站下載一個內置Shader的代碼包,鏈接如下:
http://unity3d.com/download_unity/builtin_shaders.zip 
打開後看見一堆.shader文件,可以用任何文本編輯器打開。可以看見系統內建的Shader基本都是Surface方式。
這裏隨便打開一個Normal-BumpSpec.Shader 這是普通的高光-凹凸貼圖材質

複製代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Shader "Bumped Specular" {
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)) = 0.078125
    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    _BumpMap ("Normalmap", 2D) = "bump" {}
}
SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 400
     
CGPROGRAM
#pragma surface surf BlinnPhong
 
 
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
half _Shininess;
 
struct Input {
    float2 uv_MainTex;
    float2 uv_BumpMap;
};
 
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));
}
ENDCG
}
FallBack "Specular"
}

簡單解說一下幾個關鍵行:
第一行Shader "Bumped Specular" 指定了這個shader出現在Unity系統Shader菜單裏的名字,如果要修改系統內建Shader的源代碼,最好把這個名字改掉,否則和系統內建Shader重名啦。我是這樣寫的: Shader "Hog's shaders/BumpSpec_Twoside" ,這個shader會出現在Hog's shaders組裏,系統會自動完成這個加載。
第二行Properties後面的一組以下劃線開頭的變量表示了這個渲染器需要設置的參數。對於一個高光-凹凸材質來說,需要材質顏色、反光顏色、反光率、材質貼圖和法線貼圖,這5個變量就對應這5個東西啦,詳細請參考系統手冊。
在第11行LOD 400 後面加上一行:Cull off,這個材質就會自動雙面渲染了 Cull off表示雙面都渲染,不寫默認是Cull back,不渲染背面。你也可以寫上Cull front,不渲染正面。
改完這行 ,把第一行改成你希望的名字,把這個shader文件拷貝到工程的assets目錄底下,系統就能自動加載啦。
效果如圖:
雙面貼圖
 
雙面是雙面了,但是哪有兩面是一樣亮、一樣高光區域的....,全透光的磚牆,這種雙面很少會用到吧。
 
如何做到雙面不同效果呢?
前面說了Surface shader是不能寫兩個pass渲染不同面的,但其實surface方式可以寫多個渲染過程,根本不需要pass的概念,Surface Shader可以這樣寫:
Call back 
渲染正面的代碼
Call front
渲染反面的代碼
 
就可以實現雙面不同的控制了。
根據這個原理,其實我們只要把系統內建shader的源代碼複製一份,就能實現另一面不同效果了。以下供參考:

複製代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Shader "Hog's shaders/BumpSpec_Twoside" {
Properties {
//正面5個參數
    _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)) = 0.078125
    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    _BumpMap ("Normalmap", 2D) = "bump" {}
//反面拷貝 改名 也是5個
    _BackColor ("Back Main Color", Color) = (1,1,1,1)
    _BackSpecColor ("Back Specular Color", Color) = (0.5, 0.5, 0.5, 1)
    _BackShininess ("Back Shininess", Range (0.03, 1)) = 0.078125
    _BackMainTex ("Back Base (RGB) Gloss (A)", 2D) = "white" {}
    _BackBumpMap ("Back Normalmap", 2D) = "bump" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
    LOD 400
    Cull back
//開始渲染正面   
CGPROGRAM
//表明是surface渲染方式 主渲染程序是surf 光照模型是BLinnPhong
#pragma surface surf BlinnPhong 
 
 
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
half _Shininess;
 
struct Input {
    float2 uv_MainTex;
    float2 uv_BumpMap;
};
 
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));}
ENDCG
 
 
    Cull front
//開始渲染反面 其實和就是拷貝了一份正面渲染的代碼 除了變量名要改   
CGPROGRAM
#pragma surface surf BlinnPhong
 
sampler2D _BackMainTex;
sampler2D _BackBumpMap;
fixed4 _BackColor;
half _BackShininess;
 
struct Input {
    float2 uv_BackMainTex;
    float2 uv_BackBumpMap;
};
 
void surf (Input IN, inout SurfaceOutput o) {
    fixed4 tex = tex2D(_BackMainTex, IN.uv_BackMainTex);
    o.Albedo = tex.rgb * _BackColor.rgb;
    o.Gloss = tex.a;
    o.Alpha = tex.a * _BackColor.a;
    o.Specular = _BackShininess;
    o.Normal = UnpackNormal(tex2D(_BackBumpMap, IN.uv_BackBumpMap));}
ENDCG
}
FallBack "Specular"
}
這是個雙面可以分別指定的高光-凹凸材質,注意幾個要點:
properties部分只能出現一次,所以這是不能直接拷貝的。因爲要爲雙面指定不同的參數,雙面的參數變量名肯定不能一樣,這個論壇裏都是程序猿,沒必要多解釋了。我簡單的把用於正面的5個參數前面都加上了一個Back用於反面。
在CG代碼內部也要對應的應用相應的參數,反面的渲染代碼就用剛纔全部加了Back的那5個參數。
正面代碼段用Cull back 開始 反面的代碼用Cull front開始
以下是渲染效果:

 
一面是磚牆一面是木板。。蛋疼了沒
這個模式下,雙面也完全可以指定不同的材質,基本上你不用學習很多內建Shader和CG語法,通過簡單的copy-paste就能組合出無窮的雙面材質來了。
 
再提升一下,其實我們常用的雙面效果,除了透明的材質以外,無非是兩種:
一是反面和正面同樣紋理,但是不需要高光、反射,只需要一個相對黯淡的被環境光照亮的材質,比如磚牆木盒衣服什麼的
二是反面顯示爲單身或其他紋理,但也不需要高光、反射,只需要被環境光照亮,比如汽車內部 建築物內部等等。
第一種情況,反面可以沿用正面紋理,但是以普通的Diffuse方式着色
第二種情況,反面不指定或者單獨指定紋理,也以普通的Diffuse方式着色
兩種情況,反面的渲染都可以借用系統內建Shader的Diffuse渲染代碼來實現,方式一的代碼:

複製代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Shader "Hog's shaders/BumpSpec_Twoside1" {
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)) = 0.078125
    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    _BumpMap ("Normalmap", 2D) = "bump" {}
    _BackColor ("Back Main Color", Color) = (1,1,1,1)
}
SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 400
    Cull back
     
CGPROGRAM
#pragma surface surf BlinnPhong
 
 
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
half _Shininess;
 
struct Input {
    float2 uv_MainTex;
    float2 uv_BumpMap;
};
 
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));}
ENDCG
 
    Cull front
     
CGPROGRAM
#pragma surface surf Lambert
 
sampler2D _MainTex;
fixed4 _BackColor;
 
struct Input {
    float2 uv_MainTex;
};
 
void surf (Input IN, inout SurfaceOutput o) {
    fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _BackColor;
    o.Albedo = c.rgb;
    o.Alpha = c.a;
}
ENDCG
}
FallBack "Specular"
}
反面渲染的運算就直接借用了系統的Diffuse Shader,只不過紋理是沿用正面的紋理,只增加了一個反面的顏色變量用來模擬環境光亮度,與紋理混合實現反面效果。渲染效果如下:

 
 
方式二代碼:

複製代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Shader "Hog's shaders/BumpSpec_Twoside2" {
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)) = 0.078125
    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    _BumpMap ("Normalmap", 2D) = "bump" {}
    _BackColor ("Back Main Color", Color) = (1,1,1,1)
    _BackMainTex ("Back Base (RGB) Gloss (A)", 2D) = "white" {}
}
SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 400
    Cull back
     
CGPROGRAM
#pragma surface surf BlinnPhong
 
 
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
half _Shininess;
 
struct Input {
    float2 uv_MainTex;
    float2 uv_BumpMap;
};
 
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));}
ENDCG
 
    Cull front
     
CGPROGRAM
#pragma surface surf Lambert
 
sampler2D _BackMainTex;
fixed4 _BackColor;
 
struct Input {
    float2 uv_BackMainTex;
};
 
void surf (Input IN, inout SurfaceOutput o) {
    fixed4 c = tex2D(_BackMainTex, IN.uv_BackMainTex) * _BackColor;
    o.Albedo = c.rgb;
    o.Alpha = c.a;
}
ENDCG
}
FallBack "Specular"
}
此方式下反面可以單獨指定紋理,不指定就直接顯示指定的反面顏色,渲染效果如下

 
 
以上只是介紹一個基本思想,在這個基礎上能應該能衍生出無窮的變化。對自定義shader有興趣的可以參考系統手冊和Nvidia的CG教學手冊。
不過千萬不要動不動就使用雙面材質,因爲會增加系統負荷,應該只用在需要的地方。
 
把以上代碼起個名字另存爲.shader文件,導入工程assets,就能直接使用。

發佈了30 篇原創文章 · 獲贊 12 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章