how to programmatically allow Unity Shader to control which object renders in front?

Viewed 3473

I've only just started learning Unity, but because I come from a background of coding in C#, I've found the standard scripting to be very quick to learn. Unfortunately, I've now come across a problem for which I believe a custom shader is required and I'm completely lost when it comes to shaders.

Scenario: I'm using a custom distance scaling process so that really big, far away objects are moved within a reasonable floating point precision range from the player. This works great and handles scaling of the objects based on their adjusted distance so they appear to actually be really far away. The problem occurs though when two of these objects pass close to eachother in game space (this would still be millions of units apart in real scale) because they visibly collide. Ex: https://www.youtube.com/watch?v=KFnuQg4R8NQ

Attempted Solution 1: I've looked into flattening the objects along the player's view axis and this fixes the collision, but this affects shading and particle effects so wasn't a good option

Attempted Solution 2: I've tried changing the RenderOrder, but because sometimes one object is inside the mesh of another (though the centre of this object is still closer to the camera) it doesn't fix the issue and particle effects are problematic again.

Attempted Solution 3: I've tried moving the colliding objects to their own layer, spawning a new camera with a higher depth at the same position as my main camera and forcing the cameras to only see the items on their respective layers, but this caused lighting issues as some objects are lighting others and I had only a limited number of layers so this solution was quite limiting as it forced me to only have a low number of objects that could be overlapping at a time. NOTE: this solution is probably the closest I was able to come to what I need though. Ex: https://www.youtube.com/watch?v=CyFDgimJ2-8

Attempted Solution 4: I've tried updating the Standard shader code by downloading it from Unity's downloads page and creating my own, custom shader that allows me to modify the ZWrite and ZTest properties, but because I've no real understanding of how these work, I'm not getting anywhere.

Request: I would greatly appreciate a Shader script code example of how I can programmatically force one object who's mesh is either colliding with or completely inside another mesh to render in front of said mesh. I'm hoping I can then take that example and apply it to all the shaders that I'm currently using (Standard, Particle Additive) to achieve the effect I'm looking for. Thanks in advance for your help.

1 Answers

In the gif below both objects are colliding and according to the camera position the cube is in front of the sphere but I can change their visibility with the render queue:

enter image description here

If that's what you want you only have to add ZWrite Off in your subshader before the CGPROGRAM starts, the following is the Standard Surface Shader including the line:

Shader "Custom/Shader" {
Properties {
    _Color ("Color", Color) = (1,1,1,1)
    _MainTex ("Albedo (RGB)", 2D) = "white" {}
    _Glossiness ("Smoothness", Range(0,1)) = 0.5
    _Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 200
    ZWrite Off 
    CGPROGRAM
    // Physically based Standard lighting model, and enable shadows on all light types
    #pragma surface surf Standard fullforwardshadows

    // Use shader model 3.0 target, to get nicer looking lighting
    #pragma target 3.0

    sampler2D _MainTex;

    struct Input {
        float2 uv_MainTex;
    };

    half _Glossiness;
    half _Metallic;
    fixed4 _Color;

    // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    // #pragma instancing_options assumeuniformscaling
    UNITY_INSTANCING_BUFFER_START(Props)
        // put more per-instance properties here
    UNITY_INSTANCING_BUFFER_END(Props)

    void surf (Input IN, inout SurfaceOutputStandard o) {
        // Albedo comes from a texture tinted by color
        fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
        o.Albedo = c.rgb;
        // Metallic and smoothness come from slider variables
        o.Metallic = _Metallic;
        o.Smoothness = _Glossiness;
        o.Alpha = c.a;
    }
    ENDCG
}
FallBack "Diffuse"
}

Now sorting particles, look at the shadows and how they collide and how we can change their visibility regardless of their position.

enter image description here

Here's the shader for particles, I'm using the Unity Built-in shader, the only thing added is Ztest Always

     Shader "Particles/Alpha Blended Premultiply Custom" {
 Properties {
     _MainTex ("Particle Texture", 2D) = "white" {}
     _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
 }

 Category {
     Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
     ZTest Always
     Blend SrcAlpha OneMinusSrcAlpha
     ColorMask RGB
     Cull Off Lighting Off ZWrite Off

     SubShader {
         Pass {

             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #pragma target 2.0
             #pragma multi_compile_particles
             #pragma multi_compile_fog

             #include "UnityCG.cginc"

             sampler2D _MainTex;
             fixed4 _TintColor;

             struct appdata_t {
                 float4 vertex : POSITION;
                 fixed4 color : COLOR;
                 float2 texcoord : TEXCOORD0;
                 UNITY_VERTEX_INPUT_INSTANCE_ID
             };

             struct v2f {
                 float4 vertex : SV_POSITION;
                 fixed4 color : COLOR;
                 float2 texcoord : TEXCOORD0;
                 #ifdef SOFTPARTICLES_ON
                 float4 projPos : TEXCOORD1;
                 #endif
                 UNITY_VERTEX_OUTPUT_STEREO
             };

             float4 _MainTex_ST;

             v2f vert (appdata_t v)
             {
                 v2f o;
                 UNITY_SETUP_INSTANCE_ID(v);
                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                 o.vertex = UnityObjectToClipPos(v.vertex);
                 #ifdef SOFTPARTICLES_ON
                 o.projPos = ComputeScreenPos (o.vertex);
                 COMPUTE_EYEDEPTH(o.projPos.z);
                 #endif
                 o.color = v.color;
                 o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
                 return o;
             }

             UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
             float _InvFade;

             fixed4 frag (v2f i) : SV_Target
             {
                 #ifdef SOFTPARTICLES_ON
                 float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
                 float partZ = i.projPos.z;
                 float fade = saturate (_InvFade * (sceneZ-partZ));
                 i.color.a *= fade;
                 #endif

                 return i.color * tex2D(_MainTex, i.texcoord) * i.color.a;
             }
             ENDCG
         }
     }
 }
 } 
Related