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

Obscuring Specific 3D Objects with Specific 3D Mask Objects

Discussion in 'Shaders' started by JD_Designer, Apr 25, 2017.

  1. JD_Designer

    JD_Designer

    Joined:
    Sep 1, 2015
    Posts:
    111
    Hello!

    I've been trying to achieve an effect using depth mask shaders and render queue ordering: http://answers.unity3d.com/questions/316064/can-i-obscure-an-object-using-an-invisible-object.html (but in C#). Though I've been able to successfully create obscured and mask objects, I haven't been able to take it a step further to achieve my effect. The best way I can explain the desired effect is with a visual (bellow).

    So I've got two 3D objects that I want to obscure. I want to use two separate mask 3D objects for each object.
    Image_One.jpg

    I get into trouble when I try to align my objects as desired. The below image illustrates the setup that I'm striving for.
    Image_Two.jpg

    The problem is that when I align the objects as illustrated, Mask Object 1 obscures the remainder of the blue sphere, and Mask Object 2 obscures the remainder of the red sphere = no visible spheres.

    So I'm looking for a solution that allows specific mask objects to obscure specific objects. Eg - Mask object 1 obscures the red sphere but not the blue sphere and vice versa. I've tried experimenting with render queue values/rendering order without luck. Does anyone know how to achieve this type of selective masking? Many thanks!

    -Jeff
     
  2. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    972
    clipMaskTest.png
    You can achieve this with a clipping mask shader.
    This is the example shader code.

    Code (CSharp):
    1. Shader "Test/ClippingMask" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    5.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    6.         _Metallic ("Metallic", Range(0,1)) = 0.0
    7.  
    8.         _ClippingCentre ("Clipping Centre", Vector) = (0,0,0,0)
    9.         _Plane ("Plane", Vector) = (1,0.7,0.7,0)
    10.         //_offset ("offset",  Range(-1.5,1.5)) = 1
    11.         [Toggle] _invert("invert", Float) = 0
    12.     }
    13.     SubShader {
    14.         Tags { "RenderType"="Opaque" "Queue" = "Transparent"}
    15.         LOD 200
    16.         Cull off
    17.         CGPROGRAM
    18.         // Physically based Standard lighting model, and enable shadows on all light types
    19.         #pragma surface surf Standard fullforwardshadows
    20.  
    21.         // Use shader model 3.0 target, to get nicer looking lighting
    22.         #pragma target 3.0
    23.  
    24.         sampler2D _MainTex;
    25.  
    26.         struct Input {
    27.             float2 uv_MainTex;
    28.             float3 worldPos;
    29.         };
    30.  
    31.         half _Glossiness;
    32.         half _Metallic;
    33.         fixed4 _Color;
    34.         float _invert;
    35.         uniform float _offset;
    36.  
    37.         uniform float3 _ClippingCentre;
    38.         uniform float3 _Plane;
    39.  
    40.         // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    41.         // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    42.         // #pragma instancing_options assumeuniformscaling
    43.         UNITY_INSTANCING_CBUFFER_START(Props)
    44.             // put more per-instance properties here
    45.         UNITY_INSTANCING_CBUFFER_END
    46.  
    47.         void surf (Input IN, inout SurfaceOutputStandard o) {
    48.  
    49.             if((_offset - dot((IN.worldPos - _ClippingCentre),_Plane))*(1-2*_invert)<0) discard;
    50.  
    51.             // Albedo comes from a texture tinted by color
    52.             fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    53.             o.Albedo = c.rgb;
    54.             // Metallic and smoothness come from slider variables
    55.             o.Metallic = _Metallic;
    56.             o.Smoothness = _Glossiness;
    57.             o.Alpha = c.a;
    58.         }
    59.         ENDCG
    60.     }
    61.     FallBack "Diffuse"
    62. }
    Then put two materials on a single submesh Gameobject. Set invert on on one of them.

    Then place a slider and a simple script to manage the offset value:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SetShaderOffset : MonoBehaviour {
    6.  
    7.     public void SetOffset(float val)
    8.     {
    9.         Shader.SetGlobalFloat("_offset", val);
    10.     }
    11. }
     
  3. JD_Designer

    JD_Designer

    Joined:
    Sep 1, 2015
    Posts:
    111
    Interesting. Thank you for taking the time and effort to post this code! This may be one approach but I'm not sure if it's the best one.

    Basically the reason I wanted to use 3D geometry as a mask to obscure objects is so I can animate the obscured objects - imagine the spheres in my illustration emerging from nothingness and then traveling in opposite directions along the x axis. I'm basically trying to create a kaleidoscope effect.

    In the actual application, I plan on having many 3D objects emerging from a center plane that will be traveling in opposite directions. If I can use this effect to make one half of each sphere's material invisible and then animate the offset as they move away from each other that could work. The problem is that I would have to do this for hundreds of objects and this could get cumbersome. Thoughts? Thanks again for your reply, I will save this code.
     
  4. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    972
    If that is a case - and I understand you well - you do not need to animate the offset.
    It should be enough to animate your 3D object positions.
    Set both the _ClippingCentre and _Plane variables as global shader variables, like _offset is global at the moment.
    If the plane is supposed to stay in the middle set its normal to (1,0,0) and then set _invert on the materials on objects moving from left to right to on, and on the materials on objects moving from right to left to off.
     
    JD_Designer likes this.
  5. JD_Designer

    JD_Designer

    Joined:
    Sep 1, 2015
    Posts:
    111
    Thank you. I will give this a try and let you know how it goes! :)
     
  6. jonosmars

    jonosmars

    Joined:
    Dec 12, 2018
    Posts:
    5
    Hi, I know this post is from a long time ago, but I'm new to unity would very much like to replicate this effect.

    I have some screen grabs attached to show how I've gone about it trying to recreate the above.

    However it doesn't seem to have any effect? could any one let me know which part I have set up incorrectly? many thanks
     

    Attached Files:

  7. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    972
    Possibly you have to move this sphere to 0,0,0 or set the _ClippingCentre to position of the sphere.
     
    kro11 and jonosmars like this.
  8. jonosmars

    jonosmars

    Joined:
    Dec 12, 2018
    Posts:
    5
    great it works :)