Transparency issues

Discussion in 'ShaderLab' started by nullstar, Oct 4, 2011.

  1. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    Hi folks. I'm having issues with transparency on my custom shaders. The set up I have is two quad-spheres at the same position but one larger than the other. The shader for the inner/smaller quad-sphere is set to render during the geometry queue and is opaque, and the shader for the outer/larger quad-sphere is set to render during the transparency queue and has alpha blending on. The issue is that parts of the outer sphere are obscured by parts of the inner sphere incorrectly and thus it doesn't render completely. I've been messing around with z-write settings but no combination seems to fix the problem. You can see a screen grab of the issue here:

    [​IMG]

    Does anyone know what could be causing this and how I could go about fixing it?

    Cheers folks
  2. Vancent

    Vancent

    New Member

    Joined:
    Oct 3, 2011
    Messages:
    6
    If your talking about those blue bands, that looks like a texture issue to me.
  3. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    Yes its the blue bands I'm meaning. Unfortunately it's not an issue with the textures. I've tested this by making the shader non-transparent which results in a perfect sphere being drawn with all the textures for the different faces of the quad-sphere joining and aligning correctly. Also, although you cant see it in a static image, when the camera is panned around the bands shift and change as if those pixels are being clipped against some of the faces behind them.

    You can kind of make that out in the screen grab since at the outer edges where the inner and outer spheres dont overlap, the outer transparent sphere is drawn correctly. Its only in the inner overlapping area where strangeness happens.
    Last edited: Oct 4, 2011
  4. Daniel Brauer

    Daniel Brauer

    Member

    Joined:
    Aug 11, 2006
    Messages:
    3,267
    The error looks to be in the pattern a skybox might make. What shaders are you using?
  5. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    Wow ... amazing spot! I think you might have hit the nail on the head there. I'm using my own custom shaders but I think the issue is possibly in how I generate the quad-sphere geometry. I started this project off by creating a texture generator for the built in skybox shader but then decided I wanted to use my own skybox geometry so I odered the geometry generation so it would fit the same layout of textures as for Unity's skybox shader. When I came to creating the quad-spheres I used the same layout again, so that's probably it. I'll have a tinker and get back to you but I think you've got it.

    Cheers :)
  6. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    Wow, extremely fustrating. I thought perhaps some parts of the geometry were inverted given the strange layout but it seems to not matter at all how the faces are generated, one face of the quad-sphere will always not alpha-blend correctly. To be absolutely sure I compared a super simplified custom transparency shader with Unity's built-in transparency diffuse shader, and sure enough the Unity shader works and the custom shader always fails to blend one face but manages the others fine.

    For the setup for this simple test I placed a standard unity sphere with a standard diffuse shader in the middle, and a quad-sphere around it for which I swapped the custom transparency shader and Unity's transparency shader back and forth to compare the results.

    Unity transparency diffuse shader:
    [​IMG]

    Custom transparency shader:
    [​IMG]

    As you can see the front face fails to blend with the custom shader (the quad-sphere is made up off 6 sub-meshes, one for each face). I can only imagine I'm doing something wrong in the custom shader then but I'm not sure what it is given it's simplicity, but then again I'm not very experienced with shaders. Here's the custom shader code:

    Code (csharp):
    1.  
    2. Shader "Test/TestShader"
    3. {
    4.     Properties
    5.     {
    6.     }
    7.    
    8.     SubShader
    9.     {
    10.         Pass
    11.         {
    12.             Name "TestShader"
    13.  
    14.             Tags
    15.         {
    16.         "Queue" = "Transparent"
    17.         "RenderType" = "Transparent"
    18.         }
    19.  
    20.             Cull Back
    21.         Blend SrcAlpha One
    22.            
    23.             CGPROGRAM
    24.  
    25.             #pragma vertex vert
    26.             #pragma fragment frag
    27.  
    28.         #include "UnityCG.cginc"
    29.            
    30.            
    31.             struct appdata
    32.         {
    33.             float4 vertex : POSITION;
    34.         };
    35.  
    36.         struct v2f
    37.         {
    38.         float4 pos : SV_POSITION;
    39.         };
    40.  
    41.         v2f vert(appdata v)
    42.         {
    43.             v2f o;
    44.         o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    45.         return o;
    46.         }
    47.  
    48.         half4 frag(v2f i) : COLOR
    49.         {
    50.                 return float4(1.0, 0.0, 0.0, 0.5);
    51.         }
    52.  
    53.             ENDCG
    54.         }
    55.     }
    56.    
    57.     FallBack "Diffuse"
    58. }
    59.  
    Does anyone have any ideas?
  7. Daniel Brauer

    Daniel Brauer

    Member

    Joined:
    Aug 11, 2006
    Messages:
    3,267
    To be precise, it looks like your custom shader is blending properly, but the front right face of the quadsphere is being drawn before the inner sphere. Because your custom shader writes to the Z buffer, this prevents the inner sphere from being drawn at all behind that face. If that's the case, then turning off Z writing is unlikely to solve anything because the drawing will still be out of order. It's worth trying, though, just to narrow down the problem. At the moment I have no idea why a single face would be drawing before opaque geometry when it's in the same (Transparent) render queue as the other faces.
  8. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    Ok I think I solved it and it looks like I might have discovered a bug in Unity. My theory is that when using fragment shaders with multiple sub-meshes, the first sub-mesh ignores the Queue and/or RenderType tag. Therefore what's happening in my test example is that the first quad-sphere face is getting rendered before the underneath unity sphere since it's ignoring the tags, hence it doesn't seem to blend correctly.

    I ran a couple of tests to try and verify this. The first was to alter the camera background colour whilst the setup was running and sure enough the first quad-sphere face is varying its colour which tells me that it is indeed alpha-blending but just that the diffuse sphere hasn't been rendered behind it yet. To be totaly sure I altered the quad-sphere mesh generation code to create 7 sub-meshes instead of 6 and just left the first one empty. In that instance all the quad-sphere faces rendered and blended correctly giving the right results.


    Edit: Thanks for the reply and the help Daniel, managed to post at the same time :). I have a theory anyway, I'll forward it to the Unity guys and see what they make of it.
    Last edited: Oct 4, 2011
  9. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    Rightio, another update. I've done some more experimenting and my theory was wrong. After playing around with more geometry using the 'empty sub-mesh fix' the problem started to resurface. Therefore it seems the issue isn't only to do with the first sub-mesh, it was just a coincidence that the change in the geometry in my simple test version resulted in them being rendered in the correct order. It seems that when using custom vertex / fragment shaders its just not actualy possible to control the render order by specifying queue and renderType tags, the resultant order will always come out randomly despite what you set.

    After further playing around it looks like the only way to get shaders in Unity to render in the correct order for transparency is to use surface shaders instead of vertex / fragment shaders. I knocked up a simple transparent surface shader which did the same as my simple vertex / fragment transparent shader and this worked correctly without any issues in all instances.

    I can only imagine that the Unity engine is doing something under the hood which makes transparent surface shaders render correctly but has been neglected for vertex / fragment shaders, or vice versa, something extra is being done under the hood for fragment shaders which is stopping them from working correctly.
  10. Daniel Brauer

    Daniel Brauer

    Member

    Joined:
    Aug 11, 2006
    Messages:
    3,267
    Can you post the simplest reproduction project possible? Although the error you're seeing is definitely there, I think it's very unlikely that vertex/fragment shaders cannot be ordered properly. Surface shaders are the recommended method for integrating Unity's lighting, but they are all converted to vertex/fragment shaders before compilation.
  11. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    Last edited: Oct 5, 2011
  12. Daniel Brauer

    Daniel Brauer

    Member

    Joined:
    Aug 11, 2006
    Messages:
    3,267
    The meshes appear to be missing from that project.
  13. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    The meshes are generated by the quadsphere script when you run the scene :)
  14. Daniel Brauer

    Daniel Brauer

    Member

    Joined:
    Aug 11, 2006
    Messages:
    3,267
    Your assessment that the faces are being drawn in a seemingly arbitrary order is correct, but the problem is with the Tags being in the Pass block instead of the Subshader. Tags apply to Subshaders as a whole, so I hoisting the Tags into the Subshader fixes the issue:

    [​IMG]

    Code (csharp):
    1. Shader "Test/TestFragmentShader"
    2. {
    3.     Properties
    4.     {
    5.     }
    6.    
    7.     SubShader
    8.     {
    9.         Tags
    10.         {
    11.             "Queue" = "Transparent"
    12.             "RenderType" = "Transparent"
    13.         }
    14.         Pass
    15.         {
    16.  
    17.             Cull Back
    18.             Blend SrcAlpha One
    19.            
    20.             CGPROGRAM
    21.  
    22.             #pragma vertex vert
    23.             #pragma fragment frag
    24.  
    25.             #include "UnityCG.cginc"
    26.            
    27.            
    28.             struct appdata
    29.             {
    30.                 float4 vertex : POSITION;
    31.             };
    32.  
    33.             struct v2f
    34.             {
    35.                 float4 pos : SV_POSITION;
    36.             };
    37.  
    38.             v2f vert(appdata v)
    39.             {
    40.                 v2f o;
    41.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    42.                 return o;
    43.             }
    44.  
    45.             half4 frag(v2f i) : COLOR
    46.             {
    47.                 return float4(1.0, 0.0, 0.0, 0.5);
    48.             }
    49.  
    50.             ENDCG
    51.         }
    52.     }
    53. }
    You have found a bug, though: Unity should complain when it finds Tags where it won't respect them instead of ignoring them silently like it does.
    Last edited: Oct 5, 2011
  15. nullstar

    nullstar

    New Member

    Joined:
    Jul 2, 2010
    Messages:
    185
    Thanks for all your help Daniel, I don't think I would have solved this one without your input. I've just gone back over the Unity documentation and I see the mistake I've made now that you pointed out. I hadn't realised that Unity shaders accept two distinct tag blocks, one for the sub-shader and one for the passes, each of which only accept certain tags. I'd made the assumption that all the tags should be placed within the same tag block. You learn something new every day :).

    Thanks again for your input, you've been phenomenal, an absolute star.
    Last edited: Oct 5, 2011