2

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 Answer 1

2

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
         }
     }
 }
 } 
Sign up to request clarification or add additional context in comments.

5 Comments

Will this work with particle shaders as well? I know they behave a bit differently and their renderorder is much higher. The reason I ask is that one or more of the objects could have particle systems around their surface. Thanks!
@bicarbon8 I have edited the answer, the first shader is not intended for particles, I added the one for particles, in the second gif each parent sphere has a child glow particle rendered in front and as you said the render queue is higher or equal than 3000, intended for transparencies even though the spheres are opaque, it will get quite messy if you have a lot objects and particles at the same time in the scene but you could use some kind of pooling to reuse materials and objects and change properties at runtime
thanks @shaggun. Took me a bit of playing around in a sample scene to come to grips with what is really going on here. I really like this and marked your response as the answer as I think I can make this work in my scene though for Unity's Standard shader, but I don't see how I can update the Render Queue value (the example you show is the default custom surface shader). Have you any ideas on this? Thanks again!
after taking some time to port this into my game I'm finding that the change to the Standard shader causes my ship to either not render (when at Render Queue of 2000) or to be somewhat transparent (when set to a Render Queue high enough to display). Unfortunately it looks like I'll have to find some other approach... you don't happen to know anything about Shader Stencils by chance?
@bicarbon8 I've used stencils a couple of times before but I'm afraid another generic shader can mess up with your project settings again, however, from the videos you posted I think this could be helpful for you, it's about stencils

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.