Hi all!
For our project we wanted our opaque geometry to be able to fade out without them being rendered as transparent. So we decided to use the dithering transparency (also called screen-door transparency) technique to use the alpha clipping to fade out parts of the geometry depending on its transparency value.
Since we still wanted to be able to use the URP Lit Shader functionality, I copied all the Lit shader files and includes and modified the SurfaceInput Alpha() function to accomodate for dithering which was easy enough. I just needed the Clip Space position as an additional parameter and changed the function calls accordingly and it worked! Except it didn’t quite work as it should.
On the screenshot you can see two spheres:
The left one uses our custom DitheredLit shader and the right one uses a simple vertex/fragment shader that does the same thing for testing purposes.
Now after lots of digging around I am fairly confident that the Clip Space position is the issue here and something called Homogenous Clip Space because I believe in the Lit shader the CS position is homogenous whereas in the simple vertex/fragment shader it isn’t. But that’s all I was able to find out and now I am stuck.
Here is my custom Alpha function in the custom SurfaceInput.hlsl file for the Lit shader:
half DitheredAlpha(half albedoAlpha, half4 color, half cutoff, half transparency, half ditherSize, float4 positionCS, float2 uv)
{
#if !defined(_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A) && !defined(_GLOSSINESS_FROM_BASE_ALPHA)
half alpha = albedoAlpha * transparency;
#else
half alpha = transparency;
#endif
#if defined(_ALPHATEST_ON)
float DITHER_THRESHOLDS[16] =
{
1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
};
//float4 screenPos = ComputeScreenPos(positionCS);
//uv = screenPos.xy / screenPos.w;
uv = positionCS.xy / positionCS.w;
uv *= _ScreenParams.xy;
uint index = (uint(uv.x) % 4) * 4 + uint(uv.y) % 4;
// Returns > 0 if not clipped, < 0 if clipped based
// on the dither
clip(alpha - DITHER_THRESHOLDS[index]);
#endif
return alpha;
}
And here is the simple vertex/fragment shader for testing purposes:
Shader "Custom/SurfaceDither"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_Transparency ("Transparency", Range(0,1)) = 1.0
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
Pass
{
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
float4 _Color;
float _Transparency;
struct v2f
{
float4 pos : POSITION;
float4 col : COLOR;
float4 spos : TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.spos = ComputeScreenPos(o.pos);
return o;
}
float4 frag(v2f i) : COLOR
{
float4 col = _Color;
float2 pos = i.spos.xy / i.spos.w;
pos *= _ScreenParams.xy;
// Define a dither threshold matrix which can
// be used to define how a 4x4 set of pixels
// will be dithered
float DITHER_THRESHOLDS[16] =
{
1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
};
int index = (int(pos.x) % 4) * 4 + int(pos.y) % 4;
clip(_Transparency - DITHER_THRESHOLDS[index]);
return col;
}
ENDCG
}
}
Fallback "VertexLit"
}
We are using Unity 2020.3.17f1 and URP 10.6.0.
I would appreciate any help, thanks!
