轉(zhuǎn)自http://www.manew.com/thread-98405-1-1.html
一微服、前言
終于周末了趾疚,心心掛念的依然還是那段代碼。沒辦法职辨,苦逼的命盗蟆。還是廢話少說(shuō),先上效果圖如圖所示舒裤,前面兩個(gè)的效果圖是兩種模式下的
不同效果喳资,最后一章圖片是在其中一種模式下,場(chǎng)景進(jìn)行了精心布置后的效果圖腾供。
看效果圖會(huì)不自覺的認(rèn)為實(shí)現(xiàn)這個(gè)效果是不是要在場(chǎng)景中的物體中進(jìn)行繪制仆邓,如果真是要這樣做的話那是在是太耗性能了(畢竟都工作了鲜滩,本人畢業(yè)
了就不想在搞那些不實(shí)用的
)。其實(shí)节值,這個(gè)是通過(guò)控制攝像機(jī)的最后渲染來(lái)實(shí)現(xiàn)的效果的徙硅,后面我會(huì)給出這個(gè)案例的工程文件下載地址。讀者
在運(yùn)行模式的Scene視圖里將看到場(chǎng)景的物體(立方體)并沒有被額外渲染搞疗,有了這個(gè)思路對(duì)于后面理解代碼的實(shí)現(xiàn)原理會(huì)很有幫助嗓蘑。
二、原理
1匿乃、Shader部分—光照計(jì)算
這里用到的光照計(jì)算都比較簡(jiǎn)單桩皿,光照模式我使用了最簡(jiǎn)單的漫反射模式,代碼如下
//光照計(jì)算
float4x4 modelMatrix = _Object2World;
float4x4 modelMatrixInverse = _World2Object;
float3 normalDirection = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
float3 diffuseReflection = _LightColor0.rgb * max(0.0, dot(normalDirection, lightDirection));
output.col = float4(diffuseReflection, 1.0);
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
如圖所示為漫反射的示意圖幢炸,它的計(jì)算公式為
詳細(xì)的介紹和代碼可以參考這里
2泄隔、Shader部分—聲納光波的計(jì)算
Shader代碼如下:
//聲納光波計(jì)算
#ifdef SONAR_DIRECTIONAL
floatw = dot(output.pos.xyz, _SonarWaveVector);
#else
floatw = length(output.pos.xyz - _SonarWaveVector);
#endif
// Moving wave.
w -= _Time.y * _SonarWaveParams.w;
// Get modulo (w % params.z / params.z)
w /= _SonarWaveParams.z;
w = w - floor(w);
// Make the gradient steeper.
floatp = _SonarWaveParams.y;
w = (pow(w, p) + pow(1 - w, p * 4)) * 0.5;
// Amplify.
w *= _SonarWaveParams.x;
fixed3 col = _SonarWaveColor * w + _SonarAddColor;
代碼中使用了預(yù)編譯命令,是考慮了前面說(shuō)的效果圖中的兩種模式的切換宛徊。為此佛嬉,還必須在前面添加預(yù)編譯命令
[C#]純文本查看復(fù)制代碼
#pragma multi_compile SONAR_DIRECTIONAL SONAR_SPHERICAL
這個(gè)命令的大概意思就是說(shuō)可以將這兩個(gè)模式的Shader一起編譯,然后就可以在C#代碼里通過(guò)
[C#]純文本查看復(fù)制代碼
Shader.DisableKeyword("SONAR_SPHERICAL");
或者
[C#]純文本查看復(fù)制代碼
Shader.EnableKeyword("SONAR_SPHERICAL");
來(lái)進(jìn)行切換
完整頂點(diǎn)片段Shader代碼如下:
[C#]純文本查看復(fù)制代碼
Shader"CgInUnity/SonarFxVF"
{
Properties
{
_SonarBaseColor("Base Color",? Color) = (0.1, 0.1, 0.1, 0)
_SonarWaveColor("Wave Color",? Color) = (1.0, 0.1, 0.1, 0)
_SonarWaveParams("Wave Params", Vector) = (1, 20, 20, 10)
_SonarWaveVector("Wave Vector", Vector) = (0, 0, 1, 0)
_SonarAddColor("Add Color",?? Color) = (0, 0, 0, 0)
}
SubShader
{
Tags{"LightMode"="ForwardBase"}
// make sure that all uniforms are correctly set
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile SONAR_DIRECTIONAL SONAR_SPHERICAL
#include "UnityCG.cginc"
structvertexInput
{
float4 vertex : POSITION;
float3 normal:NORMAL;
};
structvertexOutput
{
float4 pos:SV_POSITION;
float4 col:COLOR;
};
float3 _SonarBaseColor;
float3 _SonarWaveColor;
float4 _SonarWaveParams;// Amp, Exp, Interval, Speed
float3 _SonarWaveVector;
float3 _SonarAddColor;
uniform float4 _LightColor0;
vertexOutput vert (vertexInput input)
{
vertexOutput output;
//光照計(jì)算
float4x4 modelMatrix = _Object2World;
float4x4 modelMatrixInverse = _World2Object;
float3 normalDirection = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
float3 diffuseReflection = _LightColor0.rgb * max(0.0, dot(normalDirection, lightDirection));
output.col = float4(diffuseReflection, 1.0);
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
//聲納光波計(jì)算
#ifdef SONAR_DIRECTIONAL
floatw = dot(output.pos.xyz, _SonarWaveVector);
#else
floatw = length(output.pos.xyz - _SonarWaveVector);
#endif
// Moving wave.
w -= _Time.y * _SonarWaveParams.w;
// Get modulo (w % params.z / params.z)
w /= _SonarWaveParams.z;
w = w - floor(w);
// Make the gradient steeper.
floatp = _SonarWaveParams.y;
w = (pow(w, p) + pow(1 - w, p * 4)) * 0.5;
// Amplify.
w *= _SonarWaveParams.x;
fixed3 col = _SonarWaveColor * w + _SonarAddColor;
output.col += float4(col, 1);
returnoutput;
}
float4 frag (vertexOutput input) : COLOR
{
returninput.col;
}
ENDCG
}
}
}
喜歡用簡(jiǎn)潔的Surface Shader代碼的童鞋可以用如下代碼替換:
Shader"CgInUnity/SonarFxSurf"
{
Properties
{
_SonarBaseColor? ("Base Color",? Color)? = (0.1, 0.1, 0.1, 0)
_SonarWaveColor? ("Wave Color",? Color)? = (1.0, 0.1, 0.1, 0)
_SonarWaveParams ("Wave Params", Vector) = (1, 20, 20, 10)
_SonarWaveVector ("Wave Vector", Vector) = (0, 0, 1, 0)
_SonarAddColor?? ("Add Color",?? Color)? = (0, 0, 0, 0)
}
SubShader
{
Tags {"RenderType"="Opaque"}
CGPROGRAM
#pragma surface surf Lambert
#pragma multi_compile SONAR_DIRECTIONAL SONAR_SPHERICAL
structInput
{
float3 worldPos;
};
float3 _SonarBaseColor;
float3 _SonarWaveColor;
float4 _SonarWaveParams;// Amp, Exp, Interval, Speed
float3 _SonarWaveVector;
float3 _SonarAddColor;
voidsurf(Input IN, inout SurfaceOutput o)
{
#ifdef SONAR_DIRECTIONAL
floatw = dot(IN.worldPos, _SonarWaveVector);
#else
floatw = length(IN.worldPos - _SonarWaveVector);
#endif
// Moving wave.
w -= _Time.y * _SonarWaveParams.w;
// Get modulo (w % params.z / params.z)
w /= _SonarWaveParams.z;
w = w - floor(w);
// Make the gradient steeper.
floatp = _SonarWaveParams.y;
w = (pow(w, p) + pow(1 - w, p * 4)) * 0.5;
// Amplify.
w *= _SonarWaveParams.x;
// Apply to the surface.
o.Albedo = _SonarBaseColor;
o.Emission = _SonarWaveColor * w + _SonarAddColor;
}
ENDCG
}
Fallback"Diffuse"
}
這個(gè)代碼看似簡(jiǎn)單闸天,但是對(duì)于處在學(xué)習(xí)基礎(chǔ)原理階段的童鞋暖呕,我的建議還是多動(dòng)手寫寫頂點(diǎn)片段Shader,實(shí)現(xiàn)簡(jiǎn)單的光照模式苞氮、頂點(diǎn)變換等等對(duì)于學(xué)
習(xí)Shader會(huì)非常有幫助的缰揪。
3、C#腳本部分
腳本部分最關(guān)鍵的就是使用上述的Shader去渲染攝像機(jī)葱淳,這個(gè)需要通過(guò)如下的代碼來(lái)實(shí)現(xiàn)嗎钝腺,這段代碼的意思是替換成這個(gè)Shader來(lái)渲染攝像機(jī)
[C#]純文本查看復(fù)制代碼
GetComponent().SetReplacementShader(shader,null);
其他的功能就是出傳遞參數(shù)給Shader來(lái)實(shí)現(xiàn)最終隨時(shí)間變換的效果,完整的代碼如下:
usingUnityEngine;
[RequireComponent(typeof(Camera))]
publicclassSonarFxControl : MonoBehaviour
{
// 聲納的模式
publicenumSonarMode { Directional, Spherical }
[SerializeField] SonarMode _mode = SonarMode.Directional;
publicSonarMode mode {get{return_mode; }set{ _mode = value; } }
// 聲納波的方向(僅僅在Directional模式下)
[SerializeField] Vector3 _direction = Vector3.forward;
publicVector3 direction {get{return_direction; }set{ _direction = value; } }
// 聲納波區(qū)域(僅僅在Spherical模式下)
[SerializeField] Vector3 _origin = Vector3.zero;
publicVector3 origin {get{return_origin; }set{ _origin = value; } }
// 背景顏色(Surfface Shader下有用)
[SerializeField] Color _baseColor =newColor(0.2f, 0.2f, 0.2f, 0);
publicColor baseColor {get{return_baseColor; }set{ _baseColor = value; } }
// 聲納波的顏色
[SerializeField] Color _waveColor =newColor(1.0f, 0.2f, 0.2f, 0);
publicColor waveColor {get{return_waveColor; }set{ _waveColor = value; } }
// 波的高度\振幅
[SerializeField]float_waveAmplitude = 2.0f;
publicfloatwaveAmplitude {get{return_waveAmplitude; }set{ _waveAmplitude = value; } }
// 波的顏色指數(shù)
[SerializeField]float_waveExponent = 22.0f;
publicfloatwaveExponent {get{return_waveExponent; }set{ _waveExponent = value; } }
// 波的時(shí)間間隔
[SerializeField]float_waveInterval = 20.0f;
publicfloatwaveInterval {get{return_waveInterval; }set{ _waveInterval = value; } }
// 波的速度
[SerializeField]float_waveSpeed = 10.0f;
publicfloatwaveSpeed {get{return_waveSpeed; }set{ _waveSpeed = value; } }
// 額外的顏色
[SerializeField] Color _addColor = Color.black;
publicColor addColor {get{return_addColor; }set{ _addColor = value; } }
[SerializeField] Shader shader;
intbaseColorID;
intwaveColorID;
intwaveParamsID;
intwaveVectorID;
intaddColorID;
voidAwake()
{
baseColorID = Shader.PropertyToID("_SonarBaseColor");
waveColorID = Shader.PropertyToID("_SonarWaveColor");
waveParamsID = Shader.PropertyToID("_SonarWaveParams");
waveVectorID = Shader.PropertyToID("_SonarWaveVector");
addColorID = Shader.PropertyToID("_SonarAddColor");
}
voidOnEnable()
{
GetComponent().SetReplacementShader(shader,null);
Update();
}
voidOnDisable()
{
GetComponent().ResetReplacementShader();
}
voidUpdate()
{
Shader.SetGlobalColor(baseColorID, _baseColor);
Shader.SetGlobalColor(waveColorID, _waveColor);
Shader.SetGlobalColor(addColorID, _addColor);
var param =newVector4(_waveAmplitude, _waveExponent, _waveInterval, _waveSpeed);
Shader.SetGlobalVector(waveParamsID, param);
if(_mode == SonarMode.Directional)
{
Shader.DisableKeyword("SONAR_SPHERICAL");
Shader.SetGlobalVector(waveVectorID, _direction.normalized);
}
else
{
Shader.EnableKeyword("SONAR_SPHERICAL");
Shader.SetGlobalVector(waveVectorID, _origin);
}
}
}
三赞厕、結(jié)語(yǔ)
最后可以看看這種方式實(shí)現(xiàn)的聲納波效果的性能消耗如何艳狐,我們可以在運(yùn)行的時(shí)候打開state看到如圖所示的統(tǒng)計(jì)圖,瞬間有沒有覺得這個(gè)效果
其實(shí)還蠻有實(shí)用價(jià)值的皿桑。