shadowGun是官方一个很早的demo,但是里面的东西真的是值得一学
1.烟火,大多数人都是选择粒子来做这样的烟火,在shadeGun中只是用了一个片
搞定了如此真实的烟火,效率上面真的是比粒子好的不是一般的多。
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "MADFINGER/Environment/Scroll 2 Layers Sine AlphaBlended" {
Properties {
_MainTex ("Base layer (RGB)", 2D) = "white" {} //贴图1
_DetailTex ("2nd layer (RGB)", 2D) = "white" {} //贴图2
_ScrollX ("Base layer Scroll speed X", Float) = 1.0 //贴图1的x方向速度
_ScrollY ("Base layer Scroll speed Y", Float) = 0.0 //贴图1的y方向速度
_Scroll2X ("2nd layer Scroll speed X", Float) = 1.0 //贴图2的x方向速度
_Scroll2Y ("2nd layer Scroll speed Y", Float) = 0.0 //贴图2的y方向速度
_SineAmplX ("Base layer sine amplitude X",Float) = 0.5 //贴图1 x方向的扭动幅度
_SineAmplY ("Base layer sine amplitude Y",Float) = 0.5 //贴图1 y方向的扭动幅度
_SineFreqX ("Base layer sine freq X",Float) = 10 //贴图1 x方向的扭动速度
_SineFreqY ("Base layer sine freq Y",Float) = 10 //贴图1 y方向的扭动速度
_SineAmplX2 ("2nd layer sine amplitude X",Float) = 0.5 //贴图2 x方向的扭动幅度
_SineAmplY2 ("2nd layer sine amplitude Y",Float) = 0.5 //贴图2 y方向的扭动幅度
_SineFreqX2 ("2nd layer sine freq X",Float) = 10 //贴图2 x方向的扭动速度
_SineFreqY2 ("2nd layer sine freq Y",Float) = 10 //贴图2 y方向的扭动速度
_Color("Color", Color) = (1,1,1,1) //主颜色
_MMultiplier ("Layer Multiplier", Float) = 2.0 //亮度
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
ZWrite Off
LOD 100
CGINCLUDE
#pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
#pragma exclude_renderers molehill
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _DetailTex;
float4 _MainTex_ST;
float4 _DetailTex_ST;
float _ScrollX;
float _ScrollY;
float _Scroll2X;
float _Scroll2Y;
float _MMultiplier;
float _SineAmplX;
float _SineAmplY;
float _SineFreqX;
float _SineFreqY;
float _SineAmplX2;
float _SineAmplY2;
float _SineFreqX2;
float _SineFreqY2;
float4 _Color;
struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
fixed4 color : TEXCOORD1;
};
///核心代码
v2f vert (appdata_full v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord.xy,_MainTex) + frac(float2(_ScrollX, _ScrollY) * _Time);//贴图1的uv动画 frac(x) 返回标量或矢量的小数
o.uv.zw = TRANSFORM_TEX(v.texcoord.xy,_DetailTex) + frac(float2(_Scroll2X, _Scroll2Y) * _Time);//贴图2的uv动画
o.uv.x += sin(_Time * _SineFreqX) * _SineAmplX;//每个方向做曲线变化,sin里面的影响变化速度,x在外面的参数影响曲线范围
o.uv.y += sin(_Time * _SineFreqY) * _SineAmplY;
o.uv.z += sin(_Time * _SineFreqX2) * _SineAmplX2;
o.uv.w += sin(_Time * _SineFreqY2) * _SineAmplY2;
o.color = _MMultiplier * _Color * v.color; //亮度 * 颜色 * 顶点色
return o;
}
ENDCG
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 frag (v2f i) : COLOR
{
fixed4 o;
fixed4 tex = tex2D (_MainTex, i.uv.xy);
fixed4 tex2 = tex2D (_DetailTex, i.uv.zw);
o = tex * tex2 * i.color;
return o;
}
ENDCG
}
}
}
总结,frag函数里很简单,也是他的高效率的原因之一,shader优化的方向,把能在顶点计算的东西都在顶点里面去计算。
2.模拟雾效,淡进淡出效果
GodRays
这里的雾效不是指那种真的全局环境都受影响的大雾,而是一种现象:在视角逐渐接近它的时候,视野逐渐清晰。例如对于体积光,从远处看它可能会感觉很亮,但越接近亮度越小,越能看清后面的物体。这种效果可以很好地让玩家感觉到深度的变化。ShadowGun的解决方法是使用一个简单的网格(Fog planes)+透明纹理来模拟。一旦玩家靠近时,通过减淡颜色+使网格顶点移开(需要移开的原因是因为,即使是完全透明的alpha面也会消耗很多渲染时间,而这里的移开一般是把网格收缩变小,减少透明区域)的方法来模拟这个效果。
而如果要使用这个Shader,就需要在三维软件中处理那么Fog planes:
- 顶点的透明度用于决定顶点是否可以移动(透明度为0表示不可移动,1为可移动)
- 顶点法线决定移动的方向
- 然后Shader通过计算与观察者的距离来控制雾面的淡入/淡出。
Shader "MADFINGER/Transparent/GodRays" {
Properties {
_MainTex ("Base texture", 2D) = "white" {}
_FadeOutDistNear ("Near fadeout dist", float) = 10 //最近距离
_FadeOutDistFar ("Far fadeout dist", float) = 10000 //最远距离
_Multiplier("Multiplier", float) = 1 //亮度
_ContractionAmount("Near contraction amount", float) = 5 //顶点移动的速度
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Blend One One
// Blend One OneMinusSrcColor
Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
LOD 100
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
float _FadeOutDistNear;
float _FadeOutDistFar;
float _Multiplier;
float _ContractionAmount;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
fixed4 color : TEXCOORD1;
};
//核心代码
v2f vert (appdata_full v)
{
v2f o;
float3 viewPos = mul(UNITY_MATRIX_MV,v.vertex);
float dist = length(viewPos);
float nfadeout = saturate(dist / _FadeOutDistNear); //如果小于了最近距离,开始渐渐出现,如果大于最近距离,总返回为1
float ffadeout = 1 - saturate(max(dist - _FadeOutDistFar,0) * 0.2); //后面的值是 dist <=最远距离时 返回为0,大于后开始返回越接近1,所以用1-
//结果就是 dist <=最远距离时 返回1,dist>最远距离时 渐渐为0 0.2是为0的速度
//下面就是要把这两个数值结合成一个数值,直接将两个值相乘就可以
//值A 值B
//1.当dist小于最小距离时候 它也必然小于最大距离 A返回的是0到1的数值 B返回的是1 所以相乘不影响结果
//2.当dist大于最小距离 小于最大距离的时候 A 返回的是1 B返回的是1 所以相乘也不影响结果
//3.当dist大于最大距离 它必然也大于最小距离 A 返回的是1 B返回的是1到0 所以相乘不影响结果
ffadeout *= ffadeout; //乘以自身,影响曲线的变化速度
nfadeout *= nfadeout; //乘以自身,影响曲线的变化速度
nfadeout *= nfadeout; //乘以自身,影响曲线的变化速度
nfadeout *= ffadeout; //实现了两个值的相乘,统一成一个数值,也是当作物体的透明度来使用 或者亮度来使用的一个0到1的值
float4 vpos = v.vertex; //顶点
vpos.xyz -= v.normal * saturate(1 - nfadeout) * v.color.a * _ContractionAmount; //沿顶点法线的反方向缩小模型,结合了顶点色的alpha值,需要动的顶点alpha为1,不需要动的顶点alpha为0 (一般起点不动,其他点向起点进行缩放)结合了强度;
//这里用1-nafadeout,因为当alpha值为1的时候,模型不需要缩放,而当alpha值表小的时候 开始缩放,当alpha值为0的时候 模型也缩放为0
o.uv = v.texcoord.xy;
o.pos = mul(UNITY_MATRIX_MVP, vpos);//顶点裁切(在缩放模型之后)
o.color = nfadeout * v.color * _Multiplier; //颜色的亮度 用上面得出的亮度值 x 顶点色的颜色 x 强度
return o;
}
ENDCG
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
fixed4 frag (v2f i) : COLOR
{
return tex2D (_MainTex, i.uv.xy) * i.color;
}
ENDCG
}
}
}
-
3.闪烁的灯光 这里涉及到比较喜欢的参数 灯光亮暗的时间长度控制
- 跟上面想比 改了vert部分而已
-
Blinking GodRays
-
Shader "MADFINGER/Transparent/Blinking GodRays" { Properties { _MainTex ("Base texture", 2D) = "white" {} _FadeOutDistNear ("Near fadeout dist", float) = 10 //最近距离 _FadeOutDistFar ("Far fadeout dist", float) = 10000 //最远距离 _Multiplier("Color multiplier", float) = 1 //颜色亮度 _Bias("Bias",float) = 0 _TimeOnDuration("ON duration",float) = 0.5 //颜色为亮的时间 _TimeOffDuration("OFF duration",float) = 0.5 //颜色为暗的时间 _BlinkingTimeOffsScale("Blinking time offset scale (seconds)",float) = 5 //起始时间 _SizeGrowStartDist("Size grow start dist",float) = 5 // _SizeGrowEndDist("Size grow end dist",float) = 50 _MaxGrowSize("Max grow size",float) = 2.5 _NoiseAmount("Noise amount (when zero, pulse wave is used)", Range(0,0.5)) = 0 _Color("Color", Color) = (1,1,1,1) } SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } Blend One One // Blend One OneMinusSrcColor Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) } LOD 100 CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex; float _FadeOutDistNear; float _FadeOutDistFar; float _Multiplier; float _Bias; float _TimeOnDuration; float _TimeOffDuration; float _BlinkingTimeOffsScale; float _SizeGrowStartDist; float _SizeGrowEndDist; float _MaxGrowSize; float _NoiseAmount; float4 _Color; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; fixed4 color : TEXCOORD1; }; //核心代码 v2f vert (appdata_full v) { v2f o; float time = _Time.y + _BlinkingTimeOffsScale * v.color.b; float3 viewPos = mul(UNITY_MATRIX_MV,v.vertex); float dist = length(viewPos); float nfadeout = saturate(dist / _FadeOutDistNear); float ffadeout = 1 - saturate(max(dist - _FadeOutDistFar,0) * 0.2); float fracTime = fmod(time,_TimeOnDuration + _TimeOffDuration);//fmod(x,y):返回x/y的余数,如果y为0,结果不可预料 float wave = smoothstep(0,_TimeOnDuration * 0.25,fracTime) * (1 - smoothstep(_TimeOnDuration * 0.75,_TimeOnDuration,fracTime));//smoothstep(min,max,x):如果x=min,返回0;如果x=max,返回1;如果x在两者之间,返回0到1之间的数字 float noiseTime = time * (6.2831853f / _TimeOnDuration); float noise = sin(noiseTime) * (0.5f * cos(noiseTime * 0.6366f + 56.7272f) + 0.5f);//这个好懵逼 是不是复杂了点 只是改变一下曲线 也是拼 0—1 * ((0-0.5)+0.5) 结果是0到1 的参数 float noiseWave = _NoiseAmount * noise + (1 - _NoiseAmount);//noise的程度 float distScale = min(max(dist - _SizeGrowStartDist,0) / _SizeGrowEndDist,1); //放大模型的参数 wave = _NoiseAmount < 0.01f ? wave : noiseWave;//当这个值小于0.01 就用wave,反之就用noiseWave distScale = distScale * distScale * _MaxGrowSize * v.color.a;//放大模型的参数 wave += _Bias;//wave本为0到1的值,+上这个参数的时候 可以让灯闪的时候不至于全黑 把参数改变成_Bias 到 1+_Bias之间 ffadeout *= ffadeout; nfadeout *= nfadeout; nfadeout *= nfadeout; nfadeout *= ffadeout; float4 mdlPos = v.vertex; mdlPos.xyz += distScale * v.normal;//正真的放大模型 o.uv = v.texcoord.xy; o.pos = mul(UNITY_MATRIX_MVP, mdlPos); o.color = nfadeout * _Color * _Multiplier * wave; return o; } ENDCG Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest fixed4 frag (v2f i) : COLOR { return tex2D (_MainTex, i.uv.xy) * i.color; } ENDCG } } }
亲,如果您觉得本文不错,愿意给我一些动力的话,请用手机扫描二维码即可向我打赏
打赏