[Unity Shader编程之]自定义双面材质(doubleside material)二

 

继续上篇文章《[Unity Shader编程之]自定义双面材质(doubleside material)二》前面说了Surface shader是不能写两个pass渲染不同面的,但其实surface方式可以写多个渲染过程,根本不需要pass的概念,Surface Shader可以这样写:
Call back 
渲染正面的代码
Call front
渲染反面的代码

就可以实现双面不同的控制了。
根据这个原理,其实我们只要把系统内建shader的源代码复制一份,就能实现另一面不同效果了。以下供参考:


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渲染代码来实现,方式一的代码:


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,只不过纹理是沿用正面的纹理,只增加了一个反面的颜色变量用来模拟环境光亮度,与纹理混合实现反面效果。渲染效果如下:

方式二代码:


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教程到此结束,下篇我们再会!