
在Unity中,实现半透明效果通常会关闭深度写入(ZWrite Off)。这会导致渲染器无法正确判断物体自身的远近关系,从而在复杂的遮挡或非凸面体上产生排序错误,从视觉上看就像是穿模了。
🛠️ 解决方案:双Pass Shader原理
这个方法通过两个Pass来解决排序问题:
-
深度写入Pass (Depth Write Pass): 位于顶部,先执行。它只将模型的精准深度信息写入到深度缓冲区,但不输出任何颜色(
ColorMask 0)。这样GPU就能知道模型的正确前后关系了。 -
透明混合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或调整模型使其结构更简单等方案可能更合适。