Simple Cross Section Shader

just created something very simple but useful - a cross section shader.

its simply fill up the back face with solid color and assign the x-axis-up normal on those faces :slight_smile:

[ sample video ]

[ webplayer ]
http://homepage.mac.com/antonioh/unity3d/build/Cross_Section_v004a.html


(the framework of the building and the wall of the room has been cut out by the shader)

is it useful to you too ??

2 Likes

That is a cool effect. A few people did something similar a few months ago.

Thanks for the link Daniel ! I just learnt how to use Shader.SetGlobalMatrix() from there :slight_smile:

Also, we seem to use the same method, except I just made everthing inside the shader. (because, I am lazy :p)

Here is the test code of mine version… (diffuse shading only at the moment)

// Fast Edition - Single Pass

Shader "cross_section_v003"
{
	Properties 
	{
		section_depth ("section depth (x, y, z, depth)", vector) = (0,0,0,0.15)
		section_color ("section color", color) = (0.5,0.1, 0.1, 1)
		
		color_map ("color map", 2D) = "white" {}
		
	}	
	SubShader 
	{	
		Pass
		{			
			CULL OFF
			
CGPROGRAM //--------------
#pragma vertex 	 vertex_shader
#pragma fragment fragment_shader

#include "UnityCG.cginc"

uniform float4 section_depth;
uniform float4 section_color;
uniform sampler2D color_map;	

float4x4 rotate(float3 r) 
{ 
	float3 c, s; 
	sincos(r.x, s.x, c.x); 
	sincos(r.y, s.y, c.y); 
	sincos(r.z, s.z, c.z);
	return float4x4( c.y*c.z,	 -s.z,     s.y, 0, 
						 s.z, c.x*c.z,    -s.x, 0, 
						-s.y,     s.x, c.x*c.y, 0, 
						   0,       0,       0, 1 );
} 

struct a2v 
{
	float4 vertex	: POSITION;
	float4 color	: COLOR;
	float2 texcoord : TEXCOORD;
	float3 normal	: NORMAL;
};

struct v2f
{
	float4 position : POSITION;
	float2 texcoord : TEXCOORD0;								
	float3 normal	: TEXCOORD1; 
	float4 vertex	: TEXCOORD2;
	float4 mask		: TEXCOORD3;				
};
				
v2f vertex_shader( a2v IN )
{													
	v2f OUT;
				
	float4x4 r 	 = rotate(radians(section_depth.xyz) +_SinTime.xyz);
	float4 c 	 = float4(IN.vertex.xyz,1);
	
	OUT.mask 	 = mul(r, c);
	OUT.position = mul(glstate.matrix.mvp, IN.vertex);
	OUT.texcoord = IN.texcoord;
	OUT.normal   = IN.normal;
	OUT.vertex	 = IN.vertex;
	
	return OUT;
}
							
void fragment_shader(	v2f IN,
						out float4 finalcolor : COLOR)	
						
{
	if(IN.mask.x > section_depth.w)
		discard;
		
	float3 obj_view_dir = IN.vertex.xyz - _ObjectSpaceCameraPos; 
	float fd = dot(obj_view_dir, IN.normal);
	float3 N = (fd > 0) ? float3(1,0,0) : IN.normal;
		
	N = mul(glstate.matrix.invtrans.modelview[0], float4(N, 1));				
	float diffuse = saturate(dot(glstate.light[0].position, N));
	
	finalcolor = float4(0,0,0,1);								
	if (fd > 0)
	{
		finalcolor.xyz = section_color *(diffuse *0.6 +0.4);
		return;
	}				
	finalcolor.xyz = tex2D(color_map, IN.texcoord).xyz *(diffuse *0.6 +0.4);
}
ENDCG //--------------

		} // Pass
	} // SubShader
} // Shader

(Remark: the " +_SinTime.xyz" could be deleted. That’s just for the demo)

EDIT: The old attachment “cross_section_v004.shader” has been replaced by the following one.

r *= float4x4(1,-1,-1, 0, -1, 1,-1, 0, -1,-1, 1, 0, 0, 0, 0, 1 );

(In the “004a” version, one more line is added onto it. since, the “section_depth.xyz” need to be inverted before assign to the cap’s normal.)

224108–8095–$cross_section_v004a_337.shader (3.64 KB)

1 Like

Cool shader, thanks for sharing it. How are you hollowing out the spheres in the video?

Simply model it, a small flipped-face sphere inside a bigger sphere :slight_smile:

In fact, you bring out a very good idea, why not add a “thicker” function onto the shader too. so that, I don’t need to touch the modeling program anymore. (… save up some time to enjoy my coffee :p)

Many Thanks !

In my version I add a simple lighting equation to the cut surface too, that way it doesn’t always have to be constant shaded. Nice that you managed to get all the clipping code into the shader though, very handy for clipping different objects with different planes.

Surely, I will add this in my code later :slight_smile:

In fact, I still looking for the way to work with intersecting closed objects, by making a real cap :stuck_out_tongue:

I have this now…

(2 spheres, left with “cross section” shader and right with diffuse shader)

I am working on this method now… re-assign the depth value, but not success when the angle of the clipping plane is changed :s

in vertex_shader…

float4 c 	 = float4(IN.vertex.xyz,1);
OUT.vpos 	 = mul(glstate.matrix.modelview[0], c);

in fragment_shader…

float3 N = IN.normal;
float z = IN.vpos.z;

if (IN.vpos.z > section_depth.w)
{	
	N = float3(1,0,0);
	z = section_depth.w;
}

float near = _ProjectionParams.y;
float far  = _ProjectionParams.z;

float a = -far/(far-near);
float b = -far*near/(far-near);	
depth 	= (a*z+b)/(-z);

Any new idea ??

I guess you’d actually need to project the x,y coordinate onto the clipping plane to get the depth and then test if the object depth was behind.

This is a very interesting approach, I really need to solve the intersection problem to make this useful for what I want and what you are suggesting here is interesting. The question is how do you determine what is “inside” so as to only test the depth for the bits that should be capped? When 2 objects intersect you can’t rely on the normals any more.

The attached shader doesn’t appear to work in Unity 3.1 - is it still under development?

because that poor guy still using unity 2.6 to do the development… in fact, the development will be resumed very soon :slight_smile:

Hello Apple_motion, your work is very interesting.

Did you updated the shader for Unity 3.3?

Hi Apple_Motion,

I second the request for a Unity 3.3 update! Anyone else interested in this?

El Diablo

bump! Unity 3.3 update, willing to pay!! Apple motion, where are you?

Yes bump here too??

Q

bump

bump

Bump

I wanted to do the same thing and I believe the problem was because Unity 3 use Surface shader (so things are handled in another way …)

The instruction “clip” can be used in the surface function . For instance, for a plane which the equation is a.x +b.y + c.z + d = 0 :

Shader "Custom/ClipShader" 
{
   Properties 
   {
      _MainTex ("Texture", 2D) = "white" {}
      _a ("_a",Float) = 0.0
      _b ("_b",Float) = 0.0
      _c ("_c",Float) = 0.0
      _d ("_d",Float) = 0.0
      _clip ("_clip",Float) = 0
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      Cull Off
      CGPROGRAM
      #pragma surface surf Lambert
      struct Input {
          float2 uv_MainTex;
          float3 worldPos;
      };
      
      sampler2D _MainTex;
      float _a,_b,_c,_d,_clip;

      void surf (Input IN, inout SurfaceOutput o) 
      {
          if(_clip == 1)
          {
         	 clip (_a *IN.worldPos.x +
         	 	_b *IN.worldPos.y +
         		_c *IN.worldPos.z +
         		_d > 0 ? -1 :1);
         }

          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

In a Unity script; you can activate the clipping plan using Shader.SetGlobalFloat(“_clip”,1); and move the plan along its normal using Shader.SetGlobalFloat(“_d”,d); However it seems that you must unexpose the variables from the property block if you want a Shader.SetGlobal function to work.

You can of course do a script to compute _a _b _c _d parameters according to a plan (gameobject) inside the scene to let the user create easily a clipping plane with a desired orientation;

You can rewrite other defaut Unity shaders (bump, alpha, etc…) , you’ll juste have to add some properties and the clip instruction.

Hope it will help you :slight_smile: ,
Tom

Solmyr, I’m just starting to dive into learning the shader language so this is still over my head. Are you saying that the shader code you provided is a surface shader that replaces the vertex/fragment shader AppleMotion posted earlier in this thread? I’m learning the shader language (just started reading the book “The Cg Tutorial” this morning) for the specific purpose of creating the type of shader AppleMotion demonstrated in the gray sphere example, where you can define a plane that intersects an object and the shader will clip a portion of the object and fill it with a color (though I would prefer a texture). I’ve posted here and to other threads numerous times asking AppleMotion to update his shader and private messaged him a few times as well, offering to pay him but he never ever replies to anyone’s requests or messages, so I decided to just learn and do it on my own lol.

No, it’s not an update because it doesn’t work the same way.
It just uses the “clip(-1)” function in the surface shader to exclude pixel that will be on one side of the plane for objects that use this shader so it creates a hole in the object.

I havent code the part to fill the intersected zone with a color/texture. I don’t know how to do it but you can however easily change the color of a small part of the original object surface wich is near the clipping plane.

void surf (Input IN, inout SurfaceOutput o) 
{
     float dist = _a *IN.worldPos.x +_b *IN.worldPos.y +_c *IN.worldPos.z +_d;
      if(_clip == 1  abs(dist) < 0.02)[COLOR="yellowgreen"]// fill the near surface in yellow[/COLOR]
      {
		fixed4 c = fixed4(1,1,0,1); 
		o.Albedo = c.rgb;
		o.Alpha = c.a;
     }
     else if(_clip == 1  dist < -0.02)  [COLOR="yellowgreen"]// exclude pixel outside the plane[/COLOR]
     {
    	 clip(-1);
     }
     else[COLOR="yellowgreen"] // normal diffuse display[/COLOR]
     {
		fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; 
		o.Albedo = c.rgb;
		o.Alpha = c.a;
     }
}