You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Unity中实现相机边缘渐变灰化效果,适配慢动作功能触发场景

实现方案

以下提供两种适配不同开发需求的落地方式,可根据项目情况选择:

方案1:UGUI径向渐变遮罩实现(快速上手,无Shader基础要求)

适合小项目快速迭代,无需额外插件支持:

  • 在你项目的UI Canvas下新建全屏Image节点,锚点设置为全屏拉伸,层级放在所有UI的最上层确保覆盖整个屏幕
  • 提前准备一张径向渐变灰度图:图片边缘为不透明灰色,中心完全透明,可在PS或免费绘图工具生成256*256的PNG,导入Unity后将Texture Type设为Sprite(2D and UI),勾选Alpha Is Transparent选项
  • 将Image初始颜色的透明度设为0,默认隐藏
  • 给Image挂载控制脚本,和你现有慢动作系统做逻辑绑定即可:
using UnityEngine;
using UnityEngine.UI;

public class SlowmotionVignette : MonoBehaviour
{
    [Header("效果参数")]
    public float fadeSpeed = 5f;
    public float maxAlpha = 0.6f; // 边缘最大不透明度,可自行调整
    private Image vignetteImg;
    private bool isSlowmotionActive = false;

    void Awake()
    {
        vignetteImg = GetComponent<Image>();
        vignetteImg.color = new Color(0.2f, 0.2f, 0.2f, 0f);
    }

    // 现有慢动作触发逻辑调用对应方法即可
    public void OnSlowmotionStart() => isSlowmotionActive = true;
    public void OnSlowmotionEnd() => isSlowmotionActive = false;

    void Update()
    {
        float targetAlpha = isSlowmotionActive ? maxAlpha : 0f;
        Color currentColor = vignetteImg.color;
        currentColor.a = Mathf.Lerp(currentColor.a, targetAlpha, fadeSpeed * Time.unscaledDeltaTime);
        vignetteImg.color = currentColor;
    }
}
  • 最后在你原有慢动作逻辑中,按住按键触发慢动作时调用OnSlowmotionStart(),松开按键或能量耗尽时调用OnSlowmotionEnd()即可。

方案2:Shader后处理实现(性能更优,参数可调性高)

适合已经接入后处理栈,需要动态调整效果参数的项目:

  • 首先确保主相机已挂载Post Process Layer组件,Layer设置匹配相机渲染层级
  • 新建自定义后处理Shader,核心逻辑是计算像素到屏幕中心的距离,距离越远灰色叠加值越高:
Shader "Custom/SlowmotionVignette"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _VignetteColor ("边缘颜色", Color) = (0.2,0.2,0.2,1)
        _VignetteRadius ("渐变半径", Range(0,1)) = 0.5
        _VignetteSoftness ("边缘硬度", Range(0,1)) = 0.4
        _VignetteIntensity ("强度", Range(0,1)) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _VignetteColor;
            float _VignetteRadius;
            float _VignetteSoftness;
            float _VignetteIntensity;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                float2 distFromCenter = i.uv - 0.5f;
                float len = length(distFromCenter);
                float vignette = smoothstep(_VignetteRadius, _VignetteRadius - _VignetteSoftness, len);
                vignette = 1 - vignette;
                col = lerp(col, _VignetteColor, vignette * _VignetteIntensity);
                return col;
            }
            ENDCG
        }
    }
}
  • 新建材质球,Shader选择上面的Custom/SlowmotionVignette,初始强度设为0
  • 给主相机挂载后处理脚本,将上述材质球拖入对应参数槽:
using UnityEngine;

public class VignettePostProcess : MonoBehaviour
{
    public Material vignetteMat;
    public float changeSpeed = 5f;
    private float targetIntensity = 0f;

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (vignetteMat != null)
        {
            float currentIntensity = vignetteMat.GetFloat("_VignetteIntensity");
            currentIntensity = Mathf.Lerp(currentIntensity, targetIntensity, changeSpeed * Time.unscaledDeltaTime);
            vignetteMat.SetFloat("_VignetteIntensity", currentIntensity);
            Graphics.Blit(src, dest, vignetteMat);
        }
        else Graphics.Blit(src, dest);
    }

    public void SetVignetteActive(bool active) => targetIntensity = active ? 1f : 0f;
}
  • 同样在原有慢动作逻辑中触发时调用SetVignetteActive(true),结束时传false即可,支持实时调整边缘颜色、渐变范围等参数,无需修改素材。

注意:两种方案的过渡动画都使用了Time.unscaledDeltaTime,避免慢动作下时间缩放导致渐变动画也同步变慢。

内容的提问来源于stack exchange,提问作者Mason Walker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 21:48:03