The green “sphere” is really a flat card facing you. The fragment computes the sphere’s depth and writes it to gl_FragDepth, so the box intersects it correctly.
Drag to rotate
0.51.3
Exercise
Runs once per pixelDrag numbers, or edit the code
uniformmat4projectionMatrix;// Three.js declares it automatically only in the vertex shaderuniformfloatuRadius;uniformfloatuWriteDepth;invec2vUv;invec3vViewPos;outvec4fragColor;voidmain(){vec2p=vUv*2.0-1.0;floatr2=dot(p,p);if(r2>1.0)discard;// drop everything outside the circlevec3N=vec3(p,sqrt(1.0-r2));// pretend this is a sphere surfacevec3surface=vViewPos+vec3(0.0,0.0,N.z*uRadius);vec4clip=projectionMatrix*vec4(surface,1.0);floatsphereDepth=clip.z/clip.w*0.5+0.5;// NDC z -> 0..1 depth buffer valuegl_FragDepth=mix(gl_FragCoord.z,sphereDepth,uWriteDepth);floatlight=max(dot(N,normalize(vec3(-0.5,0.7,0.8))),0.0);fragColor=vec4(vec3(0.48,0.83,0.69)*(0.2+0.8*light),1.0);}
JS side
Rasterization gives every fragment a depth; writing gl_FragDepth replaces it. That is how a flat card can intersect a box like a real sphere.
Try it
Turn off “Write gl_FragDepth”: how do the box and the card intersect now?