Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Sorting issue with transparent shader and line renderer

Discussion in 'Shaders' started by robson_unity208, Dec 11, 2020.

  1. robson_unity208

    robson_unity208

    Joined:
    Jul 14, 2020
    Posts:
    7
    Hello,
    I am having a problem where the draw order gets confused, sometimes the line renderer appears as it should, but depending on the camera angle, it renders over the other mesh.
    There's a video here showing what happens.
    I have already messed around with the shader, ZWrite, Transparency sort mode on graphic settings, but nothing works.

    Here's the shader I am using for the water:

    Code (CSharp):
    1. Shader "Roystan/Toon/Water Calm"
    2. {
    3.     Properties
    4.     {
    5.         // What color the water will sample when the surface below is shallow.
    6.         _DepthGradientShallow("Depth Gradient Shallow", Color) = (0.325, 0.807, 0.971, 0.725)
    7.  
    8.         // What color the water will sample when the surface below is at its deepest.
    9.         _DepthGradientDeep("Depth Gradient Deep", Color) = (0.086, 0.407, 1, 0.749)
    10.  
    11.         // Maximum distance the surface below the water will affect the color gradient.
    12.         _DepthMaxDistance("Depth Maximum Distance", Float) = 1
    13.  
    14.         // Color to render the foam generated by objects intersecting the surface.
    15.         _FoamColor("Foam Color", Color) = (1,1,1,1)
    16.  
    17.         // Noise texture used to generate waves.
    18.         _SurfaceNoise("Surface Noise", 2D) = "white" {}
    19.  
    20.         // Speed, in UVs per second the noise will scroll. Only the xy components are used.
    21.         _SurfaceNoiseScroll("Surface Noise Scroll Amount", Vector) = (0.03, 0.03, 0, 0)
    22.  
    23.         // Values in the noise texture above this cutoff are rendered on the surface.
    24.         _SurfaceNoiseCutoff("Surface Noise Cutoff", Range(0, 1)) = 0.777
    25.  
    26.         // Red and green channels of this texture are used to offset the
    27.         // noise texture to create distortion in the waves.
    28.         _SurfaceDistortion("Surface Distortion", 2D) = "white" {}  
    29.  
    30.         // Multiplies the distortion by this value.
    31.         _SurfaceDistortionAmount("Surface Distortion Amount", Range(0, 1)) = 0.27
    32.  
    33.         // Control the distance that surfaces below the water will contribute
    34.         // to foam being rendered.
    35.         _FoamMaxDistance("Foam Maximum Distance", Float) = 0.4
    36.         _FoamMinDistance("Foam Minimum Distance", Float) = 0.04
    37.         _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
    38.         _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
    39.  
    40. [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
    41.         _MetallicGlossMap("Metallic", 2D) = "white" {}
    42.        [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
    43.         [ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0
    44.     }
    45.     SubShader
    46.     {
    47.         Tags
    48.         {
    49.             "Queue" = "Transparent"
    50.         }
    51.  
    52.         Pass
    53.         {
    54.             // Transparent "normal" blending.
    55.             Blend SrcAlpha OneMinusSrcAlpha
    56.             ZWrite Off
    57.  
    58.             CGPROGRAM
    59.             #define SMOOTHSTEP_AA 0.01
    60.  
    61.             #pragma vertex vert
    62.             #pragma fragment frag
    63.             //#pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF
    64.             //#pragma shader_feature_local _GLOSSYREFLECTIONS_OFF
    65.             //#pragma shader_feature_local _METALLICGLOSSMAP
    66.  
    67.  
    68.  
    69.             #include "UnityCG.cginc"
    70.  
    71.             // Blends two colors using the same algorithm that our shader is using
    72.             // to blend with the screen. This is usually called "normal blending",
    73.             // and is similar to how software like Photoshop blends two layers.
    74.             float4 alphaBlend(float4 top, float4 bottom)
    75.             {
    76.                 float3 color = (top.rgb * top.a) + (bottom.rgb * (1 - top.a));
    77.                 float alpha = top.a + bottom.a * (1 - top.a);
    78.  
    79.                 return float4(color, alpha);
    80.             }
    81.  
    82.             struct appdata
    83.             {
    84.                 float4 vertex : POSITION;
    85.                 float4 uv : TEXCOORD0;
    86.                 float3 normal : NORMAL;
    87.             };
    88.  
    89.             struct v2f
    90.             {
    91.                 float4 vertex : SV_POSITION;  
    92.                 float2 noiseUV : TEXCOORD0;
    93.                 float2 distortUV : TEXCOORD1;
    94.                 float4 screenPosition : TEXCOORD2;
    95.                 float3 viewNormal : NORMAL;
    96.             };
    97.  
    98.             sampler2D _SurfaceNoise;
    99.             float4 _SurfaceNoise_ST;
    100.  
    101.             sampler2D _SurfaceDistortion;
    102.             float4 _SurfaceDistortion_ST;
    103.  
    104.             v2f vert (appdata v)
    105.             {
    106.                 v2f o;
    107.  
    108.                 o.vertex = UnityObjectToClipPos(v.vertex);
    109.                 o.screenPosition = ComputeScreenPos(o.vertex);
    110.                 o.distortUV = TRANSFORM_TEX(v.uv, _SurfaceDistortion);
    111.                 o.noiseUV = TRANSFORM_TEX(v.uv, _SurfaceNoise);
    112.                 o.viewNormal = COMPUTE_VIEW_NORMAL;
    113.  
    114.                 return o;
    115.             }
    116.  
    117.             float4 _DepthGradientShallow;
    118.             float4 _DepthGradientDeep;
    119.             float4 _FoamColor;
    120.  
    121.             float _DepthMaxDistance;
    122.             float _FoamMaxDistance;
    123.             float _FoamMinDistance;
    124.             float _SurfaceNoiseCutoff;
    125.             float _SurfaceDistortionAmount;
    126.  
    127.             float2 _SurfaceNoiseScroll;
    128.  
    129.             sampler2D _CameraDepthTexture;
    130.             sampler2D _CameraNormalsTexture;
    131.  
    132.             float4 frag (v2f i) : SV_Target
    133.             {
    134.                 // Retrieve the current depth value of the surface behind the
    135.                 // pixel we are currently rendering.
    136.                 float existingDepth01 = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPosition)).r;
    137.                 // Convert the depth from non-linear 0...1 range to linear
    138.                 // depth, in Unity units.
    139.                 float existingDepthLinear = LinearEyeDepth(existingDepth01);
    140.  
    141.                 // Difference, in Unity units, between the water's surface and the object behind it.
    142.                 float depthDifference = existingDepthLinear - i.screenPosition.w;
    143.  
    144.                 // Calculate the color of the water based on the depth using our two gradient colors.
    145.                 float waterDepthDifference01 = saturate(depthDifference / _DepthMaxDistance);
    146.                 float4 waterColor = lerp(_DepthGradientShallow, _DepthGradientDeep, waterDepthDifference01);
    147.              
    148.                 // Retrieve the view-space normal of the surface behind the
    149.                 // pixel we are currently rendering.
    150.                 float3 existingNormal = tex2Dproj(_CameraNormalsTexture, UNITY_PROJ_COORD(i.screenPosition));
    151.              
    152.                 // Modulate the amount of foam we display based on the difference
    153.                 // between the normals of our water surface and the object behind it.
    154.                 // Larger differences allow for extra foam to attempt to keep the overall
    155.                 // amount consistent.
    156.                 float3 normalDot = saturate(dot(existingNormal, i.viewNormal));
    157.                 float foamDistance = lerp(_FoamMaxDistance, _FoamMinDistance, normalDot);
    158.                 float foamDepthDifference01 = saturate(depthDifference / foamDistance);
    159.  
    160.                 float surfaceNoiseCutoff = foamDepthDifference01 * _SurfaceNoiseCutoff;
    161.  
    162.                 float2 distortSample = (tex2D(_SurfaceDistortion, i.distortUV).xy * 2 - 1) * _SurfaceDistortionAmount;
    163.  
    164.                 // Distort the noise UV based off the RG channels (using xy here) of the distortion texture.
    165.                 // Also offset it by time, scaled by the scroll speed.
    166.                 float2 noiseUV = float2((i.noiseUV.x + _Time.y * _SurfaceNoiseScroll.x) + distortSample.x,
    167.                 (i.noiseUV.y + _Time.y * _SurfaceNoiseScroll.y) + distortSample.y);
    168.                 float surfaceNoiseSample = tex2D(_SurfaceNoise, noiseUV).r;
    169.  
    170.                 // Use smoothstep to ensure we get some anti-aliasing in the transition from foam to surface.
    171.                 // Uncomment the line below to see how it looks without AA.
    172.                 // float surfaceNoise = surfaceNoiseSample > surfaceNoiseCutoff ? 1 : 0;
    173.                 float surfaceNoise = smoothstep(surfaceNoiseCutoff - SMOOTHSTEP_AA, surfaceNoiseCutoff + SMOOTHSTEP_AA, surfaceNoiseSample);
    174.  
    175.                 float4 surfaceNoiseColor = _FoamColor;
    176.                 surfaceNoiseColor.a *= surfaceNoise;
    177.  
    178.                 // Use normal alpha blending to combine the foam with the surface.
    179.                 return alphaBlend(surfaceNoiseColor, waterColor);
    180.             }
    181.             ENDCG
    182.         }
    183.     }
    184. }
    185.  
     
  2. whitexroft

    whitexroft

    Joined:
    Oct 22, 2012
    Posts:
    48
    If both water and line renderer are drawn with transparent shader, their sorting becomes not a simple task
    The easiest fix for you would be getting the line renderer being opaque, so the water is always rendered last.
    Or you can also force the water to be the last by changing its "Queue" tag or rendering it with another camera
     
  3. robson_unity208

    robson_unity208

    Joined:
    Jul 14, 2020
    Posts:
    7
    By changing the queue the only thing I can achieve is the laser always being on top, or always on bottom. When the laser goes below the mesh, it works fine, problem is when it's inside it.
     
  4. whitexroft

    whitexroft

    Joined:
    Oct 22, 2012
    Posts:
    48
    Fair enough, but you do see the issue. Transparent object don't sort per pixel, rather per entire object.
    So you have to either split the line renderer in sections, or render it opaque
     
  5. robson_unity208

    robson_unity208

    Joined:
    Jul 14, 2020
    Posts:
    7
    Yeah I think that will be way more trouble than it's worth. I thought it would work if I split the water mesh into different objects but it did not.
    Thank anyway
     
  6. whitexroft

    whitexroft

    Joined:
    Oct 22, 2012
    Posts:
    48
    We can try another aproach :)
    Your water is z-fighting with lasers, because the lasers object and the water has about the same pivot position
    You can adjust waters sorting calculus by using Offset state
    https://docs.unity3d.com/Manual/SL-CullAndDepth.html

    Add "Offset -1, -1" next to your Blend operation in the water shader
    That should at least affect the way it behaves with cameras movement
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Offset will have no affect on this problem, because it's an issue of the draw order which is determined by the CPU, and the offset only affects the GPU. It's only useful for preventing z fighting against an opaque object. Doesn't help sorting between transparent objects at all.