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

Possible to Make Shader that Renders at Intersections with Another Mesh?

Discussion in 'Shaders' started by liquidgraph, Jun 17, 2011.

  1. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    I'm not sure if this is possible, but I figure it's worth asking. Is it possible to create a shader that will only render at points where it intersects with other geometry? See the image below. Basically, the grey box has a standard diffuse shader whereas the other box has this "special shader" that make it transparent except for the points where it intersects another mesh. Imagine that the black lines don't exist on the special shader, resulting in simply a grey box with patches of green.

     
    Last edited: Jun 17, 2011
  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I think its technically possible, but quite tricky, certainly out of my league. It would have to be an optical illusion though, perhaps with some alpha trickery as the intersection doesn't really exist unless you are doing a mesh boolean operation.
     
  3. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Yup, an optical illusion is what I'm looking for. I'd really appreciate it if someone could help me with this.
     
  4. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Bump. So am I to take the lack of replies as evidence of impossibility?
     
  5. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    The effect you illustrated is exactly how stencil shadow rendering works. Unfortunately it requires use of the stencil buffer, which is not exposed in Unity.
     
  6. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Hmmm... thanks for naming it. Would really love to see this possible in future editions of Unity.
     
  7. cerebrate

    cerebrate

    Joined:
    Jan 8, 2010
    Posts:
    261
    Last edited: Jun 22, 2011
  8. Slin

    Slin

    Joined:
    Jun 27, 2010
    Posts:
    85
    You can probably get the disired result by playing with depthtesting.
    Like rendering everything but your special block, then rendering your special blocks backfaces, only writing the depth buffer at depth values greater than the existing ones and then render it again, but with front faces, writing color and maybe also depth, but only at depth values smaller than the own one.
    Well I think it is lessequal for the first one and greater for the second one actually. But something like that should do it :).
     
  9. Slin

    Slin

    Joined:
    Jun 27, 2010
    Posts:
    85
    I am actually very sure that it is...
     
  10. Slin

    Slin

    Joined:
    Jun 27, 2010
    Posts:
    85
    Hm, I guess, that I will have to show you tomorrow that it works just fine through swapping the depth test function :p
     
  11. Slin

    Slin

    Joined:
    Jun 27, 2010
    Posts:
    85
    Ah, well, I guess you were right :p
    I still think that it is possible with depthtest only, but not without rendering both objects multiple times, like first the visible one, than the masking one and thn the visible one again or something like that. Which however doesn´t seem to be very comfortable to do with Unity.
    The best solution would of course be a stencil buffer, counting up on front faces, down on backfaces and ready is your mask...
     
  12. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Please elaborate. How would that be done exactly? Where would I start? I don't mind using RenderTexture if it gets the job done. I have Unity Pro. Also, would this work on iPhone?
     
  13. guitarstud101

    guitarstud101

    Joined:
    Sep 29, 2012
    Posts:
    2
    Reviving this here as I have a similar problem and haven't found a useful discussion elsewhere.

    I have managed to use a depth buffer technique (similar to Slin's suggestion) to perform such an intersection, but unfortunately it only works when both objects are convex.

    The algorithm is (using liquidgraph's language):
    1. Render Front faces of "Regular" to the depth buffer (ColorMask 0)
    2. Render Front faces of "Special" to the depth buffer (ColorMask 0) with ZTest Greater
    3. Render Back faces of "Regular" to the depth buffer (ColorMask 0)
    4. Render Front faces of "Special" to depth AND color buffers with ZTest Equal

    To get these steps in the correct order, I set the Render Queue to "Geometry+1", "Geometry+2", etc.

    Here's a screenshot of the result, using two cylinders for "Regular" and "Special":
    $cylinders.png
    (I've written another set of shaders similar to above to render the red portions)


    Problem is, this algorithm does NOT work for non-convex meshes. Here's another screen, this time using a "donut" shaped mesh for the "Special" object:
    $donut.png
    The hole in the donut gives artifacts that I can't seem to get rid of.


    Any suggestions for getting rid of these non-convex artifacts?

    I'd be happy to post an asset package of this scene if anyone is interested.
     
  14. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    The more general problem is known as CSG (constructive solid geometry). Google for GPU-based implementations of CSG and you will find a couple of articles about it. As pointed out, most GPU-based implementations will use the stencil buffer.
     
  15. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    349
    Heya, I'm interested in how you achieved the effect, any hints? Or could you upload your assets used for this? Would be awesome!
     
  16. baustin27

    baustin27

    Joined:
    Feb 21, 2014
    Posts:
    12
    Bump. Has anyone figured this trick out? Im trying to make a plane with a shader that is invisible but when a colored mesh intersects with the plane it takes the color and intersecting shape and renders it on to the plane.
     
  17. MBaadsgaard

    MBaadsgaard

    Joined:
    Mar 19, 2013
    Posts:
    39
    In case anyone else comes by and wants the red part of guitarstud101's shader:

    1. Render Front faces of "Special" to the depth buffer (ColorMask 0)
    2. Render Front faces of "Regular" to the depth buffer (ColorMask 0) with ZTest Greater
    3. Render Back faces of "Special" to the depth buffer (ColorMask 0)
    4. Render Front faces of "Special" to depth AND color buffers with ZTest Equal
     
    asdzxcv777 and kebrus like this.
  18. NicRule

    NicRule

    Joined:
    Jan 24, 2014
    Posts:
    4
    This does not answer the question but if you are having a similar problem it may help.
    This script was placed on a sphere centered on and parented to the camera with a radius of 100.
    It makes a line where the sphere intersects any other object.

    This script definitely works with Unity 5

    Code (CSharp):
    1. Shader "Custom/DistanceOutline"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Color", Color) = (1.0,1.0,1.0,1.0)//This is for the fallback
    6.     }
    7.     SubShader{
    8.         Tags {"Queue" = "Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
    9.         Blend SrcAlpha OneMinusSrcAlpha
    10.         ZWrite Off
    11.         Cull Off
    12.    
    13.         Pass{
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             #include "UnityCG.cginc"
    18.  
    19.             uniform sampler2D _CameraDepthTexture; //Depth Texture
    20.  
    21.             struct vertexOutput{
    22.                 float4 pos : SV_POSITION;
    23.                 float4 projPos : TEXCOORD1; //Screen position of pos
    24.             };
    25.  
    26.             vertexOutput vert(appdata_base v){
    27.                 vertexOutput o;            
    28.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    29.                 o.projPos = ComputeScreenPos(o.pos);
    30.                 return o;
    31.             }
    32.  
    33.             half4 frag(vertexOutput i) : COLOR{
    34.                 //Get the distance to the camera from the depth buffer for this point
    35.                 float sceneZ = LinearEyeDepth (tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r);
    36.                 //Actual distance to the camera        I know it's 100
    37.                 float partZ = i.projPos.z;
    38.                 //float partZ = 100;
    39.  
    40.                 sceneZ = (1-sceneZ/1000);
    41.                 sceneZ = sceneZ*10 - 8.7;
    42.                  
    43.                 fixed4 finalColor = float4(1 , 1 , 1 , sceneZ);
    44.                 return finalColor;
    45.             }
    46.             ENDCG
    47.         }
    48.     }
    49.     FallBack "Transparent"
    50. }
     
  19. swerit

    swerit

    Joined:
    Apr 11, 2015
    Posts:
    6
    I'm interested in how you achieved the effect,could you upload your assets used for this?
     
  20. Moralez360

    Moralez360

    Joined:
    Oct 3, 2013
    Posts:
    19
    Hi, I'm trying to get the red portions but I can not get it. Do not express the guitarstud101 code and there are bugs in the objects. Can anyone correct my shaders ?. Does anyone know how to script guitarstud101 ? @guitarstud101 has been lost !!.

    Special:

    Code (CSharp):
    1. Shader "Custom/Special" {
    2.  
    3.     SubShader {
    4.  
    5.         Pass
    6.         {
    7.             Tags {"Queue"="Geometry" }
    8.             Cull Front
    9.             //ZWrite On
    10.             ColorMask 0
    11.             ZTest Greater
    12.          
    13.             CGPROGRAM
    14.             #pragma vertex vert
    15.             #pragma fragment frag
    16.  
    17.             struct appdata {
    18.                 float4 vertex : POSITION;
    19.             };
    20.             struct v2f {
    21.                 float4 pos : SV_POSITION;
    22.             };
    23.             v2f vert(appdata v) {
    24.                 v2f o;
    25.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    26.                 return o;
    27.             }
    28.             half4 frag(v2f i) : SV_Target {
    29.                 return half4(1,1,0,1);
    30.             }
    31.  
    32.             ENDCG
    33.      
    34.         }
    35.         Pass
    36.         {
    37.             Tags {"Queue"="Geometry+1" }
    38.             cull Front
    39.             ztest Equal
    40.             ColorMask rgba
    41.          
    42.             CGPROGRAM
    43.             #pragma vertex vert
    44.             #pragma fragment frag
    45.  
    46.             struct appdata {
    47.                 float4 vertex : POSITION;
    48.             };
    49.             struct v2f {
    50.                 float4 pos : SV_POSITION;
    51.             };
    52.             v2f vert(appdata v) {
    53.                 v2f o;
    54.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    55.                 return o;
    56.             }
    57.             half4 frag(v2f i) : SV_Target {
    58.                 return half4(1,0,0,1);
    59.             }
    60.  
    61.             ENDCG
    62.      
    63.         }
    64.     }
    65.     FallBack "Diffuse"
    66. }
    67.  
    Regular:

    Code (CSharp):
    1. Shader "Custom/Regular" {
    2.  
    3.     SubShader {
    4.  
    5.         Pass
    6.         {
    7.             Tags {"Queue"="Geometry+2" }
    8.             Cull Front
    9.             //ZWrite On
    10.             ColorMask 0
    11.          
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.  
    16.             struct appdata {
    17.                 float4 vertex : POSITION;
    18.             };
    19.             struct v2f {
    20.                 float4 pos : SV_POSITION;
    21.             };
    22.             v2f vert(appdata v) {
    23.                 v2f o;
    24.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    25.                 return o;
    26.             }
    27.             half4 frag(v2f i) : SV_Target {
    28.                 return half4(0,1,0,1);
    29.             }
    30.  
    31.             ENDCG
    32.      
    33.         }
    34.         Pass
    35.         {
    36.             Tags {"Queue"="Geometry+3" }
    37.             cull back
    38.             //ztest Equal
    39.             ColorMask 0
    40.          
    41.             CGPROGRAM
    42.             #pragma vertex vert
    43.             #pragma fragment frag
    44.  
    45.             struct appdata {
    46.                 float4 vertex : POSITION;
    47.             };
    48.             struct v2f {
    49.                 float4 pos : SV_POSITION;
    50.             };
    51.             v2f vert(appdata v) {
    52.                 v2f o;
    53.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    54.                 return o;
    55.             }
    56.             half4 frag(v2f i) : SV_Target {
    57.                 return half4(0,0,1,1);
    58.             }
    59.  
    60.             ENDCG
    61.      
    62.         }
    63.     }
    64.     FallBack "Diffuse"
    65. }
    66.  
     
  21. CyanSlinky

    CyanSlinky

    Joined:
    Feb 27, 2014
    Posts:
    28
    anyone have a proper final written solution to this? that only renders the red faces of the images above? I've tried the code that @Moralez360 posted above, it kinda works but it also renders the geometry behind the intersection and not just the intersection itself, I've tried messing around with it and i cant figure out how to only render the intersections.
     
  22. blamepie

    blamepie

    Joined:
    Aug 27, 2015
    Posts:
    1
    Hey guys, any update on a final solution that only renders the red faces?
     
  23. Tudor

    Tudor

    Joined:
    Sep 27, 2012
    Posts:
    150
    You could probably get this to work if you could draw the backfaces to depth, only once within the current mesh. So if one triangle draws to depth, another triangle of the same mesh is not allowed to draw to depth any more. But there's no such functionality in unity. You'd need ZTest LEqual and also ZTestWithinThisMesh GEqual.

    Otherwise I don't think there's enough information in just the surface to derive the "red face" areas for non convex meshes.

    You need volume information about your object (ie donut). That could be voxels / 3D grid, point cloud, or a SDF function.

    If you are working with the specific case of a simple object like a donut, you can represent it with math (just google SDF donut shader). If not, then you can render your arbitrary non convex mesh into a depth thickness volume (you probably want to do this offline (before the game starts)), and store it into a 3D texture or Read Structured Buffer. At runtime when you render an object that intersects your non convex mesh, you look up the surface into the 3D Texture and see if it intersects.

    This isn't a walk in the park graphics programming wise. Not too hard but harder than just fiddling with the stencil buffer.
     
    Last edited: Mar 2, 2018