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

Question Culling on Transparent Sphere

Discussion in 'Shaders' started by luan22_unity, Mar 7, 2023.

  1. luan22_unity

    luan22_unity

    Joined:
    Feb 26, 2023
    Posts:
    4
    Hey guys, how's it going? So, I'm trying to have a transparent sphere with a equirectangular grid on it, which works fine using my NewSurfaceShader shader (yes, I'm very creative when it comes to naming things). However, I wanted to set culling off since I want to have a POV from that little astronaut, but the sphere bugs out when I set Cull Off, what am I missing here? Thanks in advance.
     

    Attached Files:

  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Fast, efficient, and correct transparency sorting is an unsolved problem for real time rendering.

    Generally for real time transparent rendering, the compromise we've been using for the last 30 years has been to sort transparent objects so that further away objects render first, and nearby objects render last. This is known as The Painter's Algorithm. With modern triangle counts and rendering pipelines, sorting per triangle is usually too slow, so it's usually limited to sorting per-object using the distance to the object's bounds.

    However since we're not doing per triangle sorting, when a single mesh is rendered by the GPU, each triangle in the mesh is rendered in the order it exists in the mesh data. If you're lucky and the triangle order is already roughly front to back for the direction you're looking at that mesh, then it'll look fine. If the triangle order something else, you'll see weird overlaps like what you're seeing.

    The solution for this kind of thing is usually to manually handle the sorting yourself. Not by sorting the triangles, but by rendering the sphere with two shader passes. One using
    Cull Front
    , and a second using
    Cull Back
    . You can then either have two separate materials which you set the render queue of to ensure the render order, or have both of these passes in one shader and Unity will (almost) always render those in the order they appear in the shader.
     
  3. luan22_unity

    luan22_unity

    Joined:
    Feb 26, 2023
    Posts:
    4
    Okay, thank you for the explanation! I'm fairly new to unity, so where exactly would I insert the passes in my code?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Short version: duplicate everything inside the
    SubShader
    after
    Tags {}
    .

    Like this:
    Code (CSharp):
    1. Shader "Custom/TwoSidedGrid"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Color", Color) = (1,1,1,1)
    6.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    7.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    8.         _Metallic ("Metallic", Range(0,1)) = 0.0
    9.     }
    10.     SubShader
    11.     {
    12.         Tags { "Queue"="Transparent" "RenderType"="Transparent" }
    13.         LOD 200
    14.         Cull Front
    15.         // Render the parts of the object facing us.
    16.         // If the object is convex, these will be closer than the
    17.         // back-faces.
    18.  
    19.         CGPROGRAM
    20.         // Physically based Standard lighting model, and enable shadows on all light types
    21.         #pragma surface surf Standard fullforwardshadows vertex:vert alpha:blend
    22.  
    23.         // Use shader model 3.0 target, to get nicer looking lighting
    24.         #pragma target 3.0
    25.  
    26.         sampler2D _MainTex;
    27.  
    28.         struct Input
    29.         {
    30.             float2 uv_MainTex;
    31.             float3 direction;
    32.         };
    33.  
    34.         void vert(inout appdata_full v, out Input o) {
    35.             UNITY_INITIALIZE_OUTPUT(Input, o);
    36.             o.direction = v.normal;
    37.             v.normal = -v.normal; // flip normal for backfaces
    38.         }
    39.  
    40.         half _Glossiness;
    41.         half _Metallic;
    42.         fixed4 _Color;
    43.  
    44.         void surf (Input IN, inout SurfaceOutputStandard o)
    45.         {
    46.             const float PI = 3.14159265359;
    47.             // Puff out the direction to compensate for interpolation.
    48.             float3 direction = normalize(IN.direction);
    49.  
    50.             // Get a longitude wrapping eastward from x-, in the range 0-1.
    51.             float longitude = 0.5 - atan2(direction.z, direction.x) / (2.0f * PI);
    52.             // Get a latitude wrapping northward from y-, in the range 0-1.
    53.             float latitude = 0.5 + asin(direction.y) / PI;
    54.  
    55.             // Combine these into our own sampling coordinate pair.
    56.             float2 customUV = float2(longitude, latitude);
    57.  
    58.             // Use them to sample our texture(s), instead of the defaults.
    59.             fixed4 c = tex2D (_MainTex, customUV) * _Color;
    60.  
    61.             // Albedo comes from a texture tinted by color
    62.             o.Albedo = c.rgba;
    63.             // Metallic and smoothness come from slider variables
    64.             o.Metallic = _Metallic;
    65.             o.Smoothness = _Glossiness;
    66.             o.Alpha = c.a;
    67.         }
    68.         ENDCG
    69.  
    70.         Cull Back
    71.         // Render the parts of the object facing us.
    72.         // If the object is convex, these will be closer than the
    73.         // back-faces.
    74.  
    75.         CGPROGRAM
    76.         // Physically based Standard lighting model, and enable shadows on all light types
    77.         #pragma surface surf Standard fullforwardshadows vertex:vert alpha:blend
    78.  
    79.         // Use shader model 3.0 target, to get nicer looking lighting
    80.         #pragma target 3.0
    81.  
    82.         sampler2D _MainTex;
    83.  
    84.         struct Input
    85.         {
    86.             float2 uv_MainTex;
    87.             float3 direction;
    88.         };
    89.  
    90.         void vert(inout appdata_full v, out Input o) {
    91.             UNITY_INITIALIZE_OUTPUT(Input, o);
    92.             o.direction = v.normal;
    93.         }
    94.  
    95.         half _Glossiness;
    96.         half _Metallic;
    97.         fixed4 _Color;
    98.  
    99.         void surf (Input IN, inout SurfaceOutputStandard o)
    100.         {
    101.             const float PI = 3.14159265359;
    102.             // Puff out the direction to compensate for interpolation.
    103.             float3 direction = normalize(IN.direction);
    104.  
    105.             // Get a longitude wrapping eastward from x-, in the range 0-1.
    106.             float longitude = 0.5 - atan2(direction.z, direction.x) / (2.0f * PI);
    107.             // Get a latitude wrapping northward from y-, in the range 0-1.
    108.             float latitude = 0.5 + asin(direction.y) / PI;
    109.  
    110.             // Combine these into our own sampling coordinate pair.
    111.             float2 customUV = float2(longitude, latitude);
    112.  
    113.             // Use them to sample our texture(s), instead of the defaults.
    114.             fixed4 c = tex2D (_MainTex, customUV) * _Color;
    115.  
    116.             // Albedo comes from a texture tinted by color
    117.             o.Albedo = c.rgba;
    118.             // Metallic and smoothness come from slider variables
    119.             o.Metallic = _Metallic;
    120.             o.Smoothness = _Glossiness;
    121.             o.Alpha = c.a;
    122.         }
    123.         ENDCG
    124.     }
    125.     FallBack "Diffuse"
    126. }