Skip to content

Commit 8188c28

Browse files
gmitrano-unityEvergreen
authored andcommitted
Rewrite Alpha To Coverage Alpha Clipping Logic
This change rewrites a good deal of the logic within URP's alpha clipping function that's responsible for handling alpha to coverage behavior. In addition to handling the degenerate use case where cutoff is set to zero, this code now also handles the case where alpha and cutoff are equal. It does this by modifying the sharpening function to yield 1.0 rather than 0.5 in the equal case while still maintaining the other parts of the curve.
1 parent da4cda2 commit 8188c28

File tree

16 files changed

+3085
-55
lines changed

16 files changed

+3085
-55
lines changed

Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,30 +180,40 @@ bool IsAlphaToMaskAvailable()
180180
return (_AlphaToMaskAvailable != 0.0);
181181
}
182182

183+
// Returns a sharpened alpha value for use with alpha to coverage
184+
// This function behaves correctly in cases where alpha and cutoff are constant values (degenerate usage of alpha clipping)
185+
half SharpenAlphaStrict(half alpha, half alphaClipTreshold)
186+
{
187+
half dAlpha = fwidth(alpha);
188+
return saturate(((alpha - alphaClipTreshold - (0.5 * dAlpha)) / max(dAlpha, 0.0001)) + 1.0);
189+
}
190+
183191
// When AlphaToMask is available: Returns a modified alpha value that should be exported from the shader so it can be combined with the sample mask
184192
// When AlphaToMask is not available: Terminates the current invocation if the alpha value is below the cutoff and returns the input alpha value otherwise
185193
half AlphaClip(half alpha, half cutoff)
186194
{
187-
// Produce 0.0 if the input value would be clipped by traditional alpha clipping and produce the original input value otherwise.
188-
// WORKAROUND: The alpha parameter in this ternary expression MUST be converted to a float in order to work around a known HLSL compiler bug.
189-
// See Fogbugz 934464 for more information
190-
half clippedAlpha = (alpha >= cutoff) ? float(alpha) : 0.0;
195+
bool a2c = IsAlphaToMaskAvailable();
191196

192-
// Calculate a specialized alpha value that should be used when alpha-to-coverage is enabled
197+
// We explicitly detect cases where the alpha cutoff threshold is zero or below.
198+
// When this case occurs, we need to modify the alpha to coverage logic to avoid visual artifacts.
199+
bool zeroCutoff = (cutoff <= 0.0);
193200

194201
// If the user has specified zero as the cutoff threshold, the expectation is that the shader will function as if alpha-clipping was disabled.
195202
// Ideally, the user should just turn off the alpha-clipping feature in this case, but in order to make this case work as expected, we force alpha
196203
// to 1.0 here to ensure that alpha-to-coverage never throws away samples when its active. (This would cause opaque objects to appear transparent)
197-
half alphaToCoverageAlpha = (cutoff <= 0.0) ? 1.0 : SharpenAlpha(alpha, cutoff);
204+
half alphaToCoverageAlpha = zeroCutoff ? 1.0 : SharpenAlphaStrict(alpha, cutoff);
205+
206+
// When the alpha to coverage alpha is used for clipping, we subtract a small value from it to ensure that pixels with zero alpha exit early
207+
// rather than running the entire shader and then multiplying the sample coverage mask by zero which outputs nothing.
208+
half clipVal = (a2c && !zeroCutoff) ? (alphaToCoverageAlpha - 0.0001) : (alpha - cutoff);
198209

199210
// When alpha-to-coverage is available: Use the specialized value which will be exported from the shader and combined with the MSAA coverage mask.
200211
// When alpha-to-coverage is not available: Use the "clipped" value. A clipped value will always result in thread termination via the clip() logic below.
201-
alpha = IsAlphaToMaskAvailable() ? alphaToCoverageAlpha : clippedAlpha;
212+
half outputAlpha = a2c ? alphaToCoverageAlpha : alpha;
202213

203-
// Terminate any threads that have an alpha value of 0.0 since we know they won't contribute anything to the final image
204-
clip(alpha - 0.0001);
214+
clip(clipVal);
205215

206-
return alpha;
216+
return outputAlpha;
207217
}
208218
#endif
209219

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!114 &-1835980439576540527
4+
MonoBehaviour:
5+
m_ObjectHideFlags: 11
6+
m_CorrespondingSourceObject: {fileID: 0}
7+
m_PrefabInstance: {fileID: 0}
8+
m_PrefabAsset: {fileID: 0}
9+
m_GameObject: {fileID: 0}
10+
m_Enabled: 1
11+
m_EditorHideFlags: 0
12+
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
13+
m_Name:
14+
m_EditorClassIdentifier:
15+
version: 9
16+
--- !u!21 &2100000
17+
Material:
18+
serializedVersion: 8
19+
m_ObjectHideFlags: 0
20+
m_CorrespondingSourceObject: {fileID: 0}
21+
m_PrefabInstance: {fileID: 0}
22+
m_PrefabAsset: {fileID: 0}
23+
m_Name: A0C0
24+
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
25+
m_Parent: {fileID: 0}
26+
m_ModifiedSerializedProperties: 0
27+
m_ValidKeywords:
28+
- _ALPHATEST_ON
29+
m_InvalidKeywords: []
30+
m_LightmapFlags: 4
31+
m_EnableInstancingVariants: 0
32+
m_DoubleSidedGI: 0
33+
m_CustomRenderQueue: 2450
34+
stringTagMap:
35+
RenderType: TransparentCutout
36+
disabledShaderPasses:
37+
- MOTIONVECTORS
38+
m_LockedProperties:
39+
m_SavedProperties:
40+
serializedVersion: 3
41+
m_TexEnvs:
42+
- _BaseMap:
43+
m_Texture: {fileID: 0}
44+
m_Scale: {x: 1, y: 1}
45+
m_Offset: {x: 0, y: 0}
46+
- _BumpMap:
47+
m_Texture: {fileID: 0}
48+
m_Scale: {x: 1, y: 1}
49+
m_Offset: {x: 0, y: 0}
50+
- _DetailAlbedoMap:
51+
m_Texture: {fileID: 0}
52+
m_Scale: {x: 1, y: 1}
53+
m_Offset: {x: 0, y: 0}
54+
- _DetailMask:
55+
m_Texture: {fileID: 0}
56+
m_Scale: {x: 1, y: 1}
57+
m_Offset: {x: 0, y: 0}
58+
- _DetailNormalMap:
59+
m_Texture: {fileID: 0}
60+
m_Scale: {x: 1, y: 1}
61+
m_Offset: {x: 0, y: 0}
62+
- _EmissionMap:
63+
m_Texture: {fileID: 0}
64+
m_Scale: {x: 1, y: 1}
65+
m_Offset: {x: 0, y: 0}
66+
- _MainTex:
67+
m_Texture: {fileID: 0}
68+
m_Scale: {x: 1, y: 1}
69+
m_Offset: {x: 0, y: 0}
70+
- _MetallicGlossMap:
71+
m_Texture: {fileID: 0}
72+
m_Scale: {x: 1, y: 1}
73+
m_Offset: {x: 0, y: 0}
74+
- _OcclusionMap:
75+
m_Texture: {fileID: 0}
76+
m_Scale: {x: 1, y: 1}
77+
m_Offset: {x: 0, y: 0}
78+
- _ParallaxMap:
79+
m_Texture: {fileID: 0}
80+
m_Scale: {x: 1, y: 1}
81+
m_Offset: {x: 0, y: 0}
82+
- _SpecGlossMap:
83+
m_Texture: {fileID: 0}
84+
m_Scale: {x: 1, y: 1}
85+
m_Offset: {x: 0, y: 0}
86+
- unity_Lightmaps:
87+
m_Texture: {fileID: 0}
88+
m_Scale: {x: 1, y: 1}
89+
m_Offset: {x: 0, y: 0}
90+
- unity_LightmapsInd:
91+
m_Texture: {fileID: 0}
92+
m_Scale: {x: 1, y: 1}
93+
m_Offset: {x: 0, y: 0}
94+
- unity_ShadowMasks:
95+
m_Texture: {fileID: 0}
96+
m_Scale: {x: 1, y: 1}
97+
m_Offset: {x: 0, y: 0}
98+
m_Ints: []
99+
m_Floats:
100+
- _AddPrecomputedVelocity: 0
101+
- _AlphaClip: 1
102+
- _AlphaToMask: 1
103+
- _Blend: 0
104+
- _BlendModePreserveSpecular: 1
105+
- _BumpScale: 1
106+
- _ClearCoatMask: 0
107+
- _ClearCoatSmoothness: 0
108+
- _Cull: 2
109+
- _Cutoff: 0
110+
- _DetailAlbedoMapScale: 1
111+
- _DetailNormalMapScale: 1
112+
- _DstBlend: 0
113+
- _DstBlendAlpha: 0
114+
- _EnvironmentReflections: 1
115+
- _GlossMapScale: 0
116+
- _Glossiness: 0
117+
- _GlossyReflections: 0
118+
- _Metallic: 0
119+
- _OcclusionStrength: 1
120+
- _Parallax: 0.005
121+
- _QueueOffset: 0
122+
- _ReceiveShadows: 1
123+
- _Smoothness: 0.5
124+
- _SmoothnessTextureChannel: 0
125+
- _SpecularHighlights: 1
126+
- _SrcBlend: 1
127+
- _SrcBlendAlpha: 1
128+
- _Surface: 0
129+
- _WorkflowMode: 1
130+
- _ZWrite: 1
131+
m_Colors:
132+
- _BaseColor: {r: 1, g: 1, b: 1, a: 0}
133+
- _Color: {r: 1, g: 1, b: 1, a: 0}
134+
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
135+
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
136+
m_BuildTextureStacks: []
137+
m_AllowLocking: 1

Tests/SRPTests/Projects/UniversalGraphicsTest_Foundation/Assets/Scenes/250_AlphaToCoverage/A0C0.mat.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!114 &-1835980439576540527
4+
MonoBehaviour:
5+
m_ObjectHideFlags: 11
6+
m_CorrespondingSourceObject: {fileID: 0}
7+
m_PrefabInstance: {fileID: 0}
8+
m_PrefabAsset: {fileID: 0}
9+
m_GameObject: {fileID: 0}
10+
m_Enabled: 1
11+
m_EditorHideFlags: 0
12+
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
13+
m_Name:
14+
m_EditorClassIdentifier:
15+
version: 9
16+
--- !u!21 &2100000
17+
Material:
18+
serializedVersion: 8
19+
m_ObjectHideFlags: 0
20+
m_CorrespondingSourceObject: {fileID: 0}
21+
m_PrefabInstance: {fileID: 0}
22+
m_PrefabAsset: {fileID: 0}
23+
m_Name: A0C1
24+
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
25+
m_Parent: {fileID: 0}
26+
m_ModifiedSerializedProperties: 0
27+
m_ValidKeywords:
28+
- _ALPHATEST_ON
29+
m_InvalidKeywords: []
30+
m_LightmapFlags: 4
31+
m_EnableInstancingVariants: 0
32+
m_DoubleSidedGI: 0
33+
m_CustomRenderQueue: 2450
34+
stringTagMap:
35+
RenderType: TransparentCutout
36+
disabledShaderPasses:
37+
- MOTIONVECTORS
38+
m_LockedProperties:
39+
m_SavedProperties:
40+
serializedVersion: 3
41+
m_TexEnvs:
42+
- _BaseMap:
43+
m_Texture: {fileID: 0}
44+
m_Scale: {x: 1, y: 1}
45+
m_Offset: {x: 0, y: 0}
46+
- _BumpMap:
47+
m_Texture: {fileID: 0}
48+
m_Scale: {x: 1, y: 1}
49+
m_Offset: {x: 0, y: 0}
50+
- _DetailAlbedoMap:
51+
m_Texture: {fileID: 0}
52+
m_Scale: {x: 1, y: 1}
53+
m_Offset: {x: 0, y: 0}
54+
- _DetailMask:
55+
m_Texture: {fileID: 0}
56+
m_Scale: {x: 1, y: 1}
57+
m_Offset: {x: 0, y: 0}
58+
- _DetailNormalMap:
59+
m_Texture: {fileID: 0}
60+
m_Scale: {x: 1, y: 1}
61+
m_Offset: {x: 0, y: 0}
62+
- _EmissionMap:
63+
m_Texture: {fileID: 0}
64+
m_Scale: {x: 1, y: 1}
65+
m_Offset: {x: 0, y: 0}
66+
- _MainTex:
67+
m_Texture: {fileID: 0}
68+
m_Scale: {x: 1, y: 1}
69+
m_Offset: {x: 0, y: 0}
70+
- _MetallicGlossMap:
71+
m_Texture: {fileID: 0}
72+
m_Scale: {x: 1, y: 1}
73+
m_Offset: {x: 0, y: 0}
74+
- _OcclusionMap:
75+
m_Texture: {fileID: 0}
76+
m_Scale: {x: 1, y: 1}
77+
m_Offset: {x: 0, y: 0}
78+
- _ParallaxMap:
79+
m_Texture: {fileID: 0}
80+
m_Scale: {x: 1, y: 1}
81+
m_Offset: {x: 0, y: 0}
82+
- _SpecGlossMap:
83+
m_Texture: {fileID: 0}
84+
m_Scale: {x: 1, y: 1}
85+
m_Offset: {x: 0, y: 0}
86+
- unity_Lightmaps:
87+
m_Texture: {fileID: 0}
88+
m_Scale: {x: 1, y: 1}
89+
m_Offset: {x: 0, y: 0}
90+
- unity_LightmapsInd:
91+
m_Texture: {fileID: 0}
92+
m_Scale: {x: 1, y: 1}
93+
m_Offset: {x: 0, y: 0}
94+
- unity_ShadowMasks:
95+
m_Texture: {fileID: 0}
96+
m_Scale: {x: 1, y: 1}
97+
m_Offset: {x: 0, y: 0}
98+
m_Ints: []
99+
m_Floats:
100+
- _AddPrecomputedVelocity: 0
101+
- _AlphaClip: 1
102+
- _AlphaToMask: 1
103+
- _Blend: 0
104+
- _BlendModePreserveSpecular: 1
105+
- _BumpScale: 1
106+
- _ClearCoatMask: 0
107+
- _ClearCoatSmoothness: 0
108+
- _Cull: 2
109+
- _Cutoff: 1
110+
- _DetailAlbedoMapScale: 1
111+
- _DetailNormalMapScale: 1
112+
- _DstBlend: 0
113+
- _DstBlendAlpha: 0
114+
- _EnvironmentReflections: 1
115+
- _GlossMapScale: 0
116+
- _Glossiness: 0
117+
- _GlossyReflections: 0
118+
- _Metallic: 0
119+
- _OcclusionStrength: 1
120+
- _Parallax: 0.005
121+
- _QueueOffset: 0
122+
- _ReceiveShadows: 1
123+
- _Smoothness: 0.5
124+
- _SmoothnessTextureChannel: 0
125+
- _SpecularHighlights: 1
126+
- _SrcBlend: 1
127+
- _SrcBlendAlpha: 1
128+
- _Surface: 0
129+
- _WorkflowMode: 1
130+
- _ZWrite: 1
131+
m_Colors:
132+
- _BaseColor: {r: 1, g: 1, b: 1, a: 0}
133+
- _Color: {r: 1, g: 1, b: 1, a: 0}
134+
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
135+
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
136+
m_BuildTextureStacks: []
137+
m_AllowLocking: 1

Tests/SRPTests/Projects/UniversalGraphicsTest_Foundation/Assets/Scenes/250_AlphaToCoverage/A0C1.mat.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)