Unity Shader 制作半透明物体 使用多Pass提前写入深度的方式 避免穿模

在Unity中,实现半透明效果通常会关闭深度写入(ZWrite Off)。这会导致渲染器无法正确判断物体自身的远近关系,从而在复杂的遮挡或非凸面体上产生排序错误,从视觉上看就像是穿模了。

🛠️ 解决方案:双Pass Shader原理

这个方法通过两个Pass来解决排序问题:

  1. 深度写入Pass (Depth Write Pass): 位于顶部,先执行。它只将模型的精准深度信息写入到深度缓冲区,但不输出任何颜色(ColorMask 0)。这样GPU就能知道模型的正确前后关系了。

  2. 透明混合Pass: 接着执行。利用第一个Pass已经建立好的深度缓冲,关闭深度写入(ZWrite Off),并开启透明度混合(Blend)。它就能准确判断哪些部分应被自身遮挡,从而正确进行半透明渲染。

📝 代码实现与详解

以下是一个典型的URP(通用渲染管线)下的双Pass Shader案例。

cs 复制代码
Shader "zmx/WhiteSnow_Kimono_A_SKEL_Code"
{
	Properties
	{
		[HideInInspector] _EmissionColor("Emission Color", Color) = (1,1,1,1)
		_RimBase("RimBase", Float) = 0
		_RimScale("RimScale", Range(0, 1)) = 0
		_RimPower("RimPower", Float) = 0
		[HDR] _RimInnerColor("RimInnerColor", Color) = (0, 0, 0, 0)
		[HDR] _RimOutColor("RimOutColor", Color) = (0, 0, 0, 0)
		_FlowMap("FlowMap", 2D) = "black" {}
		_FlowTiling("FlowTiling", Float) = 1
		_FlowSpeed("FlowSpeed", Float) = 0
		_FlowColor("FlowColor", Color) = (0, 0, 0, 0)
		_MaskMap("MaskMap", 2D) = "white" {}
		_MaskTiling("MaskTiling", Float) = 1
		_FlowIntensity("FlowIntensity", Range(0, 100)) = 0

		[HideInInspector] _QueueOffset("_QueueOffset", Float) = 0
		[HideInInspector] _QueueControl("_QueueControl", Float) = -1

		[HideInInspector][NoScaleOffset] unity_Lightmaps("unity_Lightmaps", 2DArray) = "" {}
		[HideInInspector][NoScaleOffset] unity_LightmapsInd("unity_LightmapsInd", 2DArray) = "" {}
		[HideInInspector][NoScaleOffset] unity_ShadowMasks("unity_ShadowMasks", 2DArray) = "" {}

		[HideInInspector][ToggleUI] _AddPrecomputedVelocity("Add Precomputed Velocity", Float) = 1
		[ToggleUI] _ReceiveShadows("Receive Shadows", Float) = 1.0

		[HideInInspector] _XRMotionVectorsPass("_XRMotionVectorsPass", Float) = 1
	}

	SubShader
	{
		LOD 0

		Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Transparent" "Queue"="Transparent" "UniversalMaterialType"="Unlit" }

		Cull Back
		AlphaToMask Off

		HLSLINCLUDE
		#pragma target 4.5
		#pragma prefer_hlslcc gles
		#if (SHADER_TARGET > 35) && defined(SHADER_API_GLES3)
			#error For WebGL2/GLES3, please set your shader target to 3.5 via SubShader options. URP shaders in ASE use target 4.5 by default.
		#endif
		#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
		#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Filtering.hlsl"
		#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

		CBUFFER_START(UnityPerMaterial)
			float4 _FlowColor;
			float4 _RimInnerColor;
			float4 _RimOutColor;
			float _FlowIntensity;
			float _FlowTiling;
			float _MaskTiling;
			float _FlowSpeed;
			float _RimPower;
			float _RimScale;
			float _RimBase;
		CBUFFER_END

		sampler2D _FlowMap;
		sampler2D _MaskMap;

		void Kimono_FullShading(float3 positionWS, half3 normalForDot, half3 viewDirForDot, out float3 color, out float alpha)
		{
			float3 objToWorld49 = mul(GetObjectToWorldMatrix(), float4(float3(0, 0, 0), 1)).xyz;
			float3 break43 = (positionWS - objToWorld49);
			float2 appendResult40 = float2(break43.x, break43.y);
			float mulTime47 = _TimeParameters.x * _FlowSpeed;
			float2 appendResult50 = float2(0.0, mulTime47);
			float3 var_FinalFlowColor54 = (_FlowColor.rgb * ((1.0 - tex2D(_FlowMap, appendResult40 * _FlowTiling).r) * tex2D(_MaskMap, appendResult40 * _MaskTiling + appendResult50).r));
			half dotResult16 = dot(normalForDot, viewDirForDot);
			float saferPower18 = abs((1.0 - (float)dotResult16));
			float var_Fresnel25 = saturate(((pow(saferPower18, _RimPower) * _RimScale) + _RimBase));
			float3 lerpResult31 = lerp(_RimInnerColor.rgb, _RimOutColor.rgb, var_Fresnel25);
			float3 var_RimColor33 = lerpResult31;
			color = ((0.2 + (sin(_TimeParameters.x) - -1.0) * (1.0 - 0.2) / (1.0 - -1.0)) * _FlowIntensity * var_FinalFlowColor54) + var_RimColor33;
			alpha = saturate(var_Fresnel25);
		}

		void Kimono_FresnelAlpha(float3 normalForDot, float3 viewDirForDot, out float alpha)
		{
			float dotResult16 = dot(normalForDot, viewDirForDot);
			float saferPower18 = abs((1.0 - dotResult16));
			float var_Fresnel25 = saturate(((pow(saferPower18, _RimPower) * _RimScale) + _RimBase));
			alpha = saturate(var_Fresnel25);
		}

		ENDHLSL

		Pass
		{
			Name "ExtraPrePass"

			Blend One Zero
			Cull Off
			ZWrite On
			ZTest LEqual
			Offset 0, 0
			ColorMask 0

			HLSLPROGRAM

			#define _SURFACE_TYPE_TRANSPARENT 1

			#pragma vertex vert
			#pragma fragment frag

			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"

			#if defined(LOD_FADE_CROSSFADE)
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LODCrossFade.hlsl"
			#endif

			struct Attributes
			{
				float4 positionOS : POSITION;
				half3 normalOS : NORMAL;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct PackedVaryings
			{
				float4 positionCS : SV_POSITION;
				float4 positionWSAndFogFactor : TEXCOORD0;
				half3 normalWS : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			PackedVaryings VertexFunction(Attributes input)
			{
				PackedVaryings output = (PackedVaryings)0;
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_TRANSFER_INSTANCE_ID(input, output);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

				float3 vertexValue = float3(0, 0, 0);
				input.positionOS.xyz += vertexValue;
				input.normalOS = input.normalOS;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
				VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS);

				float fogFactor = 0;
				output.positionCS = vertexInput.positionCS;
				output.positionWSAndFogFactor = float4(vertexInput.positionWS, fogFactor);
				output.normalWS = normalInput.normalWS;
				return output;
			}

			PackedVaryings vert(Attributes input)
			{
				return VertexFunction(input);
			}

			half4 frag(PackedVaryings input) : SV_Target
			{
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

				#if defined(_SURFACE_TYPE_TRANSPARENT)
				const bool isTransparent = true;
				#else
				const bool isTransparent = false;
				#endif

				float3 PositionWS = input.positionWSAndFogFactor.xyz;
				half3 NormalWS = normalize(input.normalWS);
				float4 ScreenPosNorm = float4(GetNormalizedScreenSpaceUV(input.positionCS), input.positionCS.zw);
				float4 ClipPos = ComputeClipSpacePosition(ScreenPosNorm.xy, input.positionCS.z) * input.positionCS.w;
				half3 ViewDirWS = GetWorldSpaceNormalizeViewDir(PositionWS);

				float3 Color = float3(0, 0, 0);
				float Alpha = 1;
				float AlphaClipThreshold = 0.5;

				#if defined(_ALPHATEST_ON)
				AlphaDiscard(Alpha, AlphaClipThreshold);
				#endif

				InputData inputData = (InputData)0;
				inputData.positionWS = PositionWS;
				inputData.positionCS = float4(input.positionCS.xy, ClipPos.zw / ClipPos.w);
				inputData.normalizedScreenSpaceUV = ScreenPosNorm.xy;
				inputData.normalWS = NormalWS;
				inputData.viewDirectionWS = ViewDirWS;

				#if defined(LOD_FADE_CROSSFADE)
				LODFadeCrossFade(input.positionCS);
				#endif

				return half4(Color, OutputAlpha(Alpha, isTransparent));
			}
			ENDHLSL
		}

		Pass
		{
			Name "Forward"
			Tags { "LightMode"="UniversalForwardOnly" }

			Blend SrcAlpha OneMinusSrcAlpha, SrcAlpha OneMinusSrcAlpha
			ZWrite On
			ZTest LEqual
			Offset 0, 0
			ColorMask RGBA

			HLSLPROGRAM

			#pragma shader_feature_local_fragment _RECEIVE_SHADOWS_OFF
			#define _SURFACE_TYPE_TRANSPARENT 1

			#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
			#pragma multi_compile_fragment _ DEBUG_DISPLAY

			#pragma vertex vert
			#pragma fragment frag

			#define SHADERPASS SHADERPASS_UNLIT

			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Fog.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRendering.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Debug/Debugging3D.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceData.hlsl"

			#if defined(LOD_FADE_CROSSFADE)
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LODCrossFade.hlsl"
			#endif

			#if defined(ASE_EARLY_Z_DEPTH_OPTIMIZE) && (SHADER_TARGET >= 45)
			#define ASE_SV_DEPTH SV_DepthLessEqual
			#define ASE_SV_POSITION_QUALIFIERS linear noperspective centroid
			#else
			#define ASE_SV_DEPTH SV_Depth
			#define ASE_SV_POSITION_QUALIFIERS
			#endif

			struct Attributes
			{
				float4 positionOS : POSITION;
				half3 normalOS : NORMAL;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct PackedVaryings
			{
				ASE_SV_POSITION_QUALIFIERS float4 positionCS : SV_POSITION;
				float4 positionWSAndFogFactor : TEXCOORD0;
				half3 normalWS : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			PackedVaryings VertexFunction(Attributes input)
			{
				PackedVaryings output = (PackedVaryings)0;
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_TRANSFER_INSTANCE_ID(input, output);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

				float3 vertexValue = float3(0, 0, 0);
				input.positionOS.xyz += vertexValue;
				input.normalOS = input.normalOS;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
				VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS);

				float fogFactor = 0;
				output.positionCS = vertexInput.positionCS;
				output.positionWSAndFogFactor = float4(vertexInput.positionWS, fogFactor);
				output.normalWS = normalInput.normalWS;
				return output;
			}

			PackedVaryings vert(Attributes input)
			{
				return VertexFunction(input);
			}

			half4 frag(PackedVaryings input
				#if defined(ASE_DEPTH_WRITE_ON)
				, out float outputDepth : ASE_SV_DEPTH
				#endif
				#ifdef _WRITE_RENDERING_LAYERS
				, out uint outRenderingLayers : SV_Target1
				#endif
				) : SV_Target
			{
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

				#if defined(_SURFACE_TYPE_TRANSPARENT)
				const bool isTransparent = true;
				#else
				const bool isTransparent = false;
				#endif

				#if defined(LOD_FADE_CROSSFADE)
				LODFadeCrossFade(input.positionCS);
				#endif

				#if defined(MAIN_LIGHT_CALCULATE_SHADOWS)
				float4 shadowCoord = TransformWorldToShadowCoord(input.positionWSAndFogFactor.xyz);
				#else
				float4 shadowCoord = float4(0, 0, 0, 0);
				#endif

				float3 PositionWS = input.positionWSAndFogFactor.xyz;
				half3 ViewDirWS = GetWorldSpaceNormalizeViewDir(PositionWS);
				float4 ShadowCoord = shadowCoord;
				float4 ScreenPosNorm = float4(GetNormalizedScreenSpaceUV(input.positionCS), input.positionCS.zw);
				float4 ClipPos = ComputeClipSpacePosition(ScreenPosNorm.xy, input.positionCS.z) * input.positionCS.w;
				half3 NormalWS = normalize(input.normalWS);

				float3 Color;
				float Alpha;
				Kimono_FullShading(PositionWS, NormalWS, ViewDirWS, Color, Alpha);

				float AlphaClipThreshold = 0.5;

				#if defined(ASE_DEPTH_WRITE_ON)
				float DeviceDepth = input.positionCS.z;
				#endif

				#if defined(_ALPHATEST_ON)
				AlphaDiscard(Alpha, AlphaClipThreshold);
				#endif

				#if defined(MAIN_LIGHT_CALCULATE_SHADOWS) && defined(ASE_CHANGES_WORLD_POS)
				ShadowCoord = TransformWorldToShadowCoord(PositionWS);
				#endif

				InputData inputData = (InputData)0;
				inputData.positionWS = PositionWS;
				inputData.positionCS = float4(input.positionCS.xy, ClipPos.zw / ClipPos.w);
				inputData.normalizedScreenSpaceUV = ScreenPosNorm.xy;
				inputData.normalWS = NormalWS;
				inputData.viewDirectionWS = ViewDirWS;

				#if defined(_SCREEN_SPACE_OCCLUSION) && !defined(_SURFACE_TYPE_TRANSPARENT)
				float2 normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
				AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(normalizedScreenSpaceUV);
				Color.rgb *= aoFactor.directAmbientOcclusion;
				#endif

				#if defined(_DBUFFER)
				ApplyDecalToBaseColor(input.positionCS, Color);
				#endif

				#if defined(ASE_DEPTH_WRITE_ON)
				outputDepth = DeviceDepth;
				#endif

				#ifdef _WRITE_RENDERING_LAYERS
				outRenderingLayers = EncodeMeshRenderingLayer();
				#endif

				return half4(Color, OutputAlpha(Alpha, isTransparent));
			}
			ENDHLSL
		}

		Pass
		{
			Name "DepthOnly"
			Tags { "LightMode"="DepthOnly" }

			ZWrite On
			ColorMask 0
			AlphaToMask Off

			HLSLPROGRAM

			#define _SURFACE_TYPE_TRANSPARENT 1

			#pragma vertex vert
			#pragma fragment frag

			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"

			#if defined(LOD_FADE_CROSSFADE)
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LODCrossFade.hlsl"
			#endif

			#if defined(ASE_EARLY_Z_DEPTH_OPTIMIZE) && (SHADER_TARGET >= 45)
			#define ASE_SV_DEPTH SV_DepthLessEqual
			#define ASE_SV_POSITION_QUALIFIERS linear noperspective centroid
			#else
			#define ASE_SV_DEPTH SV_Depth
			#define ASE_SV_POSITION_QUALIFIERS
			#endif

			struct Attributes
			{
				float4 positionOS : POSITION;
				half3 normalOS : NORMAL;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct PackedVaryings
			{
				ASE_SV_POSITION_QUALIFIERS float4 positionCS : SV_POSITION;
				float4 ase_texcoord : TEXCOORD0;
				float4 ase_texcoord1 : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			PackedVaryings VertexFunction(Attributes input)
			{
				PackedVaryings output = (PackedVaryings)0;
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_TRANSFER_INSTANCE_ID(input, output);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

				float3 ase_normalWS = TransformObjectToWorldNormal(input.normalOS);
				output.ase_texcoord.xyz = ase_normalWS;
				float3 ase_positionWS = TransformObjectToWorld((input.positionOS).xyz);
				output.ase_texcoord1.xyz = ase_positionWS;
				output.ase_texcoord.w = 0;
				output.ase_texcoord1.w = 0;

				float3 vertexValue = float3(0, 0, 0);
				input.positionOS.xyz += vertexValue;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
				output.positionCS = vertexInput.positionCS;
				return output;
			}

			PackedVaryings vert(Attributes input)
			{
				return VertexFunction(input);
			}

			half4 frag(PackedVaryings input
				#if defined(ASE_DEPTH_WRITE_ON)
				, out float outputDepth : ASE_SV_DEPTH
				#endif
				) : SV_Target
			{
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

				float4 ScreenPosNorm = float4(GetNormalizedScreenSpaceUV(input.positionCS), input.positionCS.zw);
				float4 ClipPos = ComputeClipSpacePosition(ScreenPosNorm.xy, input.positionCS.z) * input.positionCS.w;

				float3 ase_normalWS = input.ase_texcoord.xyz;
				float3 ase_positionWS = input.ase_texcoord1.xyz;
				float3 ase_viewVectorWS = (_WorldSpaceCameraPos.xyz - ase_positionWS);
				float3 ase_viewDirWS = normalize(ase_viewVectorWS);

				float Alpha;
				Kimono_FresnelAlpha(ase_normalWS, ase_viewDirWS, Alpha);
				float AlphaClipThreshold = 0.5;

				#if defined(ASE_DEPTH_WRITE_ON)
				float DeviceDepth = input.positionCS.z;
				#endif

				#if defined(_ALPHATEST_ON)
				AlphaDiscard(Alpha, AlphaClipThreshold);
				#endif

				#if defined(LOD_FADE_CROSSFADE)
				LODFadeCrossFade(input.positionCS);
				#endif

				#if defined(ASE_DEPTH_WRITE_ON)
				outputDepth = DeviceDepth;
				#endif

				return 0;
			}
			ENDHLSL
		}

		Pass
		{
			Name "SceneSelectionPass"
			Tags { "LightMode"="SceneSelectionPass" }

			Cull Off
			AlphaToMask Off

			HLSLPROGRAM

			#define _SURFACE_TYPE_TRANSPARENT 1

			#pragma vertex vert
			#pragma fragment frag

			#define ATTRIBUTES_NEED_NORMAL
			#define ATTRIBUTES_NEED_TANGENT
			#define SHADERPASS SHADERPASS_DEPTHONLY

			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRendering.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"

			struct Attributes
			{
				float4 positionOS : POSITION;
				half3 normalOS : NORMAL;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct PackedVaryings
			{
				float4 positionCS : SV_POSITION;
				float4 ase_texcoord : TEXCOORD0;
				float4 ase_texcoord1 : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			int _ObjectId;
			int _PassValue;

			struct SurfaceDescription
			{
				float Alpha;
				float AlphaClipThreshold;
			};

			PackedVaryings VertexFunction(Attributes input)
			{
				PackedVaryings output;
				ZERO_INITIALIZE(PackedVaryings, output);

				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_TRANSFER_INSTANCE_ID(input, output);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

				float3 ase_normalWS = TransformObjectToWorldNormal(input.normalOS);
				output.ase_texcoord.xyz = ase_normalWS;
				float3 ase_positionWS = TransformObjectToWorld((input.positionOS).xyz);
				output.ase_texcoord1.xyz = ase_positionWS;
				output.ase_texcoord.w = 0;
				output.ase_texcoord1.w = 0;

				float3 vertexValue = float3(0, 0, 0);
				input.positionOS.xyz += vertexValue;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
				output.positionCS = vertexInput.positionCS;
				return output;
			}

			PackedVaryings vert(Attributes input)
			{
				return VertexFunction(input);
			}

			half4 frag(PackedVaryings input) : SV_Target
			{
				SurfaceDescription surfaceDescription = (SurfaceDescription)0;

				float3 ase_normalWS = input.ase_texcoord.xyz;
				float3 ase_positionWS = input.ase_texcoord1.xyz;
				float3 ase_viewVectorWS = (_WorldSpaceCameraPos.xyz - ase_positionWS);
				float3 ase_viewDirWS = normalize(ase_viewVectorWS);

				float a;
				Kimono_FresnelAlpha(ase_normalWS, ase_viewDirWS, a);
				surfaceDescription.Alpha = a;
				surfaceDescription.AlphaClipThreshold = 0.5;

				#ifdef _ALPHATEST_ON
				clip(surfaceDescription.Alpha - surfaceDescription.AlphaClipThreshold);
				#endif

				return half4(_ObjectId, _PassValue, 1.0, 1.0);
			}
			ENDHLSL
		}

		Pass
		{
			Name "ScenePickingPass"
			Tags { "LightMode"="Picking" }

			AlphaToMask Off

			HLSLPROGRAM

			#define _SURFACE_TYPE_TRANSPARENT 1

			#pragma vertex vert
			#pragma fragment frag

			#define ATTRIBUTES_NEED_NORMAL
			#define ATTRIBUTES_NEED_TANGENT
			#define SHADERPASS SHADERPASS_DEPTHONLY

			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRendering.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"

			#if defined(LOD_FADE_CROSSFADE)
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LODCrossFade.hlsl"
			#endif

			struct Attributes
			{
				float4 positionOS : POSITION;
				half3 normalOS : NORMAL;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct PackedVaryings
			{
				float4 positionCS : SV_POSITION;
				float4 ase_texcoord : TEXCOORD0;
				float4 ase_texcoord1 : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			float4 _SelectionID;

			struct SurfaceDescription
			{
				float Alpha;
				float AlphaClipThreshold;
			};

			PackedVaryings VertexFunction(Attributes input)
			{
				PackedVaryings output;
				ZERO_INITIALIZE(PackedVaryings, output);

				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_TRANSFER_INSTANCE_ID(input, output);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

				float3 ase_normalWS = TransformObjectToWorldNormal(input.normalOS);
				output.ase_texcoord.xyz = ase_normalWS;
				float3 ase_positionWS = TransformObjectToWorld((input.positionOS).xyz);
				output.ase_texcoord1.xyz = ase_positionWS;
				output.ase_texcoord.w = 0;
				output.ase_texcoord1.w = 0;

				float3 vertexValue = float3(0, 0, 0);
				input.positionOS.xyz += vertexValue;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
				output.positionCS = vertexInput.positionCS;
				return output;
			}

			PackedVaryings vert(Attributes input)
			{
				return VertexFunction(input);
			}

			half4 frag(PackedVaryings input) : SV_Target
			{
				SurfaceDescription surfaceDescription = (SurfaceDescription)0;

				float3 ase_normalWS = input.ase_texcoord.xyz;
				float3 ase_positionWS = input.ase_texcoord1.xyz;
				float3 ase_viewVectorWS = (_WorldSpaceCameraPos.xyz - ase_positionWS);
				float3 ase_viewDirWS = normalize(ase_viewVectorWS);

				float a;
				Kimono_FresnelAlpha(ase_normalWS, ase_viewDirWS, a);
				surfaceDescription.Alpha = a;
				surfaceDescription.AlphaClipThreshold = 0.5;

				#ifdef _ALPHATEST_ON
				clip(surfaceDescription.Alpha - surfaceDescription.AlphaClipThreshold);
				#endif

				half4 outColor = 0;
				outColor = unity_SelectionID;
				return outColor;
			}
			ENDHLSL
		}

		Pass
		{
			Name "DepthNormals"
			Tags { "LightMode"="DepthNormalsOnly" }

			ZTest LEqual
			ZWrite On

			HLSLPROGRAM

			#define _SURFACE_TYPE_TRANSPARENT 1

			#pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT

			#pragma vertex vert
			#pragma fragment frag

			#define ATTRIBUTES_NEED_NORMAL
			#define ATTRIBUTES_NEED_TANGENT
			#define VARYINGS_NEED_NORMAL_WS
			#define SHADERPASS SHADERPASS_DEPTHNORMALSONLY

			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRendering.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"

			#if defined(LOD_FADE_CROSSFADE)
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LODCrossFade.hlsl"
			#endif

			#if defined(ASE_EARLY_Z_DEPTH_OPTIMIZE) && (SHADER_TARGET >= 45)
			#define ASE_SV_DEPTH SV_DepthLessEqual
			#define ASE_SV_POSITION_QUALIFIERS linear noperspective centroid
			#else
			#define ASE_SV_DEPTH SV_Depth
			#define ASE_SV_POSITION_QUALIFIERS
			#endif

			struct Attributes
			{
				float4 positionOS : POSITION;
				half3 normalOS : NORMAL;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct PackedVaryings
			{
				ASE_SV_POSITION_QUALIFIERS float4 positionCS : SV_POSITION;
				half3 normalWS : TEXCOORD0;
				float4 ase_texcoord1 : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			PackedVaryings VertexFunction(Attributes input)
			{
				PackedVaryings output;
				ZERO_INITIALIZE(PackedVaryings, output);

				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_TRANSFER_INSTANCE_ID(input, output);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

				float3 ase_positionWS = TransformObjectToWorld((input.positionOS).xyz);
				output.ase_texcoord1.xyz = ase_positionWS;
				output.ase_texcoord1.w = 0;

				float3 vertexValue = float3(0, 0, 0);
				input.positionOS.xyz += vertexValue;
				input.normalOS = input.normalOS;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
				VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS);

				output.positionCS = vertexInput.positionCS;
				output.normalWS = normalInput.normalWS;
				return output;
			}

			PackedVaryings vert(Attributes input)
			{
				return VertexFunction(input);
			}

			void frag(PackedVaryings input
				, out half4 outNormalWS : SV_Target0
				#if defined(ASE_DEPTH_WRITE_ON)
				, out float outputDepth : ASE_SV_DEPTH
				#endif
				#ifdef _WRITE_RENDERING_LAYERS
				, out uint outRenderingLayers : SV_Target1
				#endif
				)
			{
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

				half3 NormalWS = normalize(input.normalWS);
				float4 ScreenPosNorm = float4(GetNormalizedScreenSpaceUV(input.positionCS), input.positionCS.zw);
				float4 ClipPos = ComputeClipSpacePosition(ScreenPosNorm.xy, input.positionCS.z) * input.positionCS.w;

				float3 ase_positionWS = input.ase_texcoord1.xyz;
				float3 ase_viewVectorWS = (_WorldSpaceCameraPos.xyz - ase_positionWS);
				float3 ase_viewDirWS = normalize(ase_viewVectorWS);

				float Alpha;
				Kimono_FresnelAlpha((float3)NormalWS, ase_viewDirWS, Alpha);
				float AlphaClipThreshold = 0.5;

				#if defined(ASE_DEPTH_WRITE_ON)
				float DeviceDepth = input.positionCS.z;
				#endif

				#ifdef _ALPHATEST_ON
				clip(Alpha - AlphaClipThreshold);
				#endif

				#if defined(LOD_FADE_CROSSFADE)
				LODFadeCrossFade(input.positionCS);
				#endif

				#if defined(ASE_DEPTH_WRITE_ON)
				outputDepth = DeviceDepth;
				#endif

				#if defined(_GBUFFER_NORMALS_OCT)
				float2 octNormalWS = PackNormalOctQuadEncode(NormalWS);
				float2 remappedOctNormalWS = saturate(octNormalWS * 0.5 + 0.5);
				half3 packedNormalWS = PackFloat2To888(remappedOctNormalWS);
				outNormalWS = half4(packedNormalWS, 0.0);
				#else
				outNormalWS = half4(NormalizeNormalPerPixel(NormalWS), 0.0);
				#endif

				#ifdef _WRITE_RENDERING_LAYERS
				outRenderingLayers = EncodeMeshRenderingLayer();
				#endif
			}
			ENDHLSL
		}

		Pass
		{
			Name "GBuffer"
			Tags { "LightMode"="UniversalGBuffer" }

			Blend SrcAlpha OneMinusSrcAlpha, SrcAlpha OneMinusSrcAlpha
			ZWrite On
			ZTest LEqual
			Offset 0, 0
			ColorMask RGBA

			HLSLPROGRAM

			#pragma shader_feature_local_fragment _RECEIVE_SHADOWS_OFF
			#define _SURFACE_TYPE_TRANSPARENT 1

			#pragma exclude_renderers glcore gles3

			#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
			#pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT
			#pragma multi_compile_fragment _ _RENDER_PASS_ENABLED

			#pragma vertex vert
			#pragma fragment frag

			#define SHADERPASS SHADERPASS_GBUFFER

			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRendering.hlsl"
			#include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"

			#if defined(LOD_FADE_CROSSFADE)
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LODCrossFade.hlsl"
			#endif

			#if defined(UNITY_INSTANCING_ENABLED) && defined(_TERRAIN_INSTANCED_PERPIXEL_NORMAL)
			#define ENABLE_TERRAIN_PERPIXEL_NORMAL
			#endif

			#if defined(ASE_EARLY_Z_DEPTH_OPTIMIZE) && (SHADER_TARGET >= 45)
			#define ASE_SV_DEPTH SV_DepthLessEqual
			#define ASE_SV_POSITION_QUALIFIERS linear noperspective centroid
			#else
			#define ASE_SV_DEPTH SV_Depth
			#define ASE_SV_POSITION_QUALIFIERS
			#endif

			struct Attributes
			{
				float4 positionOS : POSITION;
				half3 normalOS : NORMAL;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct PackedVaryings
			{
				ASE_SV_POSITION_QUALIFIERS float4 positionCS : SV_POSITION;
				float3 positionWS : TEXCOORD0;
				half3 normalWS : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			#ifdef SCENEPICKINGPASS
			float4 _SelectionID;
			#endif

			#ifdef SCENESELECTIONPASS
			int _ObjectId;
			int _PassValue;
			#endif

			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/GBufferOutput.hlsl"

			PackedVaryings VertexFunction(Attributes input)
			{
				PackedVaryings output = (PackedVaryings)0;
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_TRANSFER_INSTANCE_ID(input, output);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

				float3 vertexValue = float3(0, 0, 0);
				input.positionOS.xyz += vertexValue;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
				VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS);

				output.positionCS = vertexInput.positionCS;
				output.positionWS = vertexInput.positionWS;
				output.normalWS = normalInput.normalWS;
				return output;
			}

			PackedVaryings vert(Attributes input)
			{
				return VertexFunction(input);
			}

			GBufferFragOutput frag(PackedVaryings input
				#if defined(ASE_DEPTH_WRITE_ON)
				, out float outputDepth : ASE_SV_DEPTH
				#endif
				)
			{
				UNITY_SETUP_INSTANCE_ID(input);
				UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

				#if defined(LOD_FADE_CROSSFADE)
				LODFadeCrossFade(input.positionCS);
				#endif

				float3 PositionWS = input.positionWS;
				half3 ViewDirWS = GetWorldSpaceNormalizeViewDir(PositionWS);
				float4 ScreenPosNorm = float4(GetNormalizedScreenSpaceUV(input.positionCS), input.positionCS.zw);
				float4 ClipPos = ComputeClipSpacePosition(ScreenPosNorm.xy, input.positionCS.z) * input.positionCS.w;
				half3 NormalWS = normalize(input.normalWS);

				float3 Color;
				float Alpha;
				Kimono_FullShading(PositionWS, NormalWS, ViewDirWS, Color, Alpha);

				float AlphaClipThreshold = 0.5;

				#if defined(ASE_DEPTH_WRITE_ON)
				float DeviceDepth = input.positionCS.z;
				#endif

				#ifdef _ALPHATEST_ON
				clip(Alpha - AlphaClipThreshold);
				#endif

				InputData inputData = (InputData)0;
				inputData.positionWS = PositionWS;
				inputData.positionCS = float4(input.positionCS.xy, ClipPos.zw / ClipPos.w);
				inputData.normalizedScreenSpaceUV = ScreenPosNorm.xy;
				inputData.normalWS = NormalWS;
				inputData.viewDirectionWS = ViewDirWS;

				#if defined(_DBUFFER)
				ApplyDecalToBaseColor(input.positionCS, Color);
				#endif

				#if defined(ASE_DEPTH_WRITE_ON)
				outputDepth = DeviceDepth;
				#endif

				SurfaceData surfaceData = (SurfaceData)0;
				surfaceData.albedo = Color;
				surfaceData.alpha = Alpha;

				#if defined(_SCREEN_SPACE_OCCLUSION)
				float2 normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
				AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(normalizedScreenSpaceUV);
				surfaceData.occlusion = aoFactor.directAmbientOcclusion;
				#else
				surfaceData.occlusion = 1;
				#endif

				return PackGBuffersSurfaceData(surfaceData, inputData, float3(0, 0, 0));
			}

			ENDHLSL
		}
	}

	FallBack Off
}
  • 性能开销 :会额外增加一次完整的绘制调用,因此对渲染性能有一定影响。

  • 渲染限制:仅在物体自身的渲染排序中有效。对于多个半透明物体相互穿插的复杂情况,由于引擎限制,依然可能出现排序错误的穿模现象。

⚖️ 替代方案选择

对于不同的需求,也可以考虑更简单的方案:

  • Alpha Test(透明度测试) :也叫"镂空",通过 clip函数直接丢弃透明度低于阈值的片元,无需关闭深度写入。它不会有排序错误,但效果生硬,非0即1,无法实现边缘柔和的半透明效果。

  • 性能更优的替代:Single Pass + ZWrite On :如果你的Shader逻辑简单(如纯色、无顶点动画),也可以尝试合并为单个Pass并保持ZWrite On,这能避免双Pass的性能浪费,但兼容性不如双Pass方案。

💎 总结与建议

双Pass Shader是解决半透明物体自身穿模问题的"标准答案"。如果你的模型几何关系复杂,或因深度排序混乱导致视觉效果不佳,这个方法值得尝试。但如果对性能要求极高,或需要在移动设备上有良好表现,那么Alpha Test或调整模型使其结构更简单等方案可能更合适。

相关推荐
nnsix3 小时前
Unity HybridCLR 笔记
笔记·unity·游戏引擎
nnsix4 小时前
Unity Addressables 笔记
unity·游戏引擎
RReality4 小时前
【Unity Shader URP】视差贴图 实战教程
ui·平面·unity·游戏引擎·图形渲染·贴图
小清兔19 小时前
Addressable的设置打包流程
笔记·游戏·unity·c#
3D霸霸1 天前
Sourcetree 拉取新工程
数据仓库·unity
程序员正茂1 天前
Unity3d中RawImage显示视频画面偏白的解决方法
unity·视频·rawimage
mxwin1 天前
Unity SetPassCall和DrawCall的区别是什么
unity·游戏引擎·shader
电子云与长程纠缠1 天前
UE5 GameFeature创建与使用
开发语言·学习·ue5·游戏引擎
moonsims1 天前
AiBrainLink:无人化系统异构连接架构-多执行体、多链路(5G+自组网)、多业务流(控制、遥测、视频、文件)透明传输、多对多控制
unity·游戏引擎