unity双面材质攻略

转载 2015年07月08日 17:34:11

转载: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,就能直接使用。

【Unity3d学习笔记】unity制作双面材质shader

Unity3d制作双面材质: 1.在项目面板中创建plane作为演示。 2.导入两张图片作为贴图演示。 3.在项目面板中创建DoubleSurface shader 4.打开创建的shader...

【猫猫的Unity Shader之旅】之双面材质和多Pass渲染

默认情况下,我们编写的Shader都只对模型的正面进行渲染,因为大多数模型都是封闭的,我们看不到反面。在实际的开发过程中,也经常会遇到需要正反面都渲染的时候,比如开发手游的时候经常需要用一个面片+透明...
  • dbtxdxy
  • dbtxdxy
  • 2015年06月01日 13:35
  • 6037

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Unity3d 双面材质

Shader "Nature/Vegitation Vertex Lit" {     Properties {         _Color ("Main Color", C...

[Shader 着色器]终极DIY双面材质攻略

Unity内置的Shader,都是单面效果,想必导入Mesh的同学都碰到过这样的痛苦,布料飘起的背面部分看起来是空气,汽车透过车窗看到是路面...各种蛋疼。 有些文章教导大家 把模型做出厚度来吧,这...
  • ldghd
  • ldghd
  • 2015年06月02日 15:17
  • 765

自定义双面Shader

在Unity开发中,难免有的时候会用到双面材质,可通过修改Shader达到想要的效果。...

Unity给力插件之ShaderForge(一)

这是一个用来制作shader的插件,也是一个很好的学习shader的工具。这个插件上手很容易,但是要用它来制作理想的Shader,需要下点功夫。 这儿先列举出基础知识,以及我的一些实践。以后我还会继...

Unity 制作灯罩 双面材质背面不受光照影响的解决方法

最近开发一个Unity项目主要是打算模拟一下灯效果,本以为把一个光源放入我们的灯的模型中就可以完美解决问题,然而当开始操作的时候,真的是困难重重,套在模型里面的点光源根本无法照亮外面的模型!    ...
  • nijiayy
  • nijiayy
  • 2015年12月10日 22:09
  • 2731

Unity3D双面渲染

那个其实黑简单,只要把背面剔除关了就可以了,官网有标准shader的实现,然后改下就可以了。 这里只改alpha测试的shader,如果要改alpha混合的shader类似。 内建shader路径...
  • honeycat
  • honeycat
  • 2012年08月23日 09:24
  • 13815
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:unity双面材质攻略
举报原因:
原因补充:

(最多只允许输入30个字)