Search Unity

Double sided transparent surface shader - Undefined Behavior?

Discussion in 'Shaders' started by khlorghaal, Aug 13, 2020.

  1. khlorghaal

    khlorghaal

    Joined:
    Nov 15, 2018
    Posts:
    9
    So I figured out how to make a double sided transparent surface shader, despite all sane knowledge on the topic informing me this should not be possible.

    However, my process of dealing with unity's meta-shader syntax, is to guess randomly until it does what I want. Because that is often easier than trying to develop any meaningful understanding from the 30% complete documentation.

    Code (cg):
    1. Shader "khlor/..." {
    2.     Properties {...}
    3.     SubShader {
    4.         CGINCLUDE
    5.         ...
    6.         ENDCG
    7.  
    8.         LOD 400
    9.         Tags { "RenderType"="Transparent" "Queue"="Transparent"}
    10.         cull Front
    11.         zwrite on
    12.         CGPROGRAM
    13.         #pragma surface surf StandardSpecular alpha:blend vertex:vert
    14.         ENDCG
    15.  
    16.         Tags { "RenderType"="Transparent" "Queue"="Transparent+1"}
    17.         cull back
    18.         zwrite on
    19.         CGPROGRAM
    20.         #pragma surface surf StandardSpecular alpha:blend vertex:vert
    21.         ENDCG
    22.     }
    23. }
    Note there are no pass blocks, since "pass"es are not be compatible with surface shaders, but this does multiple passes despite not using "pass".
    I have no idea what including two cgprograms in the same subshader is even doing. My guess is its abusing unintended side-effects of unity's pragmas.

    Flaw - overlapping more than one closed surface, within the same queue, will cause z-artefacts
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The only thing you're doing wrong here is having two sets of tags. the second one will be ignored as only the first set of tags within a SubShader will be used. A shader can't have more than one
    Queue
    , or one
    RenderType
    . Instead the order of the passes (including those generated by multiple Surface Shaders) determine the order they're rendered in.*

    * If multiple objects use the same material, Unity may choose to render multiple objects using the first pass, then multiple objects with the second pass rather than running both for each object before rendering the next. There were also some bugs with this that can cause passes to render out of order in this situation, though they might be fixed now.

    Having multiple Surface Shaders in a single
    SubShader
    does work though, and is a common way to do double sided transparent shaders. Each Surface Shader individually generates a set of passes, specifically
    ForwardBase
    ,
    ForwardAdd
    , and
    Meta
    for anything with
    alpha
    in the
    #pragma surface
    line. Without
    alpha
    they'll also generate a
    Deferred
    pass, and can optionally generate a
    ShadowCaster
    pass with
    addshadow
    . This is all fine.

    However the "z-artifacts" you're seeing are some parts of the transparent object disappear when you're expecting to be able to see them. Unfortunately what you're seeing is the expected result when using
    ZWrite On
    with any transparent surface. Having two passes just makes it more obvious, but it'll happen with a single transparent pass on a complex enough mesh.

    This is because correct, fast, and efficient sorting of real time transparent surfaces is an unsolved problem.
    https://forum.unity.com/threads/render-mode-transparent-doesnt-work-see-video.357853/#post-2315934