IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity Geometry Shader 物体溶解效果 -> 正文阅读

[游戏开发]Unity Geometry Shader 物体溶解效果

这篇文章中效果的实现思路主要参考自这里:Geometry Shader学习笔记
游戏Rime中的物体溶解效果:
请添加图片描述
在之前的基础上增加了以下内容:

  1. 溶解方块拖尾长度与偏移程度的控制;
  2. 动画持续时间的控制;
  3. 按照模型的比例来计算动画,不再受模型位置与大小的影响。

最后的效果如下:
请添加图片描述
请添加图片描述
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;

        //计算出当前时刻中溶解高度(取决于动画播放进度)与任意顶点位置之差,用百分比表示,溶解过的部分值小于0,未溶解的部分大于0
        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
        {
            // 计算差值,溶解过的部分值小于0,未溶解的部分大于0,方便裁剪和计算溶解环
            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);

            // 方块的八个顶点以及12个三角形面,顺时针为正方向
            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;

            // 裁减掉尚未溶解的部分,取反使其小于0
            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;

    // Start is called before the first frame update
    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);
    }

    // Update is called once per frame
    void Update()
    {
        timer = Time.time - t0;
        DissolveMat.SetFloat("_Timer", timer);
    }
}

效果虽然实现了,但是与游戏中的原效果比起来有较大差异,存在许多不足之处,如下:

  1. 最主要的一个问题,物体溶解后无法封闭上方区域;
  2. 必须要获取模型的包围盒数据;
  3. 可以加入手动调整溶解后方块飞行的方向,同时使用噪声贴图;
  4. 未添加泛光效果。

最后想了想还是叫做物体外壳溶解效果吧哈哈哈。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 18:15:32  更:2022-04-18 18:17:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 20:53:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码