您现在的位置是:首页 > 正文

Unity:圆底烧瓶中液体液面升降变化的效果

2024-02-01 05:40:21阅读 3

一、效果展示

请添加图片描述

二、实现的原理

1、从image的filled模式说起

image的filled模式,适合用来做进度条:
在这里插入图片描述
请添加图片描述

2、能否为一个3D object实现一个image filled 的shader ?

Shader "Custom/FilledImageEffect"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1, 1, 1, 1)
        _FillAmount ("Fill Amount", Range(0, 1)) = 1
    }

    SubShader
    {
        Tags {"Queue"="Transparent" "RenderType"="Transparent"}
        LOD 100

        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;
        float4 _Color;
        float _FillAmount;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            float2 uv = IN.uv_MainTex;
            float4 c = tex2D(_MainTex, uv) * _Color;

            if (uv.x > _FillAmount)
            {
                c.a = 0;
            }

            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

3、液面变化的Shader

Shader "Custom/LiquidBottle"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _MainColor("MainColor",Color) = (1, 1, 1, 1)
        _TopColor("TopColor",Color) = (1, 1, 0, 1)
        _FillAmount("FillAmout",Range(-1 , 2)) = 0
        _EdgeWidth("EdgeWidth",Range(0 , 1)) = 0.2

        _BottleWidth("BottleWidth",Range(0,1)) = 0.2
        _BottleColor("BottleColor",Color) = (1,1,1,1)

        _RimColor("RimColor",Color)=(1,1,1,1)
        _RimWidth("RimWidth",float)=0.2

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Cull OFF
            AlphaToMask on
            //ZWrite On
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
               
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float fillEdge : TEXCOORD1;
                float3 viewDir : COLOR;
                float3 normal : COLOR2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            float4 _MainColor;
            float _FillAmount;
            float4 _TopColor;
            float _EdgeWidth;

            float4 _RimColor;
            float _RimWidth;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                o.fillEdge=mul(unity_ObjectToWorld,v.vertex.xyz).y+_FillAmount;
                o.normal=v.normal;
                o.viewDir=normalize(ObjSpaceViewDir(v.vertex));
                return o;
            }

            fixed4 frag (v2f i,fixed facing : VFace) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv) * _MainColor;
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);

                float dotProduct = 1-pow(dot(i.normal, i.viewDir),_RimWidth);
                float4 rimCol=_RimColor*smoothstep(0.5,1,dotProduct);

                fixed4 edgeVal=step(i.fillEdge,0.5)-step(i.fillEdge,0.5-_EdgeWidth);
                fixed4 edgeCol=edgeVal *= _TopColor*0.9;

                fixed4 finalVal=step(i.fillEdge,0.5)-edgeVal;
                fixed4 finalCol=finalVal*col;
                finalCol+=edgeCol+rimCol;

                 fixed4 topCol=_TopColor * (edgeVal+finalVal);
                // float dotVal = 1- pow(dot(i.normal, i.viewDir),0.3);
                // return float4(dotVal,dotVal,dotVal,1);
                return facing > 0 ? finalCol : topCol;
            }
            ENDCG
        }

        Pass
        {
            //Cull Front
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float4 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float3 viewDir : COLOR;
                float3 normal :NORMAL;
            };

            float4 _BottleColor;
            float _BottleWidth;

            float4 _RimColor;
            float _RimWidth;

            v2f vert (appdata v)
            {
                v2f o;
                v.vertex.xyz+=v.normal.xyz*_BottleWidth;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                UNITY_TRANSFER_FOG(o,o.vertex);
                o.normal=v.normal.xyz;
                o.viewDir=normalize(ObjSpaceViewDir(v.vertex));

                return o;
            }

            fixed4 frag (v2f i,fixed facing : VFace) : SV_Target
            {
                // sample the texture
                fixed4 col = _BottleColor;
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);

                float dotProduct = 1-pow(dot(i.normal, i.viewDir),_RimWidth);//1-pow(dot(i.normal, i.viewDir),_RimWidth/10);
                float4 rimCol=_RimColor*smoothstep(0.2,1,dotProduct);
              
                return rimCol;
            }
            ENDCG
        }
    }
}

三、服用方法

  • 1、创建一个液面效果的材质
    在这里插入图片描述

  • 2、把这个材质挂载到瓶子中的【液体】模型上

  • 3、在面板上拖动FillAmout

  • 4、在脚本中设置mat的该属性
    mat.SetFloat(“_FillAmount”, currentValue);

四、附录:C#调用代码

1、液面升降动画的异步方法

/// <summary>
/// 容器的液面变化:化学实验中,量筒中倒入液体或者被吸取液体后,液面会升降
/// 材质用shader,控制其fillAmout参数
/// </summary>
/// <param name="mat">操作的材质</param>
/// <param name="levelStart">起始液面位置</param>
/// <param name="levelEnd">结束页面位置</param>
/// <param name="duration">动画的时间</param>
/// <returns></returns>
public static async UniTask DoLiquidLevel(Material mat, float levelStart, float levelEnd, float duration)
{
    float currentValue = levelStart;
    float timeElapsed = 0f;
    while (timeElapsed < duration)
    {
        currentValue = Mathf.Lerp(levelStart, levelEnd, timeElapsed / duration);
        Debug.Log(currentValue);
        mat.SetFloat("_FillAmount", currentValue);      //变量名字确保与Shader源码中的变量一致。
        await UniTask.Yield();
        timeElapsed += Time.deltaTime;
    }
    mat.SetFloat("_FillAmount", levelEnd);              //确保最终结果准确
}

2、monobehaviour示例脚本

using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using static txlib;//包含 DoLiquidLevel()

/// <summary>
/// 容器的液面升降控制
/// </summary>
public class ChangeLiquidLevel : MonoBehaviour
{
    /// <summary>
    /// 物体
    /// </summary>
    public GameObject obj;

    /// <summary>
    /// 起始液面
    /// </summary>
    public float levelStart;

    /// <summary>
    /// 终止液面
    /// </summary>
    public float levelEnd;

    /// <summary>
    /// 动画耗时
    /// </summary>
    public float duration;
    
    /// <summary>
    /// 材质
    /// </summary>
    private Material mat;

    [ContextMenu("测试液面")]
    async UniTask Test()
    {
        mat = obj.GetComponent<Renderer>().sharedMaterial;
        while (true)
        {
            await DoLiquidLevel(mat, levelStart, levelEnd, duration);
            await DoLiquidLevel(mat, levelEnd, levelStart, duration);
            await UniTask.Delay(TimeSpan.FromSeconds(0.8f));
        }
    }
}

网站文章

  • Gstreamer利用category调试

    之前总结过category的定义和使用,基于这个基础,发现利用gstreamer的categories和log系统,调试起来效率会提升很多。这两个例子都是Gstreamer中基础的部分,其他的elem...

    2024-02-01 05:40:14
  • 基本算法之枚举.205.余数相同问题

    求余

    2024-02-01 05:40:07
  • selenium自动化截图的方式

    自动化截图的三种方式

    2024-02-01 05:39:37
  • idea基本建立工程与注意事项

    1.src–&gt;new–&gt;Package 2.File–&gt;New–&gt;Module 3.File–&gt;New–&gt;Project 4.File–&gt;Project Structure 5.具体包–&gt;New --&gt;Java Class 6.remove–&gt;delete后检查.idea下的modules.xml

    2024-02-01 05:39:31
  • BUUCTF:[watevrCTF-2019]Pickle Store

    BUUCTF:[watevrCTF-2019]Pickle Store

    题目地址:https://buuoj.cn/challenges#[watevrCTF-2019]Pickle%20Store Pickle反序列化 使用pickle.loads()和base64对这...

    2024-02-01 05:39:25
  • 阿里云网盘和百度网盘功能体验上谁更胜一筹呢?

    阿里云网盘和百度网盘功能体验上谁更胜一筹呢?

    阿里云网盘和百度网盘功能体验上谁更胜一筹呢?由阿里巴巴推出的阿里云网盘即将上线,相信这对于百度网盘来说将是一个强劲的对手,会流失许多的用户。那么阿里云网盘和百度网盘哪个好?这两个网盘在功能和体验上谁更...

    2024-02-01 05:39:18
  • Element级联选择器自定义value label

    Element级联选择器自定义value label

    实际项目中后端返回的数据属性可能不是以`label/value`等命名的,这时候数据就渲染不上去。我们需要利用`props`指定选项的值为选项对象的某个属性值。

    2024-02-01 05:38:47
  • LeetCode-160. 相交链表

    LeetCode-160. 相交链表

    于是有一个巧妙的思路诞生了,如果让短的先走完自己的再走长的,长的走完走短的,他俩就会正好对齐。如果让他们尾端队齐,那么从后面遍历就会很快找到第一个相交的点。

    2024-02-01 05:38:21
  • 2021-07-23

    2021-07-23

    com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure遇到这个问题,刚开始很懵,于是疯狂百度,大多数都是让修改等待时间,但对我来说无济于事,然后就想着修改代码,于是,发现成功啦!上图:另:数据库版本和驱动jar包不一样,就在注册驱动时找到jdbc的上级目录为止...

    2024-02-01 05:37:47
  • 初学Java Web(3)——第一个Servlet

    初学Java Web(3)——第一个Servlet

    这学期 Java Web 课程的第一节课就简短复习了一下 Java 的一些基础知识,所以觉得 Java 的基础知识还是很重要的,但当我想要去写一篇 Java 回顾的文章的时候发现很难,因为坑实在太多了(一个头,两个大),只能另外找一些时间去写顺便巩固巩固自己的基础。——【1. 什么是 Servlet 】——学习一个东西就要先去了解它是什么东西。Servlet 取自两个单词:Se...

    2024-02-01 05:37:40