ue3dUnity游戏对象淡出/剔除效果简单设置方法

Unity游戏对象淡出/剔除效果简单设置方法

分类:
ue3d - Unity游戏对象淡出/剔除效果简单设置方法

在这篇文章中,将展示一个用于Unity自上而下游戏对象的淡出剔除系统,达到不遮挡对象的目的。该系统设置具有 3 种不同的基于着色器的效果,以淡出查看到玩家对象。

Unity 中需要两个类来管理剔除系统:

可剔除脚本

Cullable.cs :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cullable : MonoBehaviour
{
    //speed of the transition
    public float m_alphaChangeSpeed = 3f;

    // name of the variable in the shader that will be adjusted
    public string m_shaderVariableName = "_PosSlider";

    // end point of culling
    public float m_fadeTo = 0;
    // start point of culling
    public float m_fadeFrom = 1;

    // the float for the transition
    private float m_currentAlpha = 1.0f;

    // The material that will be affected by the transition 
    private Material m_mat;
    // Set to true if we want to fade the object out because it's in the way
    private bool m_occluding;

    // Set to true when the fade control co-routine is active
    private bool m_inCoroutine = false;


    public bool Occluding
    {
        get { return m_occluding; }
        set
        {
            if (m_occluding != value)
            {
                m_occluding = value;
                OnOccludingChanged();
            }
        }

    }

    // Called when the Occluding value is changed
    private void OnOccludingChanged()
    {
        if (!m_inCoroutine)
        {

            StartCoroutine("FadeAlphaRoutine");
            m_inCoroutine = true;


        }
    }
    // Start is called before the first frame update
    void Start()
    {
        // grab the renderer's material and set the current alpha 
        m_mat = GetComponent<Renderer>().material;
        m_currentAlpha = m_fadeFrom;
    }


    // Return the alpha value we want on all of our models
    private float GetTargetAlpha()
    {
        if (m_occluding)
        {
            return m_fadeTo;
        }
        else
        {
            return m_fadeFrom;
        }
    }

    private IEnumerator FadeAlphaRoutine()
    {
        while (m_currentAlpha != GetTargetAlpha())
        {
            float alphaShift = m_alphaChangeSpeed * Time.deltaTime;

            float targetAlpha = GetTargetAlpha();
            if (m_currentAlpha < targetAlpha)
            {
                m_currentAlpha += alphaShift;
                if (m_currentAlpha > targetAlpha)
                {
                    m_currentAlpha = targetAlpha;
                }
            }
            else
            {
                m_currentAlpha -= alphaShift;
                if (m_currentAlpha < targetAlpha)
                {
                    m_currentAlpha = targetAlpha;
                }
            }

            m_mat.SetFloat(m_shaderVariableName, m_currentAlpha);

            yield return null;
        }
        m_inCoroutine = false;
    }
}

该组件将位于场景中想要隐藏的对象上,并通过与着色器对话来管理它们的淡入和淡出。它实际上并没有检测到对象何时需要褪色,另一个类会这样做,稍后会介绍。

cullable 类有一个 bool m_occluding 来存储组件的对象是否妨碍重要的事情。当它为真时,组件想要隐藏它的游戏对象,当它为假时它想再次取消隐藏它。

m_occluding 改变时,启动一个名为 FadeAlphaRoutine 的协程,它负责实际淡入和淡出对象。它用:

  • m_currentAlpha – 这是我们在着色器中输入的实际值,用于告诉它使对象淡出多少
  • m_fadeTo – 当对象被遮挡时,这就是想要隐藏它的 alpha 值。
  • m_fadeFrom – 当对象不被遮挡时,这就是我们想要的 alpha 值以取消隐藏它
  • m_alphaChangeSpeed – 这是每秒可以更改多少m_currentAlpha

协程的工作是随着时间的推移改变 m_currentAlpha 直到它达到我们想要的目标值。使用类似于 LERP. 的简单 将值移动到目标 算法来做到这一点。

这是伪代码:

ue3d - Unity游戏对象淡出/剔除效果简单设置方法

剔除管理

CullingManager.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CullingManager : MonoBehaviour
{

    public float m_occlusionCapsuleHeight = 0f;

    public float m_occlusionCapsuleRadius = 1f;

    // list of objects that will trigger the culling effect
    public List<GameObject> m_importantObjects = new List<GameObject>();

    // include the mouse in the important objects
    public bool m_includeMouse;

    public LayerMask m_layerMask;


    // List of all the objects that we've set to occluding state
    private List<Cullable> m_occludingObjects = new List<Cullable>();

    
    List<Cullable> cullableList = new List<Cullable>();


    

    // Update is called once per frame // Handle per frame logic
    public void Update()
    {
        // Can only do occlusion checks if we have a camera
        if (Camera.main != null)
        {
            // This is the list of positions we're trying not to occlude
            List<Vector3> importantPositions = FindImportantPositions();

            // This is the list of objects whihc are in the way
            List<Cullable> newOccludingObjects = FindOccludingObjects(importantPositions);

            SetOccludingObjects(newOccludingObjects);
        }
    }

    private List<Vector3> FindImportantPositions()
    {
        List<Vector3> positions = new List<Vector3>();


        // All units are important
        foreach (GameObject unit in m_importantObjects)
        {
            positions.Add(unit.transform.position);
        }


        if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit, 100, m_layerMask) && m_includeMouse)
        {
            Vector3 mousePos = hit.point;
            if (!positions.Contains(mousePos))
            {
                positions.Add(mousePos);
            }
        }

        return positions;
    }

    // Update the stored list of occluding objects
    private void SetOccludingObjects(List<Cullable> newList)
    {
        foreach (Cullable cullable in newList)
        {
            int foundIndex = m_occludingObjects.IndexOf(cullable);

            if (foundIndex < 0)
            {
                // This object isnt in the old list, so we need to mark it as occluding
                cullable.Occluding = true;
            }
            else
            {
                // This object was already in the list, so remove it from the old list
                m_occludingObjects.RemoveAt(foundIndex);
            }
        }

        // Any object left in the old list, was not in the new list, so it's no longer occludding
        foreach (Cullable cullable in m_occludingObjects)
        {
            cullable.Occluding = false;
        }

        m_occludingObjects = newList;
    }

    private List<Cullable> FindOccludingObjects(List<Vector3> importantPositions)
    {
        List<Cullable> occludingObjects = new List<Cullable>();


        Camera camera = Camera.main;

        // We want to do a capsule check from each position to the camera, any cullable object we hit should be culled
        foreach (Vector3 pos in importantPositions)
        {
            Vector3 capsuleStart = (pos);
            capsuleStart.y += m_occlusionCapsuleHeight;

            Collider[] colliders = Physics.OverlapCapsule(capsuleStart, camera.transform.position, m_occlusionCapsuleRadius, m_layerMask, QueryTriggerInteraction.Ignore);

            // Add cullable objects we found to the list
            foreach (Collider collider in colliders)
            {
                Cullable cullable = collider.GetComponent<Cullable>();
                Debug.Assert(cullable != null, "Found an object on the occlusion layer without the occlusion component!");

                if (!occludingObjects.Contains(cullable))
                {
                    occludingObjects.Add(cullable);
                }
            }
        }

        return occludingObjects;
    }
}

这个类实际上与场景中的Cullable组件对话,并告诉它们何时它们实际上妨碍了事物并且需要淡化,每一帧它都会执行 3 步序列:

1.查找重要位置

这个函数返回世界上重要事物的位置列表,需要确保我们保持可见。现在的重要位置是玩家角色和鼠标光标位置,但可能还想返回 NPC、射弹或陷阱的位置。

2.查找遮挡对象

此函数从 FindImportantPositions 中获取位置列表,并使用 OverlapCapsule 尝试找出哪些可剔除对象位于重要位置和相机之间。

例如,在场景中,有 2 个角色,玩家和敌人,我们希望确保它们始终可见。我们还有 3 个可剔除对象:1,2 和 3。

ue3d - Unity游戏对象淡出/剔除效果简单设置方法

每一帧都制作胶囊,从每个重要对象的中心延伸到当前相机的中心。然后检查这些圆柱体是否与世界上的任何 Cullable 对象发生碰撞。

在图表中,可以看到 cullable 2 与玩家的圆柱体重叠,因此需要隐藏。Cullable 3 与来自敌人的圆柱体重叠,因此也需要隐藏。Cullable 1 不会与任何东西发生碰撞,因此它应该是可见的。

在这个过程结束时,我们有一个需要隐藏的所有可剔除对象的列表。

3.设置遮挡对象

这个函数从我们称为 NewListFindOccludingObjects 中获取我们想要隐藏的对象列表,并将它与我们在前一帧中从该函数获得的对象列表进行比较

  1. 如果条目不在 OldList 中,那么我们需要将其 Occluding 属性设置为 true,因此它会隐藏自己
  2. 如果该条目也在 OldList 中,这意味着我们已经将 Occluding 属性设置为 true,因此我们不需要更改它。但是,我们确实想从 OldList 中删除该条目(稍后会明白为什么)

一旦这个循环完成,我们就知道所有应该隐藏的相同者都将他们的 Occluding 标志设置为 true。但是那些被隐藏,现在需要展示的东西呢?

好吧,当我们开始时,OldList 拥有上一帧被剔除的所有内容。我们现在已经从该列表中删除了应该在这一帧中剔除的所有内容。这意味着列表中剩下的任何东西都不应该被遮挡!

一旦我们将 OldList 中所有内容的 Occluding 设置为 false ,我们就会用 NewList 覆盖它,准备在下一帧重复该过程。

额外的内容

由于我们使用的是Unity碰撞系统,因此请确保每个 Cullable 对象都附加了一个碰撞器。否则,将无法检测到它们需要隐藏!

可能还希望将所有遮挡物放在某个层中,这样它们就不会与其他事物(例如来自鼠标的光线投射)发生碰撞。CullingManager 有一个名为 m_layerMask 的成员,可以使用它来过滤掉该层上的碰撞器。

现在,我们为碰撞圆柱体使用固定半径。你可能希望扩展系统,使每个重要位置都有自己的半径,因此较大的对象比较小的对象可以被更多的东西遮挡。

最后,不要忘记确保每个 Cullable 对象都具有附加了正确着色器的材质。如果没有这个,这一切都会有点浪费时间。

淡出效果

淡出效果
淡出效果

这只是使用 _Alpha值 淡出整个对象。  

BIR着色器代码

AlphaFade.shader :

Shader "Custom/AlphaFade" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
        _Alpha ("Alpha",  Range(0,1)) = 1.0
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }

    SubShader {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        LOD 200
        Blend SrcAlpha OneMinusSrcAlpha 

        CGPROGRAM
        #pragma surface surf Standard keepalpha

        sampler2D _MainTex;
        fixed4 _Color;
        float _Alpha;
        float _Metallic, _Glossiness;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = _Alpha;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
        }
        ENDCG
    }

    // enable shadow casting

    Fallback "Diffuse"
}
ue3d - Unity游戏对象淡出/剔除效果简单设置方法

非常简单的设置,只需要对标准表面着色器进行一些更改。QueueRendertype 设置为透明。Blend SrcAlpha OneMinusSrcAlpha 是传统的透明度。最后,keepalpha关键字实际上确保了 alpha 被使用。

ue3d - Unity游戏对象淡出/剔除效果简单设置方法

然后只需将表面输出的 o.Alpha 设置为我们将在脚本中更改的 _Alpha。

(单独使用这种方法的一个缺点是透明材质不会接收阴影,如果想要效果和阴影,请考虑在 FadeAlphaRoutine 期间将Unity材质交换为该材质)

URP

AlphaFade图表
AlphaFade图表

几乎不值得为其发布图形文件,只需将 Graph Settings 设置Transparent,并将引用 _Alpha 的 Property 插入 Alpha。

抖动效果

ue3d - Unity游戏对象淡出/剔除效果简单设置方法

使用带有 _Fade 变量的抖动,以及基于对象位置的可选截止,因此只有部分会抖动。

BIRP着色器代码:

DitherFade.shader

Shader "Custom/DitherFade"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)        
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Noise ("Noise (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _PosSlider ("Cutoff Slider", float) = 0.0
        _Dither("Dither", float) = 0.0
        _AlphaThreshold("Alphacutoff", Range(0,1)) = 0.0
        _Fade("Fade", Range(0,10)) = 5

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
        //  Blend SrcAlpha OneMinusSrcAlpha
        Cull Off

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard vertex:vert addshadow

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 4.0

        sampler2D _MainTex, _Noise;

        struct Input
        {
            float2 uv_MainTex;
            float3 localPos;
            float vFace : VFACE; 
            float4 screenPos;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        half _PosSlider;
        float _Dither;
        float _AlphaThreshold,_Fade;
        
        void vert (inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input,o);        
            o.localPos = v.vertex.xyz;
        }

        float4 Unity_Dither_float4(float4 In, float4 ScreenPosition)
        {
            float2 coords = ScreenPosition.xy / ScreenPosition.w;
            float2 uv = coords * _ScreenParams.xy;
            float DITHER_THRESHOLDS[16] =
            {
                1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
                13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
                4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
                16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0
            };
            uint index = (uint(uv.x) % 4) * 4 + uint(uv.y) % 4;
            return In - DITHER_THRESHOLDS[index];
        }

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
        // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c;
             //  the object position and a slider that moves over the object
            float objectPosSliding = IN.localPos.y  + _PosSlider;
            // the dither effect, adding in the cutoff
            float ditheredCutoff = Unity_Dither_float4(_Dither, IN.screenPos).r + (1-(saturate(objectPosSliding)) * _Fade) ;
            // discard pixels based on dither
            clip(ditheredCutoff - _AlphaThreshold);
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

在顶点函数中获取对象位置,只需要 y,然后用浮点数添加到它上面,以便在模型上上下移动。

ue3d - Unity游戏对象淡出/剔除效果简单设置方法

Dither Node 从 unity3d文档中获取,并进行了一些微调以使其在表面着色器中工作。

ue3d - Unity游戏对象淡出/剔除效果简单设置方法

使用添加的对象位置剪辑抖动,因此只有在截止点上方的部分模型会被抖动。

URP

DitherFade 图表
DitherFade 图表

Urp 有一个仅用于抖动的节点,因此我们只需要添加对象位置和滑块即可在对象上移动。确保 Graph Settings 启用了 Alpha Clipping。

注意:为了这个Unity特效,在 Cullable Object 中设置 _Dither 值,(从 0 到 1)

具有裁切效果

ue3d - Unity游戏对象淡出/剔除效果简单设置方法

类似于假的着色器,切断网格的顶部并将背面着色为纯色。在此Unity效果中需要设置 _PosSlider,值取决于每个游戏模型。

BIRP着色器代码

Cutoff.Shader:

Shader "Custom/Cutoff"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)       
        _MainTex ("Albedo (RGB)", 2D) = "white" {}     
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _PosSlider ("Slider", float) = 0.0
        _Stretch ("Stretch", float) = 7
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
        //  Blend SrcAlpha OneMinusSrcAlpha
        Cull Off

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard vertex:vert keepalpha  

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
            float3 localPos;
            float vFace : VFACE; 
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        half _PosSlider;
        half _Stretch;
        
        void vert (inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input,o);
            o.localPos = v.vertex.xyz;
            
        }

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
        // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            
            //  the object position and a slider that moves over the object
            float objectPosSliding = IN.localPos.y  + _PosSlider;

            // the cutoff that will be clipped
            float cutoff = step(objectPosSliding, 0.5);
            // lerp the color of the albedo towards the cutoff point
            c = lerp(c, _Color, saturate(objectPosSliding * _Stretch));
            // 
            float4 final = (IN.vFace>0) ? c : _Color;
            o.Albedo = final;
            //
            float4 e = lerp(0, _Color, saturate(objectPosSliding * _Stretch));
            // color the backfaces in _Color, and the the rest in the f
            o.Emission = (IN.vFace>0) ? e : _Color;
            // clip everything above the cutoff;
            clip(cutoff - 0.06);
            
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha =1;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

使用与抖动效果相同的设置,仅剪辑滑动对象 y,然后为背面着色 ( VFace )。还会向截止点倾斜渐变以使其更平滑。_Stretch 控制渐变的宽度。

URP

图形设置
图形设置

确保图形设置设置为渲染两侧,VFace 的 Shader Graph 版本称为 Is Front Face ,需要将其连接到分支节点。

渐变是使用与以前相同的对象 y 滑块的 lerp。

以上是所有关于在Unity中设置对象淡出和剔除的简单方法。

相关信息

  • 类型:知识
  • 字数:1724
  • 字符:4660
  • 适用软件:Unity
  • 说明:无
  • 编号:61327

热门内容

提示:3D天堂作为服务提供者,尊重网络版权及知识产权,对某些行为的发生不具备充分的监控能力,若无意间侵犯到您的权利,请 联系我们,我们会在收到信息后尽快给予处理。

本站文章版权归本站自创作者所有,未经允许不得转载!