原文:http://www.manew.com/thread-99195-1-1.html
一、前言
首先,來看一下效果圖,如圖所示:
這個效果要使得攝像機的Clear Flags爲Solid Color模式,如果爲其他模式可能會看不到外部那一圈光環。
二、實現原理
1、Shader部分:將需要使用兩個Pass塊,兩個Pass塊裏輸出的顏色不同,並最終使用透明度混合得到最後輸出的像素顏色。
●第一個Pass塊:這個Pass塊頂點和片段程序都比較簡單,代碼如下:
Pass{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 _Color;
float4 vert(float4 vertexPos : POSITION) : SV_POSITION{
return mul(UNITY_MATRIX_MVP, vertexPos);
}
float4 frag(void) : COLOR{
return _Color;
}
ENDCG
}
只需將模型的頂點和預設的顏色輸出就可以了。
●第二個Pass塊:這個pass塊相對複雜一點,頂點程序主要的計算內容就是頂點的法線方向和相機觀察方向的向量,這兩個的點積就是膨脹的強度Strength。通過指數函數縮放對Strength和透明度opacity進行計算就會得到膨脹的效果。計算的代碼部分爲:
float3 normalDirectionT = normalize(normalDirection);
float3 viewDirectionT = normalize(viewDirection);
float strength = abs(dot(viewDirectionT, normalDirectionT));
float opacity = pow(strength, _Strength);
而片段程序也是簡單的輸出最終的顏色就可以,完整的代碼如下:
Pass{
Tags{"LightMode" = "ForwardBase" "Queue" = "Transparent" "RenderType" = "Transparent"}
// Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _GlowColor;
float _Strength;;
float _GlowRange;
struct vInput {
float4 vertex : POSITION;
float4 normal : NORMAL;
};
struct v2f {
float4 position : SV_POSITION;
float4 col:COLOR;
};
v2f vert(vInput i) {
v2f o;
float4x4 modelMatrix = _Object2World;
float4x4 modelMatrixInverse = _World2Object;
float3 normalDirection = normalize(mul(i.normal, modelMatrixInverse)).xyz;
float3 viewDirection = normalize(_WorldSpaceCameraPos - mul(modelMatrix, i.vertex).xyz);
float4 pos = i.vertex + (i.normal * _GlowRange);
o.position = mul(UNITY_MATRIX_MVP, pos);
float3 normalDirectionT = normalize(normalDirection);
float3 viewDirectionT = normalize(viewDirection);
float strength = abs(dot(viewDirectionT, normalDirectionT));
float opacity = pow(strength, _Strength);
float4 col = float4(_GlowColor.xyz, opacity);
o.col = col;
return o;
}
float4 frag(v2f i) : COLOR{
return i.col;
}
ENDCG
}
在第二個Pass塊中使用到了ZWrite Off命令,也即關閉遮擋,模型所有的面和通道都會被渲染,如果使用了ZWrite On命令,你會發現好像並沒有什麼變化,還是可以正常運行得到前面的效果。這是因爲,Cull命令,默認的是Cull Back,即提出背面,不渲染模型的背面。如果使用Cull Front命令,即不渲染模型的前面,渲染的是模型的背面,得到的效果圖如圖所示,當然最終的選擇就看你想要實現什麼樣的效果吧。
完整的Shader代碼:
Shader "CgInUnity/Glow"
{
Properties{
_Color("Object's Color", Color) = (0, 1, 0, 1)
_GlowColor("Glow's Color", Color) = (1, 0, 0, 0)
_Strength("Glow Strength", Range(5.0, 1.0)) = 2.0
_GlowRange("GlowRange",Range(0.1,1))=0.3
}
SubShader{
Pass{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 _Color;
float4 vert(float4 vertexPos : POSITION) : SV_POSITION{
return mul(UNITY_MATRIX_MVP, vertexPos);
}
float4 frag(void) : COLOR{
return _Color;
}
ENDCG
}
Pass{
Tags{"LightMode" = "ForwardBase" "Queue" = "Transparent" "RenderType" = "Transparent"}
// Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _GlowColor;
float _Strength;;
float _GlowRange;
struct vInput {
float4 vertex : POSITION;
float4 normal : NORMAL;
};
struct v2f {
float4 position : SV_POSITION;
float4 col:COLOR;
};
v2f vert(vInput i) {
v2f o;
float4x4 modelMatrix = _Object2World;
float4x4 modelMatrixInverse = _World2Object;
float3 normalDirection = normalize(mul(i.normal, modelMatrixInverse)).xyz;
float3 viewDirection = normalize(_WorldSpaceCameraPos - mul(modelMatrix, i.vertex).xyz);
float4 pos = i.vertex + (i.normal * _GlowRange);
o.position = mul(UNITY_MATRIX_MVP, pos);
float3 normalDirectionT = normalize(normalDirection);
float3 viewDirectionT = normalize(viewDirection);
float strength = abs(dot(viewDirectionT, normalDirectionT));
float opacity = pow(strength, _Strength);
float4 col = float4(_GlowColor.xyz, opacity);
o.col = col;
return o;
}
float4 frag(v2f i) : COLOR{
return i.col;
}
ENDCG
}
}
}
2、控制腳本部分C#代碼:
using UnityEngine;
using System.Collections;
public class GlowControl : MonoBehaviour {
private Material mat;
private float value;
[SerializeField]
float speed=1;
// Use this for initialization
void Start () {
mat = GetComponent<MeshRenderer>().sharedMaterial;
}
// Update is called once per frame
void Update () {
value = Mathf.PingPong(Time.time * speed, 5);
/// Debug.Log(value);
mat.SetFloat("_Strength", value);
}
}
就是一個簡單控制強度隨着時間變換的代碼。
三、總結
這個小小的案例我們學習到了怎麼使用多個Pass塊來渲染同一個物體,以及在多個Pass塊中使用透明度混合。我個人認爲是一個非常不錯的學習案例,不只是因爲
它的代碼部分非常簡潔明瞭,更重要的是這個案例在很多應用中也是非常有用的。祝好好學習,慢慢變牛。
每天進步一點點。