如何在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
相关产品推荐
相关产品推荐

