如何修改Unity果冻Shader使其持续生效,解决远距离失效、移动卡顿问题
果冻Shader问题修复方案
问题根源说明
- 距离过远失效:原Shader采用世界空间计算顶点和波动原点的距离,物体远离初始设置的
_ImpactOrigin后,距离值过大导致1/dist趋近于0,顶点偏移量直接归零,看起来就是Shader停止工作。同时你将_ModelOrigin和_ImpactOrigin设为几乎重合的位置,方向计算容易出现浮点精度错误,也会导致效果异常。 - 移动时卡顿:你调用
modelRenderer.material时每次都会生成新的材质实例,频繁赋值会触发重复的内存分配和渲染状态切换,导致卡顿。
修复步骤
1. Shader修改
主要优化点:
- 内置获取物体自身原点,无需外部传入
_ModelOrigin - 波动计算改为模型本地空间,不受世界坐标位置影响,不会出现距离过远失效问题
- 默认使用Unity内置时间参数
_Time,无需外部传入_ControlTime - 固定默认的波动方向偏移,避免两个原点重合导致的精度问题
修改后的完整Shader代码如下:
// Upgrade NOTE: upgraded instancing buffer 'Props' to new syntax. Shader "Custom/JellyShader" { 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 _Transparency("Transparency", float) = 0.1 _LocalImpactOffset("本地波动偏移", Vector) = (0.1, 0.1, 0.1, 0) _UseExternalParams("启用外部参数控制", Range(0,1)) = 0 _ControlTime("Time", float) = 0 _ModelOrigin("Model Origin", Vector) = (0,0,0,0) _ImpactOrigin("Impact Origin", Vector) = (-5,0,0,0) _Emission("Emission", float) = 0 [HDR] _EmissionColor("Color", Color) = (0,0,0) _Frequency("Frequency", Range(0, 1000)) = 10 _Amplitude("Amplitude", Range(0, 5)) = 0.1 _WaveFalloff("Wave Falloff", Range(1, 8)) = 4 _MaxWaveDistortion("Max Wave Distortion", Range(0.1, 2.0)) = 1 _ImpactSpeed("Impact Speed", Range(0, 10)) = 0.5 _WaveSpeed("Wave Speed", Range(-10, 10)) = -5 } SubShader{ Tags { "Queue" = "Transparent" "RenderType" = "Transparent" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows addshadow vertex:vert alpha:fade // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; fixed4 _EmissionColor; float _UseExternalParams; float _ControlTime; float4 _ModelOrigin; float4 _ImpactOrigin; float4 _LocalImpactOffset; float _Transparency; half _Frequency; //Base frequency for our waves. half _Amplitude; //Base amplitude for our waves. half _WaveFalloff; //How quickly our distortion should fall off given distance. half _MaxWaveDistortion; //Smaller number here will lead to larger distortion as the vertex approaches origin. half _ImpactSpeed; //How quickly our wave origin moves across the sphere. half _WaveSpeed; //Oscillation speed of an individual wave. // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader. UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) void vert(inout appdata_base v) { float4 modelOriginWorld = float4(unity_ObjectToWorld[0].w, unity_ObjectToWorld[1].w, unity_ObjectToWorld[2].w, 1); float4 impactOriginWorld = _UseExternalParams ? _ImpactOrigin : modelOriginWorld + _LocalImpactOffset; float4 direction = normalize(modelOriginWorld - impactOriginWorld); float time = _UseExternalParams ? _ControlTime : _Time.y; float4 origin = impactOriginWorld + time * _ImpactSpeed * direction; float4 world_space_vertex = mul(unity_ObjectToWorld, v.vertex); //Get the distance in model space to avoid distance decay in world space float dist = distance(v.vertex, mul(unity_WorldToObject, origin)); //Adjust our distance to be non-linear. dist = pow(dist, _WaveFalloff); //Set the max amount a wave can be distorted based on distance. dist = max(dist, _MaxWaveDistortion); //Convert direction and _ImpactOrigin to model space for later trig magic. float4 l_ImpactOrigin = mul(unity_WorldToObject, impactOriginWorld); float4 l_direction = mul(unity_WorldToObject, direction); //Magic float impactAxis = l_ImpactOrigin + dot((v.vertex - l_ImpactOrigin), l_direction); v.vertex.xyz += v.normal * sin(impactAxis * _Frequency + time * _WaveSpeed) * _Amplitude * (1 / dist); } 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; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; o.Emission = c.rgb * tex2D(_MainTex, IN.uv_MainTex).a * _EmissionColor; o.Smoothness = _Glossiness; } ENDCG } FallBack "Diffuse" }
2. C#代码优化(可选,仅需外部控制时使用)
如果不需要动态修改参数,Shader默认状态下就可以直接运行,无需任何C#代码赋值。如果需要外部控制,使用MaterialPropertyBlock避免创建材质实例,消除卡顿:
private MaterialPropertyBlock mpb; private Renderer modelRenderer; void Start() { mpb = new MaterialPropertyBlock(); modelRenderer = GetComponent<Renderer>(); // 开启外部参数控制,不需要的话可以省略这部分 mpb.SetFloat("_UseExternalParams", 1); } void Update() { // 仅在参数需要变更时赋值,不要每帧都调用 mpb.SetVector("_ModelOrigin", transform.position); mpb.SetVector("_ImpactOrigin", transform.position + new Vector3(0.001f, 0.001f, 0.001f)); mpb.SetFloat("_ControlTime", Time.time); modelRenderer.SetPropertyBlock(mpb); }
效果验证
修改后不管物体移动到世界坐标的哪个位置,果冻效果都会始终生效,也不会出现移动时的卡顿问题。可以调整面板上的本地波动偏移参数修改抖动方向,不需要修改代码。
内容的提问来源于stack exchange,提问作者OmaeWAc
相关产品推荐
相关产品推荐

