Writing to the ZBuffer

Hey All,

I made a cutout shader that raytraces the intersection with a sphere and discards all fragments that lie within the sphere. In order to render a cap on the cutout part I check in the shader if a fragment is backfacing or not. If it is backfacing I calculate the normal at that point by intersecting a ray with the sphere and use that normal for shading. This works fine and gives the illusion of a clean boolean between my object and a sphere.

However, since the zbuffer only stores the original depth of the other side of the object inside the cutout part the cap doesn’t actually occlude geometry behind it. I would like to fix this by updating the zbuffer to the correct, calculated, depth value inside of the cutout. I thought I could simply do this by writing the distance from the camera to the intersection with the sphere. That doesn’t work though.

I also tried 1/depth and some stuff in the BufferDepth function in the code below. Any combination of these doesn’t make the object sort properly. Can anyone help me with this?

Shader "Custom/CutOut" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_Color ("Color", color) = (1,1,1,1)
		_Sphere ("Sphere", vector) = (1,1,1,1)
	}
	SubShader {
		Tags {"LightMode" = "ForwardBase"}
		LOD 200
		
		cull off
		
		Pass {
			CGPROGRAM

				#pragma exclude_renderers gles flash xbox360

				#pragma vertex vert
				#pragma fragment frag
				#include "UnityCG.cginc"
			
				struct v2f {
					float4 pos : SV_POSITION;
					float2 uv_MainTex : TEXCOORD0;
					float3 N : TEXCOORD1;
					float3 P : TEXCOORD2;
					float3 E : TEXCOORD3;
					float3 C : TEXCOORD4;
					float3 L : TEXCOORD5;
				};
			
				float4 _MainTex_ST;
				float4 _Sphere;
				
				float iSphere( float3 ro, float3 rd, float4 sph ) {
					float3 oc = ro - sph.xyz; // looks like we are going place sphere from an offset from ray origin, which is = camera
					float b = 2.0 * dot( oc, rd );
					float c = dot(oc, oc) - sph.w * sph.w; // w should be size
					float h = b*b - 4.0 *c;
					//if (h<0.0) { return -1.0; }
					float t = (-b + sqrt(h)) / 2.0;
					return t;
				}

				v2f vert(appdata_full v) {
					v2f o;
					o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
					o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.N = mul((float3x3)_Object2World, v.normal);
					o.P = mul((float3x3)_Object2World, v.vertex.xyz);
					o.E = WorldSpaceViewDir(v.vertex);
					o.C = _WorldSpaceCameraPos;
					o.L = float3(unity_4LightPosX0[0], unity_4LightPosY0[0], unity_4LightPosZ0[0]);//_WorldSpaceLightPos0;
					return o;
				}
			
				sampler2D _MainTex;
				
				float BufferDepth(float z) {
					// returns a zbuffer value (which is depth non-linearly encoded
					float zNear = _ProjectionParams.y;
					float zFar = _ProjectionParams.z;
					
					float a = zFar / ( zFar - zNear );
					float b = zFar * zNear / ( zNear - zFar );
					
					return a + b / z;
				}
				
				struct C2E2f_Output {
                    float4 col:COLOR;
                    float dep:smile:EPTH;
                };     
               
               C2E2f_Output frag(v2f IN) : COLOR{
               // float4 frag(v2f IN) : COLOR {
                	C2E2f_Output o;
                	
					float dCenter = length(IN.P-_Sphere.xyz);
					if(dCenter < _Sphere.w)
						discard;
					
					float3 N = normalize(IN.N);
					float3 L = IN.L-IN.P;
					float3 P = IN.P;
					
					half4 c = tex2D (_MainTex, IN.uv_MainTex);	
					if(dot(N, IN.E) < 0) {
						// fragment is backfacing, so render cap instead
						
						//Get intersection of ray from C through P with sphere
						float3 E = normalize(IN.P-IN.C); 
						float t = iSphere(IN.C, E, _Sphere);
						P = IN.C + t*E;
						N = normalize(_Sphere.xyz-P);
						L = IN.L-P;
					}
					float depth = length(P-IN.C);
					
					L = normalize(L);
					
					float lambert = dot(N, L);
					
					o.col = lambert;			// SHow diffuse lighting
					//o.col = float4(N, 1);		// Show normals
					//o.col = 1/depth;			// Show depth, there should be no sharp edge at the cut edges
					
					o.dep = BufferDepth(1/depth);
					
					return o;
					//return o.col;
				}
			ENDCG
		}
	}
}

BUMP.

ANyone? How do I write custom depths to the zbuffer?

The way you do now. You have to output the correct depths though. You can do that by replicating what the vertex shader does. You’re already in world space, so try this:

float4 projPos = mul(UNITY_MATRIX_VP, float4(P, 1.0));
o.dep = projPos.z / projPos.w;