Shader案例篇—声纳光波效果(转)

转自http://www.manew.com/thread-98405-1-1.html

一、前言

终于周末了,心心挂念的依然还是那段代码。没办法,苦逼的命。还是废话少说,先上效果图如图所示,前面两个的效果图是两种模式下的

不同效果,最后一章图片是在其中一种模式下,场景进行了精心布置后的效果图。

看效果图会不自觉的认为实现这个效果是不是要在场景中的物体中进行绘制,如果真是要这样做的话那是在是太耗性能了(毕竟都工作了,本人毕业

了就不想在搞那些不实用的

)。其实,这个是通过控制摄像机的最后渲染来实现的效果的,后面我会给出这个案例的工程文件下载地址。读者

在运行模式的Scene视图里将看到场景的物体(立方体)并没有被额外渲染,有了这个思路对于后面理解代码的实现原理会很有帮助。

二、原理

1、Shader部分—光照计算

这里用到的光照计算都比较简单,光照模式我使用了最简单的漫反射模式,代码如下

//光照计算

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);

如图所示为漫反射的示意图,它的计算公式为

详细的介绍和代码可以参考这里

2、Shader部分—声纳光波的计算

Shader代码如下:

//声纳光波计算

#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;

代码中使用了预编译命令,是考虑了前面说的效果图中的两种模式的切换。为此,还必须在前面添加预编译命令

[C#]纯文本查看复制代码

#pragma multi_compile SONAR_DIRECTIONAL SONAR_SPHERICAL

这个命令的大概意思就是说可以将这两个模式的Shader一起编译,然后就可以在C#代码里通过

[C#]纯文本查看复制代码

Shader.DisableKeyword("SONAR_SPHERICAL");

或者

[C#]纯文本查看复制代码

?

Shader.EnableKeyword("SONAR_SPHERICAL");

来进行切换

完整顶点片段Shader代码如下:

[C#]纯文本查看复制代码

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;

//光照计算

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);

//声纳光波计算

#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

}

}

}

喜欢用简洁的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"

}

这个代码看似简单,但是对于处在学习基础原理阶段的童鞋,我的建议还是多动手写写顶点片段Shader,实现简单的光照模式、顶点变换等等对于学

习Shader会非常有帮助的。

3、C#脚本部分

脚本部分最关键的就是使用上述的Shader去渲染摄像机,这个需要通过如下的代码来实现吗,这段代码的意思是替换成这个Shader来渲染摄像机

[C#]纯文本查看复制代码

GetComponent().SetReplacementShader(shader,null);

其他的功能就是出传递参数给Shader来实现最终随时间变换的效果,完整的代码如下:




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; } }

// 声纳波区域(仅仅在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; } }

// 波的颜色指数

[SerializeField]float_waveExponent = 22.0f;

publicfloatwaveExponent {get{return_waveExponent; }set{ _waveExponent = value; } }

// 波的时间间隔

[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);

}

}

}

三、结语

最后可以看看这种方式实现的声纳波效果的性能消耗如何,我们可以在运行的时候打开state看到如图所示的统计图,瞬间有没有觉得这个效果

其实还蛮有实用价值的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容