Monday, April 22, 2019

unity - Why does this transparent shader cause objects' z-sorting to be backwards?


I am making a Klondike Solitaire game, with the intent of later expanding it to encompass other solitaire games and allow deck customization. As part of this, the playing card meshes I am creating programatically are mostly opaque, but require non-alphatest transparency in order to draw the pips (rank/suit icons) correctly.


The problem is this:


image of problem: back cards are being rendered in front of front cards


The z-sorting of the cards is the exact opposite of what it's supposed to be. I know that transparency is the problem because the z-sorting works properly when it's disabled (the #pragma alpha:fade in the shader is removed):


transparency disabled: z-order is correct but the material is fundamentally broken


...which obviously breaks how the cards' material is being rendered.


This is the shader I have:


Shader "Custom/Card" {

Properties {
_CardAtlasTex ("Card Atlas", 2D) = "white" {} // card atlas texture
_ColorUVZone ("Colour UV Zone", Vector) = (0,0,0,0) // texture zone inside which the pips are, and so the suit colour should be used
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
LOD 200

CGPROGRAM
#pragma surface surf Lambert vertex:vert alpha:fade


sampler2D _CardAtlasTex;
float4 _ColorUVZone;

struct Input {
float2 uv_CardAtlasTex;
float3 vertexColor;
};

void vert (inout appdata_full v, out Input o) { // http://answers.unity3d.com/questions/923726/unity-5-standard-shader-support-for-vertex-colors.html

UNITY_INITIALIZE_OUTPUT(Input,o);
o.vertexColor = v.color;
}

void surf (Input IN, inout SurfaceOutput o) {
half4 card = tex2D (_CardAtlasTex, IN.uv_CardAtlasTex);
o.Albedo = card;
o.Alpha = 1; // make it opaque to start with

// if this is a pip, colourize it

if (IN.uv_CardAtlasTex.x >= _ColorUVZone.x && IN.uv_CardAtlasTex.y >= _ColorUVZone.y && IN.uv_CardAtlasTex.x <= _ColorUVZone.z && IN.uv_CardAtlasTex.y <= _ColorUVZone.w) {
o.Albedo *= IN.vertexColor; // colourized
o.Alpha = card.a; // transparented
}
}
ENDCG
}
FallBack "Diffuse"
}


Things I have tried:



  • Changing the subshader's RenderType to things like Transparent or TransparentCutout, or removing it entirely. No effect.

  • Changing the subshader's Queue to things like Transparent or AlphaTest, or removing it entirely. No effect.

  • Adding a ZWrite On (just before the LOD 200). Does nothing because the generated code forces ZWrite Off due to having #pragma alpha:fade (as far as I can tell), and putting the line after the pragma in hopes of re-overwriting it is a syntax error.


From #3, I'm guessing that if I can somehow force ZWrite On despite the alpha:fade, it should work as expected. But chances are there's something else I'm missing that could work.


Other notes:



  • The problem is very consistent: the back-most cards are always drawn front-most, no matter what the shuffle is or whether the lower cards are ahead or behind.


  • There is no alternative to using full transparency; pips can be any colour against any background. (The face card designs don't use transparency.)

  • All cards use the same material, which uses a single texture atlas.



Answer



After doing some tests, it looks like this may be a bug in a recent version of Unity.


When you create a new material, under the hood its Custom Render Queue property defaults to -1, meaning "use whatever queue is specified in the shader file," as we would expect.


But as soon as we change the material's shader to use a custom one (I've now tried this with newly-created Surface and Unlit shaders), Unity sets the Custom Render Queue to 2000, meaning "ignore what the shader says and always render this in the opaque geometry queue"


That seems blatantly wrong, so I expect they'll fix that. In the meantime, as Toomai discovered in the comments above, we can fix this by...



  1. Switch the Inspector to Debug mode by opening the hamburger menu (≡) in the top-right corner and selecting "Debug"



Switching the Inspector mode to Debug



  1. Replace the 2000 in the Custom Render Queue field with -1 instead


The Custom Render Queue field in the Debug Inspector


Your shader's queue settings should now be respected again.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...