先上效果图
shader所用的贴图资源
扰动
直接对uv进行变换就可以了,记得首先把六边形格子地图的Tilling调高点 先预先调成合适大小的六边形,然后repeat铺满整个护盾
// Tiles and offsets the value of input UV by the inputs Tiling and Offset respectively.
// This is commonly used for detail maps and scrolling textures over Time.
void Unity_TilingAndOffset_float(float2 UV, float2 Tiling, float2 Offset, out float2 Out)
{
Out = UV * Tiling + Offset;
}
直接在vertex着色器中对appdata传进来的uv进行变动,两者 offset的变动频率不同
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
half2 uv = TRANSFORM_TEX(v.uv, _MainTex);
Unity_TilingAndOffset_float(uv, half2(1, 1), half2(0, -_Time.y), o.uv);
Unity_TilingAndOffset_float(uv, half2(1, 1), half2(0, -_Time.y * 2), o.uvGlow);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
护盾边缘光
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float2 uvGlow : TEXCOORD1;
float4 vertex : SV_POSITION;
half3 worldNormal : TEXCOORD2;
half3 worldViewDir : TEXCOORD3;
};
vertex着色器
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
...
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
o.worldNormal = UnityObjectToWorldNormal(v.normal);
...
return o;
}
fragment着色器
先获取世界坐标系下的法线向量和世界坐标系下摄像机到顶点的向量,1-点乘得到的值然后结合到最后的颜色输出计算中,法线向量与摄像机到顶点的向量的所成夹角可视为从中心点向边缘扩散,角度越来越大,到边缘区角度呈局部最大值
half4 frag (v2f i) : SV_Target
{
...
half rim = pow(1 - abs(dot(normalize(i.worldNormal), normalize(i.worldViewDir))), _RimNum);
// 边缘高光
col = lerp(col, _LightColor, rim);
...
return col;
}
全部代码
Shader "Unlit/ShieldForceField"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
[NoScaleOffset] _LightTex ("LightTex", 2D) = "white" {}
_Hologram ("Hologram", 2D) = "white" {}
[HDR] _LightColor ("LightColor", Color) = (1, 1, 1, 1)
// 边缘光幅度
_RimNum("Rim" , Range(0 , 5)) = 1
}
SubShader
{
Tags
{
"RenderType" = "Transparent"
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"ForceNoShadowCasting" = "True"
}
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "../../../../../Assets/ShaderGraphic.cginc"
sampler2D _MainTex, _LightTex, _Hologram;
float4 _MainTex_ST;
half4 _LightColor;
float _RimNum;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float2 uvGlow : TEXCOORD1;
float4 vertex : SV_POSITION;
half3 worldNormal : TEXCOORD2;
half3 worldViewDir : TEXCOORD3;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
half2 uv = TRANSFORM_TEX(v.uv, _MainTex);
Unity_TilingAndOffset_float(uv, half2(1, 1), half2(0, -_Time.y), o.uv);
Unity_TilingAndOffset_float(uv, half2(1, 1), half2(0, -_Time.y * 2), o.uvGlow);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
half4 frag (v2f i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
half4 light = tex2D(_LightTex, i.uv);
// light.rgb = light.r * _LightColor.rgb;
light.rgb = _LightColor.rgb;
col.rgb += light.rgb * _LightColor.a;
half4 holoColor = tex2D(_Hologram, i.uvGlow);
col += holoColor * 0.3;
col.a = 0.5;
half rim = pow(1 - abs(dot(normalize(i.worldNormal), normalize(i.worldViewDir))), _RimNum);
// 边缘高光
col = lerp(col, _LightColor, rim);
return col;
}
ENDCG
}
}
}
最后大功告成了,后续可能会继续改进一下(加上相交时会留下边缘光)