这篇文章中效果的实现思路主要参考自这里:Geometry Shader学习笔记 游戏Rime中的物体溶解效果: 在之前的基础上增加了以下内容:
- 溶解方块拖尾长度与偏移程度的控制;
- 动画持续时间的控制;
- 按照模型的比例来计算动画,不再受模型位置与大小的影响。
最后的效果如下: shader代码与C#代码如下,需要用脚本来传入模型的位置信息,以及启动动画播放计时器。
Shader "Unlit/Dissolve"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_StartHeightPos("StartPos",Float) = 1.0
_EndHeightPos("EndHeightPos",Float) = 0.0
_DissolveColor("DissolveColor",Color) = (1.0, 1.0, 1.0, 1.0)
_DissolveColorThreshholdPercent("DissolveColorThreshhold",Range(0.05,1)) = 0.5
_BoxColor("BoxColor",Color) = (1.0, 1.0, 1.0, 1.0)
_BoxSize("BoxSize",Range(0.002,0.2)) = 0.1
_BoxTrailLength("BoxTrailLength",Range(0.05 ,1.0)) = 0.2
_BoxTrailSwing("BoxTrailSwing",Range(1,50)) = 1
_Timer("Timer",Float) = 0.0
_LifeTime("LifeTime",Float) = 2.0
}
SubShader
{
Tags { "LightMode"="ForwardBase" }
LOD 100
CGINCLUDE
#include "UnityCG.cginc"
struct v2f{
float2 uv : TEXCOORD0;
float3 normalWorld : NORMAL;
float4 vertex : SV_POSITION;
float4 objPos : TEXCOORD1;
};
struct v2g
{
float4 vertex : POSITION;
float3 normalWorld : NORMAL;
};
struct g2f
{
float difference : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _StartHeightPos;
float _EndHeightPos;
float _DissolveColorThreshholdPercent;
fixed4 _DissolveColor;
float _BoxSize;
float _BoxTrailLength;
float _BoxTrailSwing;
fixed4 _BoxColor;
float _Timer;
float _LifeTime;
float calculateDifferenceY(float y){
float playProgressInv = 1.0 -_Timer / _LifeTime;
float height = _StartHeightPos - _EndHeightPos;
float vertexHeightPercent = (y - _EndHeightPos) / height;
float difference = playProgressInv - vertexHeightPercent;
return difference;
}
v2f vertVanish (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.objPos = v.vertex;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.normalWorld = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 fragVanish (v2f i) : SV_Target
{
float difference = calculateDifferenceY(i.objPos.y);
clip(difference);
if(difference < _DissolveColorThreshholdPercent){
return _DissolveColor;
}
float3 light = normalize(UnityWorldSpaceLightDir(mul(unity_ObjectToWorld, i.objPos)));
float diffuse = abs(dot(normalize(i.normalWorld), light));
fixed4 col = tex2D(_MainTex, i.uv);
return fixed4(col.rgb * diffuse, 1.0);
}
v2g vertParticle(appdata_base v){
v2g o;
o.vertex = v.vertex;
o.normalWorld = UnityObjectToWorldNormal(v.normal);
return o;
}
#define ADD_VERTEX(v) o.vertex = UnityObjectToClipPos(v); triStream.Append(o);
#define ADD_TRIANGLE(v0, v1, v2) ADD_VERTEX(v0) ADD_VERTEX(v1) ADD_VERTEX(v2)
#define ADD_CUBE(v0, v1, v2, v3, v4, v5, v6, v7) ADD_TRIANGLE(v0, v1, v2) ADD_TRIANGLE(v2, v3, v0)\
ADD_TRIANGLE(v4, v5, v7) ADD_TRIANGLE(v7, v5, v6)\
ADD_TRIANGLE(v2, v4, v3) ADD_TRIANGLE(v3, v4, v7)\
ADD_TRIANGLE(v5, v3, v6) ADD_TRIANGLE(v6, v3, v0)\
ADD_TRIANGLE(v5, v4, v1) ADD_TRIANGLE(v1, v4, v2)\
ADD_TRIANGLE(v0, v3, v6) ADD_TRIANGLE(v6, v3, v7)
[maxvertexcount(36)]
void geomParticle(triangle v2g IN[3], inout TriangleStream<g2f> triStream){
g2f o;
float3 centerPos = ( IN[0].vertex + IN[1].vertex + IN[2].vertex).xyz/3.0;
float3 edge1 = (IN[1].vertex - IN[0].vertex).xyz;
float3 edge2 = (IN[2].vertex - IN[0].vertex).xyz;
float3 faceNormal = normalize(cross(edge1, edge2));
float difference = calculateDifferenceY(centerPos.y);
o.difference = difference;
centerPos = centerPos + faceNormal * _BoxTrailSwing * saturate(-difference);
float4 v0 = float4(-1, -1, -1, 0.0) * _BoxSize + float4(centerPos,1.0);
float4 v1 = float4(-1, 1, -1, 0.0) * _BoxSize + float4(centerPos,1.0);
float4 v2 = float4( 1, 1, -1, 0.0) * _BoxSize + float4(centerPos,1.0);
float4 v3 = float4( 1, -1, -1, 0.0) * _BoxSize + float4(centerPos,1.0);
float4 v4 = float4( 1, 1, 1, 0.0) * _BoxSize + float4(centerPos,1.0);
float4 v5 = float4(-1, 1, 1, 0.0) * _BoxSize + float4(centerPos,1.0);
float4 v6 = float4(-1, -1, 1, 0.0) * _BoxSize + float4(centerPos,1.0);
float4 v7 = float4( 1, -1, 1, 0.0) * _BoxSize + float4(centerPos,1.0);
ADD_CUBE(v0, v1, v2, v3, v4, v5, v6, v7);
triStream.RestartStrip();
}
fixed4 fragParticle(g2f i): SV_TARGET{
float difference = i.difference;
clip(-difference);
return fixed4(_BoxColor.rgb, smoothstep(-_BoxTrailLength,0, difference));
}
ENDCG
Pass
{
Tags{"Queue"="Geometry" "RenderType"="Opaque"}
Cull Off
CGPROGRAM
#pragma vertex vertVanish
#pragma fragment fragVanish
ENDCG
}
pass{
Tags{"Queue"="Transparent" "RenderType"="Transparent"}
Cull Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vertParticle
#pragma geometry geomParticle
#pragma fragment fragParticle
ENDCG
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayAnimation : MonoBehaviour
{
[SerializeField] GameObject go;
[SerializeField] Material DissolveMat;
float t0 = 0, timer = 0;
void Start()
{
}
public void StartButtonClick()
{
t0 = Time.time;
Vector3 center = go.GetComponent<MeshFilter>().mesh.bounds.center;
Vector3 extents = go.GetComponent<MeshFilter>().mesh.bounds.extents;
float startHeightPos = center.y + extents.y * 1.2f;
float endHeightPos = center.y - extents.y;
DissolveMat.SetFloat("_Timer", 0);
DissolveMat.SetFloat("_StartHeightPos", startHeightPos);
DissolveMat.SetFloat("_EndHeightPos", endHeightPos);
}
void Update()
{
timer = Time.time - t0;
DissolveMat.SetFloat("_Timer", timer);
}
}
效果虽然实现了,但是与游戏中的原效果比起来有较大差异,存在许多不足之处,如下:
- 最主要的一个问题,物体溶解后无法封闭上方区域;
- 必须要获取模型的包围盒数据;
- 可以加入手动调整溶解后方块飞行的方向,同时使用噪声贴图;
- 未添加泛光效果。
最后想了想还是叫做物体外壳溶解效果吧哈哈哈。
|