本系列文章由@浅墨_毛星云 出品,转载请注明出处。
文章链接: http://blog.csdn.net/poem_qianmo/article/details/51764028
作者:毛星云(浅墨) 微博:http://weibo.com/u/1723155442
本文工程使用的Unity3D版本: 5.2.1
这篇文章主要讲授了如何在Unity3D中分别使用Surface Shader和Vertex & Fragment Shader来编写边沿发光Shader。
1、终究实现的效果
边沿发光Shader比较直观的1个应用便是摹拟宇宙中的星球效果。将本文实现的边沿发光Shader先赋1个Material,此Material再赋给1个球体,加上Galaxy Skybox, 实现的效果以下图:

固然,边沿发光Shader也能够实现例如暗黑3中精英怪的高亮效果,将1个怪物模型本身的Shader用边沿发光Shader替换,实现效果以下图:

以下是本文实现的Shader在编辑器中的1些效果图。




2、Shader实现思路分析
思路方面,其实理解起来非常简单,就是在正常的漫反射Shader的基础之上,在终究的漫反射色彩值出来以后,再准备1个自发光色彩值,附加上去,即得到了终究的带自发光的色彩值。
按公式来表达,也就是这样:
终究色彩 = (漫反射系数 x 纹理色彩 x RGB色彩)+自发光色彩
按英文公式来表达,也就是这样:
FinalColor=(Diffuse x Texture x RGBColor)+Emissive
3、Surface Shader版边沿发光Shader实现
如果读过这个系列第1篇文章的朋友,应当还记得这个系列的第1篇文章(传送门:http://blog.csdn.net/poem_qianmo/article/details/40723789),里面的TheFirstShader,它就是1个标准的使用Unity Surface Shader实现的边沿发光Shader。
利用Unity本身的Shader封装,Surface Shader,也就是Shaderlab,算是新手或想快速上手的童鞋学习Unity中Shader书写的1个非常好的切入点。
这边贴出经过加强的第1期TheFirstShader详细注释后的源代码。可以发现里面关于主要框架的注释都是中英双语的,由于发现很多国外友人也关注了我在Github上的Shader repo(https://github.com/QianMo/Awesome-Unity-Shader),为了方便他们和后面更多的国外友人,以后如果周末写博客的时间充裕,就干脆写中英双语的注释得了。
OK,详细注释后的Surface Shader版可发光Shader源代码以下:
Shader "Learning Unity Shader/Lecture 14/Surface Rim Shader"
{
//-----------------------------------【属性 || Properties】------------------------------------------
Properties
{
//主色彩 || Main Color
_MainColor("【主色彩】Main Color",Color) = (0.5,0.5,1)
//漫反射纹理 || Diffuse Texture
_MainTex("【纹理】Texture",2D) = "white" {}
//凹凸纹理 || Bump Texture
_BumpMap("【凹凸纹理】Bumpmap",2D) = "bump" {}
//边沿发光色彩 || Rim Color
_RimColor("【边沿发光色彩】Rim Color",Color) = (0.17,0.36,0.81,0.0)
//边沿发光强度 ||Rim Power
_RimPower("【边沿色彩强度】Rim Power",Range(0.6,36.0)) = 8.0
//边沿发光强度系数 || Rim Intensity Factor
_RimIntensity("【边沿色彩强度系数】Rim Intensity",Range(0.0,100.0)) = 1.0
}
//----------------------------------【子着色器 || SubShader】---------------------------------------
SubShader
{
//渲染类型为Opaque,不透明 || RenderType Opaque
Tags
{
"RenderType" = "Opaque"
}
//-------------------------开启CG着色器编程语言段 || Begin CG Programming Part----------------------
CGPROGRAM
//【1】声明使用兰伯特光照模式 ||Using the Lambert light mode
#pragma surface surf Lambert
//【2】定义输入结构 || Input Struct
struct Input
{
//纹理贴图 || Texture
float2 uv_MainTex;
//法线贴图 || Bump Texture
float2 uv_BumpMap;
//视察方向 || Observation direction
float3 viewDir;
};
//【3】变量声明 || Variable Declaration
//边沿色彩
float4 _MainColor;
//主纹理
sampler2D _MainTex;
//凹凸纹理
sampler2D _BumpMap;
//边沿色彩
float4 _RimColor;
//边沿色彩强度
float _RimPower;
//边沿色彩强度
float _RimIntensity;
//【4】表面着色函数的编写 || Writing the surface shader function
void surf(Input IN,inout SurfaceOutput o)
{
//表面反射色彩为纹理色彩
o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb*_MainColor.rgb;
//表面法线为凹凸纹理的色彩
o.Normal = UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap));
//边沿色彩
half rim = 1.0 - saturate(dot(normalize(IN.viewDir),o.Normal));
//计算出边沿色彩强度系数
o.Emission = _RimColor.rgb * pow(rim,_RimPower)*_RimIntensity;
}
//-------------------结束CG着色器编程语言段 || End CG Programming Part------------------
ENDCG
}
//后备着色器为普通漫反射 || Fallback use Diffuse
Fallback "Diffuse"
}
略微揣摩1下就明白,此Shader其实就是用利用了Unity5中封装好的Standard Surface Output结构体中的Emission(自发光)属性,来到达这样的边沿光效果,技术含量很有限。这边附1下Unity5中的SurfaceOutputStandard原型:
// Unity5 SurfaceOutputStandard原型:
struct SurfaceOutputStandard
{
fixed3 Albedo; // 漫反射色彩
fixed3 Normal; // 切线空间法线
half3 Emission; //自发光
half Metallic; // 金属度;取0为非金属,取1为金属
half Smoothness; // 光泽度;取0为非常粗糙,取1为非常光滑
half Occlusion; // 遮挡(默许值为1)
fixed Alpha; // 透明度
};
将此Shader赋给Material后在编辑器效果图:


里面有6个参数,包括主色彩、纹理、凹凸纹理、边沿发光色彩、边沿色彩强度、边沿色彩强度系数这6个参数可以定制与调理,只要贴图资源到位,就很容易可以调出自己满意的效果来。
4、可编程Shader版边沿发光Shader的实现
这篇文章核心主要就是实现本节的这个可编程版(也就是Vertex & Fragment Shader)边沿发光Shader。大家知道,Vertex & Fragment Shader是比Surface Shader更高1段位的实现形态,有更大的可控性,更好的可编程性,可以实现更加丰富的效果,是更贴近CG着色语言的1种Shader形态。
OK,直接贴出经过详细注释的Vertex & Fragment Shader版边沿发光Shader实现源代码:
Shader "Learning Unity Shader/Lecture 14/Basic Rim Shader"
{
//-----------------------------------【属性 || Properties】------------------------------------------
Properties
{
//主色彩 || Main Color
_MainColor("【主色彩】Main Color",1)
//漫反射纹理 || Diffuse Texture
_TextureDiffuse("【漫反射纹理】Texture Diffuse",2D) = "white" {}
//边沿发光色彩 || Rim Color
_RimColor("【边沿发光色彩】Rim Color",1)
//边沿发光强度 ||Rim Power
_RimPower("【边沿发光强度】Rim Power",36)) = 0.1
//边沿发光强度系数 || Rim Intensity Factor
_RimIntensity("【边沿发光强度系数】Rim Intensity",100)) = 3
}
//----------------------------------【子着色器 || SubShader】---------------------------------------
SubShader
{
//渲染类型为Opaque,不透明 || RenderType Opaque
Tags
{
"RenderType" = "Opaque"
}
//---------------------------------------【唯1的通道 || Pass】------------------------------------
Pass
{
//设定通道名称 || Set Pass Name
Name "ForwardBase"
//设置光照模式 || LightMode ForwardBase
Tags
{
"LightMode" = "ForwardBase"
}
//-------------------------开启CG着色器编程语言段 || Begin CG Programming Part----------------------
CGPROGRAM
//【1】指定顶点和片断着色函数名称 || Set the name of vertex and fragment shader function
#pragma vertex vert
#pragma fragment frag
//【2】头文件包括 || include
#include "UnityCG.cginc"
#include "AutoLight.cginc"
//【3】指定Shader Model 3.0 || Set Shader Model 3.0
#pragma target 3.0
//【4】变量声明 || Variable Declaration
//系统光照色彩
uniform float4 _LightColor0;
//主色彩
uniform float4 _MainColor;
//漫反射纹理
uniform sampler2D _TextureDiffuse;
//漫反射纹理_ST后缀版
uniform float4 _TextureDiffuse_ST;
//边沿光色彩
uniform float4 _RimColor;
//边沿光强度
uniform float _RimPower;
//边沿光强度系数
uniform float _RimIntensity;
//【5】顶点输入结构体 || Vertex Input Struct
struct VertexInput
{
//顶点位置 || Vertex position
float4 vertex : POSITION;
//法线向量坐标 || Normal vector coordinates
float3 normal : NORMAL;
//1级纹理坐标 || Primary texture coordinates
float4 texcoord : TEXCOORD0;
};
//【6】顶点输出结构体 || Vertex Output Struct
struct VertexOutput
{
//像素位置 || Pixel position
float4 pos : SV_POSITION;
//1级纹理坐标 || Primary texture coordinates
float4 texcoord : TEXCOORD0;
//法线向量坐标 || Normal vector coordinates
float3 normal : NORMAL;
//世界空间中的坐标位置 || Coordinate position in world space
float4 posWorld : TEXCOORD1;
//创建光源坐标,用于内置的光照 || Function in AutoLight.cginc to create light coordinates
LIGHTING_COORDS(3,4)
};
//【7】顶点着色函数 || Vertex Shader Function
VertexOutput vert(VertexInput v)
{
//【1】声明1个顶点输出结构对象 || Declares a vertex output structure object
VertexOutput o;
//【2】填充此输出结构 || Fill the output structure
//将输入纹理坐标赋值给输出纹理坐标
o.texcoord = v.texcoord;
//获得顶点在世界空间中的法线向量坐标
o.normal = mul(float4(v.normal,0),_World2Object).xyz;
//取得顶点在世界空间中的位置坐标
o.posWorld = mul(_Object2World,v.vertex);
//获得像素位置
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
//【3】返回此输出结构对象 || Returns the output structure
return o;
}
//【8】片断着色函数 || Fragment Shader Function
fixed4 frag(VertexOutput i) : COLOR
{
//【8.1】方向参数准备 || Direction
//视角方向
float3 ViewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
//法线方向
float3 Normalection = normalize(i.normal);
//光照方向
float3 LightDirection = normalize(_WorldSpaceLightPos0.xyz);
//【8.2】计算光照的衰减 || Lighting attenuation
//衰减值
float Attenuation = LIGHT_ATTENUATION(i);
//衰减后色彩值
float3 AttenColor = Attenuation * _LightColor0.xyz;
//【8.3】计算漫反射 || Diffuse
float NdotL = dot(Normalection,LightDirection);
float3 Diffuse = max(0.0,NdotL) * AttenColor + UNITY_LIGHTMODEL_AMBIENT.xyz;
//【8.4】准备自发光参数 || Emissive
//计算边沿强度
half Rim = 1.0 - max(0,dot(i.normal,ViewDirection));
//计算出边沿自发光强度
float3 Emissive = _RimColor.rgb * pow(Rim,_RimPower) *_RimIntensity;
//【8.5】计在终究色彩中加入自发光色彩 || Calculate the final color
//终究色彩 = (漫反射系数 x 纹理色彩 x rgb色彩)+自发光色彩 || Final Color=(Diffuse x Texture x rgbColor)+Emissive
float3 finalColor = Diffuse * (tex2D(_TextureDiffuse,TRANSFORM_TEX(i.texcoord.rg,_TextureDiffuse)).rgb*_MainColor.rgb) + Emissive;
//【8.6】返回终究色彩 || Return final color
return fixed4(finalColor,1);
}
//-------------------结束CG着色器编程语言段 || End CG Programming Part------------------
ENDCG
}
}
//后备着色器为普通漫反射 || Fallback use Diffuse
FallBack "Diffuse"
}
相信很多朋友已看出来了,与普通的漫反射Shader相比,这个Shader的魔力就在于多出了“8.4准备自发光参数”和“8.5在终究色彩中加入自发光色彩"两个步骤而已,前面都是普通的Vertex & Fragment Shader常规写法。
将此Shader赋给Material,得到的效果以下:




固然,你也能够将这两个Shader用于场景中各种模型,以下是1组效果图:




OK,这篇文章的内容大致如此。我们下篇文章,再会。
附: 本文配套源码下载链接
【Github】本文Shader源码