Unity - 记录解决 部分手机设备上 浮点精度 不足 导致 UV 采样断层严重的 BUG


环境

Unity : 2020.3.37f1
Pipeline : BRP


目的

备忘,便于日后索引


问题

正常手机显卡芯片的浮点解析进度上的效果(其实不用手机上,PC 上将 uv * scale 一个巨大的值也会出现的)
在这里插入图片描述

异常手机显卡芯片的浮点解析进度上的效果(其实不用手机上,PC 上将 uv * scale 一个巨大的值也会出现的)
在这里插入图片描述


解决

诊断发现是: UV 精度 scale 之后溢出了

这个 shader 是 网上的一位TA大佬 (听说是叫: 猫大 的TA大佬 写的,下面的 shader 可以看到是使用 ASE 连连看 生成的代码)

// Made with Amplify Shader Editor
// Available at the Unity Asset Store - http://u3d.as/y3X 
Shader "VFX/PandaShaderSampleV1.0"
{
	Properties
	{
		[Enum(UnityEngine.Rendering.BlendMode)]_Scr("Scr", Float) = 5
		[Enum(UnityEngine.Rendering.BlendMode)]_Dst("Dst", Float) = 10
		[Enum(UnityEngine.Rendering.CullMode)]_CullMode("CullMode", Float) = 0
		_MainTex("MainTex", 2D) = "white" {}
		[Toggle]_MainTexAR("MainTexAR", Float) = 0
		[HDR]_MainColor("MainColor", Color) = (1,1,1,1)
		_MainTexUSpeed("MainTexUSpeed", Float) = 0
		_MainTexVSpeed("MainTexVSpeed", Float) = 0
		[Toggle]_CustomMainTex("CustomMainTex", Float) = 0
		[Toggle(_FMASKTEX_ON)] _FMaskTex("FMaskTex", Float) = 0
		_MaskTex("MaskTex", 2D) = "white" {}
		[Toggle]_MaskTexAR("MaskTexAR", Float) = 1
		_MaskTexUSpeed("MaskTexUSpeed", Float) = 0
		_MaskTexVSpeed("MaskTexVSpeed", Float) = 0
		[Toggle(_FDISTORTTEX_ON)] _FDistortTex("FDistortTex", Float) = 0
		_DistortTex("DistortTex", 2D) = "white" {}
		[Toggle]_DistortTexAR("DistortTexAR", Float) = 1
		_DistortFactor("DistortFactor", Range( 0 , 1)) = 0
		_DistortTexUSpeed("DistortTexUSpeed", Float) = 0
		_DistortTexVSpeed("DistortTexVSpeed", Float) = 0
		[Toggle]_DistortMainTex("DistortMainTex", Float) = 0
		[Toggle]_DistortMaskTex("DistortMaskTex", Float) = 0
		[Toggle]_DistortDissolveTex("DistortDissolveTex", Float) = 0
		[Toggle(_FDISSOLVETEX_ON)] _FDissolveTex("FDissolveTex", Float) = 0
		_DissolveTex("DissolveTex", 2D) = "white" {}
		[Toggle]_DissolveTexAR("DissolveTexAR", Float) = 1
		[HDR]_DissolveColor("DissolveColor", Color) = (1,1,1,1)
		[Toggle]_CustomDissolve("CustomDissolve", Float) = 0
		_DissolveFactor("DissolveFactor", Range( 0 , 1)) = 0
		_DissolveSoft("DissolveSoft", Range( 0 , 1)) = 0.1
		_DissolveWide("DissolveWide", Range( 0 , 1)) = 0.05
		_DissolveTexUSpeed("DissolveTexUSpeed", Float) = 0
		_DissolveTexVSpeed("DissolveTexVSpeed", Float) = 0
		_MainAlpha("MainAlpha", Range( 0 , 10)) = 1
		[Toggle(_FFNL_ON)] _FFnl("FFnl", Float) = 0
		[Toggle(_FDEPTH_ON)] _FDepth("FDepth", Float) = 0
		[HDR]_FnlColor("FnlColor", Color) = (1,1,1,1)
		_FnlScale("FnlScale", Range( 0 , 2)) = 0
		_FnlPower("FnlPower", Range( 1 , 10)) = 1
		[Toggle]_ReFnl("ReFnl", Float) = 0
		[Enum(Alpha,0,Add,1)]_BlendMode("BlendMode", Float) = 0
		_DepthFade("DepthFade", Range( 0 , 10)) = 1
		[HideInInspector] _texcoord2( "", 2D ) = "white" {}
		[HideInInspector] _texcoord( "", 2D ) = "white" {}
		[HideInInspector] __dirty( "", Int ) = 1
	}

	SubShader
	{
		Tags{ "RenderType" = "Transparent"  "Queue" = "Transparent+0" "IsEmissive" = "true"  }
		Cull [_CullMode]
		ZWrite Off
		ZTest LEqual
		Blend [_Scr] [_Dst], One OneMinusSrcAlpha
		
		CGPROGRAM
		#include "UnityShaderVariables.cginc"
		#include "UnityCG.cginc"
		#pragma target 3.0
		#pragma shader_feature_local _FDISSOLVETEX_ON
		#pragma shader_feature_local _FDISTORTTEX_ON
		#pragma shader_feature_local _FFNL_ON
		#pragma shader_feature_local _FMASKTEX_ON
		#pragma shader_feature_local _FDEPTH_ON
		#pragma surface surf Unlit keepalpha noshadow noambient novertexlights nolightmap  nodynlightmap nodirlightmap nofog nometa noforwardadd 
		#undef TRANSFORM_TEX
		#define TRANSFORM_TEX(tex,name) float4(tex.xy * name##_ST.xy + name##_ST.zw, tex.z, tex.w)
		struct Input
		{
			float4 vertexColor : COLOR;
			float2 uv_texcoord;
			float4 uv2_texcoord2;
			float3 worldPos;
			half3 worldNormal;
			float4 screenPos;
		};

		uniform half _Dst;
		uniform half _CullMode;
		uniform half _BlendMode;
		uniform half _Scr;
		uniform float4 _MainColor;
		uniform sampler2D _MainTex;
		uniform half _MainTexUSpeed;
		uniform half _MainTexVSpeed;
		uniform half _CustomMainTex;
		uniform float4 _MainTex_ST;
		uniform half _DistortMainTex;
		uniform half _DistortTexAR;
		uniform sampler2D _DistortTex;
		uniform half _DistortTexUSpeed;
		uniform half _DistortTexVSpeed;
		uniform float4 _DistortTex_ST;
		uniform half _DistortFactor;
		uniform float4 _DissolveColor;
		uniform half _CustomDissolve;
		uniform half _DissolveFactor;
		uniform half _DissolveWide;
		uniform half _DissolveSoft;
		uniform half _DissolveTexAR;
		uniform sampler2D _DissolveTex;
		uniform half _DissolveTexUSpeed;
		uniform half _DissolveTexVSpeed;
		uniform float4 _DissolveTex_ST;
		uniform half _DistortDissolveTex;
		uniform half _MainAlpha;
		uniform half _ReFnl;
		uniform half4 _FnlColor;
		uniform half _FnlScale;
		uniform half _FnlPower;
		uniform half _MainTexAR;
		uniform half _MaskTexAR;
		uniform sampler2D _MaskTex;
		uniform half _MaskTexUSpeed;
		uniform half _MaskTexVSpeed;
		uniform float4 _MaskTex_ST;
		uniform half _DistortMaskTex;
		UNITY_DECLARE_DEPTH_TEXTURE( _CameraDepthTexture );
		uniform float4 _CameraDepthTexture_TexelSize;
		uniform half _DepthFade;

		inline half4 LightingUnlit( SurfaceOutput s, half3 lightDir, half atten )
		{
			return half4 ( 0, 0, 0, s.Alpha );
		}

		void surf( Input i , inout SurfaceOutput o )
		{
			float Scr106 = _Scr;
			half2 appendResult4_g42 = (half2(_MainTexUSpeed , _MainTexVSpeed));
			float2 uv_MainTex = i.uv_texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
			half2 temp_output_3_0_g39 = uv_MainTex;
			half2 appendResult4_g33 = (half2(_DistortTexUSpeed , _DistortTexVSpeed));
			float2 uv_DistortTex = i.uv_texcoord * _DistortTex_ST.xy + _DistortTex_ST.zw;

			// jave.lin : 修复前
			//half2 panner5_g33 = ( 1.0 * _Time.y * appendResult4_g33 + uv_DistortTex);
			// jave.lin : 修复后
			half2 panner5_g33 = ( 1.0 * frac(_Time.y * appendResult4_g33) + uv_DistortTex);

			half4 tex2DNode7_g33 = tex2D( _DistortTex, panner5_g33 );
			half Distort148 = ( ( _DistortTexAR == 0.0 ? tex2DNode7_g33.a : tex2DNode7_g33.r ) * _DistortFactor );
			#ifdef _FDISTORTTEX_ON
				half2 staticSwitch316 = ( _DistortMainTex == 0.0 ? temp_output_3_0_g39 : ( temp_output_3_0_g39 + Distort148 ) );
			#else
				half2 staticSwitch316 = uv_MainTex;
			#endif
			half2 appendResult330 = (half2(i.uv2_texcoord2.x , i.uv2_texcoord2.y));
			// jave.lin : 修复前
			//half2 panner5_g42 = ( 1.0 * _Time.y * appendResult4_g42 + (( _CustomMainTex )?( ( staticSwitch316 + appendResult330 ) ):( staticSwitch316 )));
			// jave.lin : 修复后
			half2 panner5_g42 = ( 1.0 * frac(_Time.y * appendResult4_g42) + (( _CustomMainTex )?( ( staticSwitch316 + appendResult330 ) ):( staticSwitch316 )));
			half4 tex2DNode7_g42 = tex2D( _MainTex, panner5_g42 );
			half4 MainTexColor215 = ( _MainColor * tex2DNode7_g42 );
			half temp_output_275_0 = (-_DissolveWide + ((( _CustomDissolve )?( i.uv2_texcoord2.z ):( _DissolveFactor )) - 0.0) * (1.0 - -_DissolveWide) / (1.0 - 0.0));
			half temp_output_277_0 = ( _DissolveSoft + 0.0001 );
			half temp_output_272_0 = (-temp_output_277_0 + (( temp_output_275_0 + _DissolveWide ) - 0.0) * (1.0 - -temp_output_277_0) / (1.0 - 0.0));
			half2 appendResult4_g41 = (half2(_DissolveTexUSpeed , _DissolveTexVSpeed));
			float2 uv_DissolveTex = i.uv_texcoord * _DissolveTex_ST.xy + _DissolveTex_ST.zw;
			half2 temp_output_3_0_g40 = uv_DissolveTex;
			#ifdef _FDISTORTTEX_ON
				half2 staticSwitch314 = ( _DistortDissolveTex == 0.0 ? temp_output_3_0_g40 : ( temp_output_3_0_g40 + Distort148 ) );
			#else
				half2 staticSwitch314 = uv_DissolveTex;
			#endif
			// jave.lin : 修复前
			//half2 panner5_g41 = ( 1.0 * _Time.y * appendResult4_g41 + staticSwitch314);
			// jave.lin : 修复后
			half2 panner5_g41 = ( 1.0 * frac(_Time.y * appendResult4_g41) + staticSwitch314);
			half4 tex2DNode7_g41 = tex2D( _DissolveTex, panner5_g41 );
			half temp_output_308_20 = ( _DissolveTexAR == 0.0 ? tex2DNode7_g41.a : tex2DNode7_g41.r );
			half smoothstepResult264 = smoothstep( temp_output_272_0 , ( temp_output_272_0 + temp_output_277_0 ) , temp_output_308_20);
			half Alpha337 = _MainAlpha;
			half4 lerpResult223 = lerp( MainTexColor215 , _DissolveColor , ( _DissolveColor.a * ( 1.0 - smoothstepResult264 ) * Alpha337 ));
			#ifdef _FDISSOLVETEX_ON
				half4 staticSwitch298 = lerpResult223;
			#else
				half4 staticSwitch298 = MainTexColor215;
			#endif
			half4 temp_cast_0 = (0.0).xxxx;
			half Refnl339 = _ReFnl;
			float3 ase_worldPos = i.worldPos;
			half3 ase_worldViewDir = normalize( UnityWorldSpaceViewDir( ase_worldPos ) );
			half3 ase_worldNormal = i.worldNormal;
			half fresnelNdotV279 = dot( ase_worldNormal, ase_worldViewDir );
			half fresnelNode279 = ( 0.0 + _FnlScale * pow( 1.0 - fresnelNdotV279, _FnlPower ) );
			half temp_output_283_0 = saturate( fresnelNode279 );
			half4 FnlMainColor286 = ( _FnlColor * temp_output_283_0 * _FnlColor.a );
			half4 temp_cast_1 = (0.0).xxxx;
			#ifdef _FFNL_ON
				half4 staticSwitch300 = ( Refnl339 == 0.0 ? FnlMainColor286 : temp_cast_1 );
			#else
				half4 staticSwitch300 = temp_cast_0;
			#endif
			float4 MainColor98 = ( i.vertexColor * ( staticSwitch298 + staticSwitch300 ) );
			float MainTexAlpha138 = ( _MainColor.a * ( _MainTexAR == 0.0 ? tex2DNode7_g42.a : tex2DNode7_g42.r ) );
			half2 appendResult4_g44 = (half2(_MaskTexUSpeed , _MaskTexVSpeed));
			float2 uv_MaskTex = i.uv_texcoord * _MaskTex_ST.xy + _MaskTex_ST.zw;
			half2 temp_output_3_0_g43 = uv_MaskTex;
			#ifdef _FDISTORTTEX_ON
				half2 staticSwitch312 = ( _DistortMaskTex == 0.0 ? temp_output_3_0_g43 : ( temp_output_3_0_g43 + Distort148 ) );
			#else
				half2 staticSwitch312 = uv_MaskTex;
			#endif
			// jave.lin : 修复前
			//half2 panner5_g44 = ( 1.0 * _Time.y * appendResult4_g44 + staticSwitch312);
			// jave.lin : 修复后
			half2 panner5_g44 = ( 1.0 * frac(_Time.y * appendResult4_g44) + staticSwitch312);
			half4 tex2DNode7_g44 = tex2D( _MaskTex, panner5_g44 );
			#ifdef _FMASKTEX_ON
				half staticSwitch291 = ( _MaskTexAR == 0.0 ? tex2DNode7_g44.a : tex2DNode7_g44.r );
			#else
				half staticSwitch291 = 1.0;
			#endif
			half temp_output_270_0 = (-temp_output_277_0 + (temp_output_275_0 - 0.0) * (1.0 - -temp_output_277_0) / (1.0 - 0.0));
			half smoothstepResult256 = smoothstep( temp_output_270_0 , ( temp_output_270_0 + temp_output_277_0 ) , temp_output_308_20);
			half DissolveAlpha212 = smoothstepResult256;
			#ifdef _FDISSOLVETEX_ON
				half staticSwitch299 = DissolveAlpha212;
			#else
				half staticSwitch299 = 1.0;
			#endif
			half ReFnlAlpha318 = ( 1.0 - temp_output_283_0 );
			#ifdef _FFNL_ON
				half staticSwitch319 = ( Refnl339 == 0.0 ? 1.0 : ReFnlAlpha318 );
			#else
				half staticSwitch319 = 1.0;
			#endif
			float4 ase_screenPos = float4( i.screenPos.xyz , i.screenPos.w + 0.00000000001 );
			half4 ase_screenPosNorm = ase_screenPos / ase_screenPos.w;
			ase_screenPosNorm.z = ( UNITY_NEAR_CLIP_VALUE >= 0 ) ? ase_screenPosNorm.z : ase_screenPosNorm.z * 0.5 + 0.5;
			float screenDepth348 = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE( _CameraDepthTexture, ase_screenPosNorm.xy ));
			half distanceDepth348 = abs( ( screenDepth348 - LinearEyeDepth( ase_screenPosNorm.z ) ) / ( _DepthFade ) );
			#ifdef _FDEPTH_ON
				half staticSwitch350 = saturate( distanceDepth348 );
			#else
				half staticSwitch350 = 1.0;
			#endif
			float MainAlpha97 = saturate( ( MainTexAlpha138 * staticSwitch291 * i.vertexColor.a * Alpha337 * staticSwitch299 * staticSwitch319 * staticSwitch350 ) );
			o.Emission = ( Scr106 == 5.0 ? MainColor98 : ( MainColor98 * MainAlpha97 ) ).rgb;
			half temp_output_100_0 = MainAlpha97;
			o.Alpha = temp_output_100_0;
		}

		ENDCG
	}
	CustomEditor "SampleGUI"
}

上面的 shader 代码中,搜索: “修复后”,机可查看到类似下面的代码:


...
			// jave.lin : 修复前
			//half2 panner5_g33 = ( 1.0 * _Time.y * appendResult4_g33 + uv_DistortTex);
			// jave.lin : 修复后
			half2 panner5_g33 = ( 1.0 * frac(_Time.y * appendResult4_g33) + uv_DistortTex);
...
			// jave.lin : 修复前
			//half2 panner5_g42 = ( 1.0 * _Time.y * appendResult4_g42 + (( _CustomMainTex )?( ( staticSwitch316 + appendResult330 ) ):( staticSwitch316 )));
			// jave.lin : 修复后
			half2 panner5_g42 = ( 1.0 * frac(_Time.y * appendResult4_g42) + (( _CustomMainTex )?( ( staticSwitch316 + appendResult330 ) ):( staticSwitch316 )));
...
			// jave.lin : 修复前
			//half2 panner5_g41 = ( 1.0 * _Time.y * appendResult4_g41 + staticSwitch314);
			// jave.lin : 修复后
			half2 panner5_g41 = ( 1.0 * frac(_Time.y * appendResult4_g41) + staticSwitch314);
...
			// jave.lin : 修复前
			//half2 panner5_g44 = ( 1.0 * _Time.y * appendResult4_g44 + staticSwitch312);
			// jave.lin : 修复后
			half2 panner5_g44 = ( 1.0 * frac(_Time.y * appendResult4_g44) + staticSwitch312);
...

这种优化方式也有局限性的,因为我们的 sampler 的 filer mode 是 repeat,所以 uv 值域可以控制再 -1 ~ 1 即可,因为可以使用 frac 来优化,也可以 fmod(val, 1.0) 但是这个过于费,因为使用 frac 就OK

这个问题的是100%必现的,最终发布到出问题的手机上验证,修复了


其实这个解决方法,我以前在学习 B站上的 庄懂 TA巨佬 就有分享过
现在想起帮助还是挺大的


Project

backup : Testing_Effect_Error_4_RenderDoc_UV精度不足、断层的修复.rar