Unity制作晶格护盾
大家好,我是阿赵。
继续来做护盾,这一期做一个蜂窝晶体护盾的效果。
这个晶体护盾的特点是,整个护盾是由很多五边形和六边形的晶体构成,每一块晶体的颜色都在不停的变化,然后每一块晶体都可以单独的浮动。
当鼠标点击模拟攻击护盾的时候,被攻击的护盾会浮起来做一个波浪往四周扩散。
要做这个效果,需要美术资源和shader相结合。
怎样制作这个球体呢?可以参考一下这个步骤:
(1)创建一个GeoSphere
(2)塌陷为EditablePoly
(3)选择所有顶点
(4)给所有顶点做一个倒角
接下来我们要把这些五边形和六边形的面分离,具体做法是选择所有的顶点,然后break一下。
现在所有的面都是独立的了。
这个效果的一个重点,就是展UV。我们先把刚才所有的面正常的展开,不要重叠。
然后选中所有的面,使用Tools/Relex工具,类型选中Relex By Centers,点击Start Relex按钮
这里其实很简单,就是用一张噪声图做UV动画而已。那么为什么看起来是每一个晶体的颜色都是整体变化,而看不出来是噪声图渐变呢?这是因为上面把所有面的UV都缩成一个点了,所以每个面在采样的时候,只会采样到噪声图的一个像素,看起来就是每个面的颜色都是独立的。
还是刚才那张噪声图,然后在顶点着色器程序进行采样,得到了每个晶体不同深浅的颜色,然后根据这个不同深浅,沿着法线方向做偏移而已。
不过这里有一个问题,tex2D采样只能用在片段着色器程序,如果我们想在顶点着色器程序采样,要用tex2Dlod方法。
点击扩散,其实就是上一篇文章里面的冲击波护盾的做法了。只不过冲击波护盾改变的是颜色,我们这里改变的是形状,所以需要在顶点着色器程序里面写。同意通过数组传入最多10个顶点信息和扩散大小。
C#代码和冲击波护盾的时候一样。下面提供一下Shader的写法
Shader "azhao/GridShield"
{
Properties
{
_NoiseTex("NoiseTex", 2D) = "white" {}
_dir("dir", Vector) = (0,0,0,0)
_speed("speed", Range(0 , 2)) = 1
_offsetVal("offsetVal", Range(-1 , 1)) = 1
[HDR]_baseColor("baseColor", Color) = (1,1,1,0)
_alphaMul("alphaMul", Range(0 , 1)) = 0
_diffuse("diffuse", Range(0 , 1)) = 0
_gradient("gradient", 2D) = "white" {}
_minOffset("minOffset",float) = 0.1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha, SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv :TEXCOORD0;
float3 normal: NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 vertex_world:TEXCOORD1;
float3 normal_world:TEXCOORD2;
};
uniform sampler2D _NoiseTex;
SamplerState sampler_NoiseTex;
uniform float2 _dir;
uniform float _speed;
uniform float _offsetVal;
uniform sampler2D _gradient;
SamplerState sampler_gradient;
uniform float _diffuse;
uniform float4 _baseColor;
uniform float _alphaMul;
float3 _hitCenter[10];
float _hitSize[10];
float _minOffset;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.normal_world = UnityObjectToWorldNormal(v.normal);
float4 tex2DNode2 = tex2Dlod(_NoiseTex, float4((v.uv + (_dir * frac((_Time.y * _speed)))), 0, 0.0));
float3 vertexValue = float3(0, 0, 0);
for (int i = 0; i < 10; i++)
{
float2 appendResult = (float2(((distance(_hitCenter[i], worldPos) - _hitSize[i]) / _diffuse), 0.0));
float clampResult = clamp((tex2Dlod(_gradient, float4(appendResult, 0, 0.0)).r + (max(_hitSize[i], 0.0) * -1.0)), 0.0, 1.0);
clampResult = max(clampResult, _minOffset);
vertexValue += (o.normal_world * tex2DNode2.r * _offsetVal * (clampResult * 0.5));
}
o.uv = v.uv;
v.vertex.xyz += vertexValue;
o.vertex = UnityObjectToClipPos(v.vertex);
o.vertex_world = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
half4 frag (v2f i) : SV_Target
{
float4 tex2DNode2 = tex2D(_NoiseTex, (i.uv + (_dir * frac((_Time.y * _speed)))));
half4 finalColor = (float4((_baseColor * tex2DNode2).rgb , max((max(tex2DNode2.r , 0.1) * _alphaMul) , 0.0)));
return finalColor;
}
ENDCG
}
}
}