Unity ShaderLab中如何用lerp与deltaTime实现向目标值的动画过渡?
嘿,我来帮你搞定这两个问题,先说说你给出的示例代码为什么没法正常运行——原因很简单:currrentAlpha是着色器的内部局部变量,每帧执行surf函数时都会被重新初始化(默认值为0),所以每次都是从0开始lerp到targetAlpha,根本没法累积变化实现平滑过渡。下面是正确的实现方式:
1. 在Unity ShaderLab中用lerp和deltaTime实现向目标值的动画过渡
核心要点是必须维护一个可以跨帧保留的当前值,因为Shader本身没有持久化状态,通常有两种可靠的做法:
方法一:C#脚本驱动(推荐,可控性强)
在C#脚本里维护当前值,逐帧计算过渡后传递给Shader,这种方式灵活性最高:
Shader代码片段
Shader "Custom/BasicLerpTransition" { Properties { _TargetValue ("Target Value", Range(0,1)) = 0.5 _CurrentValue ("Current Value", Range(0,1)) = 0.5 // 由C#脚本更新 _TransitionSpeed ("Transition Speed", Float) = 2.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; float _TargetValue; float _CurrentValue; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // 直接使用C#传递的已完成过渡计算的值 return fixed4(_CurrentValue, _CurrentValue, _CurrentValue, 1.0); } ENDCG } } }
配套C#脚本(挂载在使用该Shader的物体上)
using UnityEngine; public class LerpTransitionController : MonoBehaviour { public Material targetMaterial; public float transitionSpeed = 2f; private float currentValue; void Start() { // 初始化当前值为Shader的初始TargetValue currentValue = targetMaterial.GetFloat("_TargetValue"); } void Update() { float targetValue = targetMaterial.GetFloat("_TargetValue"); // 使用lerp结合deltaTime实现平滑过渡,speed参数控制过渡快慢 currentValue = Mathf.Lerp(currentValue, targetValue, Time.deltaTime * transitionSpeed); targetMaterial.SetFloat("_CurrentValue", currentValue); } }
方法二:Shader内部基于时间的过渡(无需C#,但触发逻辑受限)
如果不想依赖C#,可以通过传递触发时间戳给Shader,结合Unity内置的_Time变量计算过渡进度:
Shader "Custom/ShaderInternalLerp" { Properties { _TargetValue ("Target Value", Range(0,1)) = 0.5 _StartTransitionTime ("Start Time (Seconds)", Float) = 0.0 _TransitionDuration ("Duration (Seconds)", Float) = 1.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; float _TargetValue; float _StartTransitionTime; float _TransitionDuration; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // 计算当前过渡进度,saturate确保值在0-1范围内 float progress = saturate((_Time.y - _StartTransitionTime) / _TransitionDuration); // 从初始值(这里假设为0)lerp到目标值 float currentValue = lerp(0, _TargetValue, progress); return fixed4(currentValue, currentValue, currentValue, 1.0); } ENDCG } } }
2. 使用自定义标准表面着色器实现向目标值的动画过渡
标准表面着色器的逻辑类似,核心还是要维护跨帧的当前值,下面是针对Alpha过渡的完整修复实现:
完整Shader代码
Shader "Custom/AnimatedAlphaStandardSurface" { Properties { _Color ("Color", Color) = (1,1,1,1) _Metallic ("Metallic", Range(0,1)) = 0.0 _Glossiness ("Smoothness", Range(0,1)) = 0.5 _TargetAlpha ("Target Alpha", Range(0,1)) = 1.0 _CurrentAlpha ("Current Alpha", Range(0,1)) = 1.0 // 由C#脚本更新 _TransitionSpeed ("Transition Speed", Float) = 2.0 } SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } LOD 200 CGPROGRAM // 声明为支持透明的标准表面着色器 #pragma surface surf Standard alpha:fade #pragma target 3.0 struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; float _TargetAlpha; float _CurrentAlpha; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; // 使用C#传递的已完成过渡的当前Alpha值 o.Alpha = _CurrentAlpha; } ENDCG } FallBack "Standard" }
配套C#脚本
using UnityEngine; public class SurfaceShaderAlphaController : MonoBehaviour { public Material surfaceMaterial; public float transitionSpeed = 2f; private float currentAlpha; void Start() { // 初始化当前Alpha为材质的初始值 currentAlpha = surfaceMaterial.GetFloat("_CurrentAlpha"); } void Update() { float targetAlpha = surfaceMaterial.GetFloat("_TargetAlpha"); // 平滑过渡到目标Alpha currentAlpha = Mathf.Lerp(currentAlpha, targetAlpha, Time.deltaTime * transitionSpeed); surfaceMaterial.SetFloat("_CurrentAlpha", currentAlpha); } }
内容的提问来源于stack exchange,提问作者Jethro
相关产品推荐
相关产品推荐

