Hi, I'm working on a game where I want to only render geometry that sits inside the confines of 6 sided box, somewhat like a fishtank. Any geometry outside the tank should be invisible. I will be using Unity Pro with Windows. The effect I want is like the example on the right in this image :- http://imgur.com/bWaDXJp The objects that might intersect the tank it are animated, so I need to do this at runtime. Maybe I could do this by checking all geometry for an intersection every frame. But presumably it could be very slow. Is it possible to do something like this using a shader? I'm not asking for a full solution, just suggestions that I could research. Thanks for any suggestions.
Create a box. Add script to it. In the script: Send "world to object" matrix to shader. In the vertex shader: Transform vertex position from object space to world, then from world to the space from sended in previous step matrix. "Send" resulting position to fragment shader. In fragment shader: Discard fragment if any component of position(absolut value from any axis) greater then 0.5. --------- Here it's almost what you want. Code (csharp): usingUnityEngine; usingSystem.Collections; [ExecuteInEditMode] publicclass SetDecalMatr : MonoBehaviour { publicTransform proj; void Update() { SetMatr(); } [ContextMenu("set matrix")] void SetMatr() { Matrix4x4 mat = proj.worldToLocalMatrix*transform.localToWorldMatrix; renderer.sharedMaterial.SetMatrix("_DecalMatr", mat); } } Code (csharp): Shader "ProjectedDecal" { Properties { _MainTex ("Texture Image", 2D)="white"{} _DecalTex ("DecalTex Image", 2D)="white"{} } SubShader { Pass { CGPROGRAM #pragma only_renderers d3d9 gles #pragma glsl #pragma vertex vert #pragma fragment frag uniformsampler2D _MainTex; uniformsampler2D _DecalTex; uniform float4x4 _DecalMatr; struct vertexInput { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct vertexOutput { float4 pos : SV_POSITION; float3 pTex : TEXCOORD1; float2 tex : TEXCOORD0; }; vertexOutput vert(vertexInput input) { vertexOutput output; float4 trasformed = mul(_DecalMatr,input.vertex); output.pTex= trasformed.xyz+float3(0.5, 0.5, 0); output.tex=input.texcoord; output.pos= mul(UNITY_MATRIX_MVP,input.vertex); returnoutput; } float4 frag(vertexOutput input): COLOR { float4 pc = tex2D(_DecalTex, float2(input.pTex)); if(input.pTex.z<-0.5 ||input.pTex.z>0.5) { pc.a=0; } float4 c = tex2D(_MainTex,input.tex); return lerp(c, pc, pc.a); } ENDCG } } }
You could do something like this: - Draw the geometry that goes into the box normally. - Draw the box with no color or z writes, no backface culling and a xor stencil. (So the stencil buffer is 1 if the geometry is inside the box and 0 if outside the box.) - Put a background color in where the stencil buffer is 0. You could also draw a square at "infinite" distance without z check where the stencil buffer is 0. That way you'd reset the z buffer and you can continue drawing other objects into the scene. Just rejecting pixels in the pixel shader is easier if you only need a few shaders for the objects inside the box. Using the stencil buffer gives you the freedom to pretty much use any existing shader on the object inside the box. (As long as they do a z write.)
if you can use the same shader on all objects visible in the tank, you can just do... if pixel worldspace min max is in box, alpha = alpha, else alpha = 0. will make everything outside box transparent.
Transparent, but still writing to the z buffer if not rejected. I'd prefer to use the clip instruction (in the pixel shader.) Which can work on a vector. Code (csharp): clip(pos-min); clip(max-pos); Where pos, min and max are float3's.
Could you explain this a bit more, the XOR stencil? Is the background color where the stencil buffer is 0 what would create the effect of only seeing the geometry in the box? If I draw a quad without z check where the stencil buffer is 0, would that be drawn in something like OnPostRender for example?
The exclusive or method is described very shortly here: http://en.wikipedia.org/wiki/Shadow_volume#Exclusive-or What it comes down to is that you flip the stencil buffer when you go into the volume and flip it again when you go out. If you "hit" any geometry while inside the volume, this will leave the stencil buffer valued at 1. In code: Code (csharp): Stencil { ReadMask 1 WriteMask 1 Comp always Pass invert } Then you can draw a plane very far away where the geometry is outside the volume to reset the color and depth: Code (csharp): ZTest Always Stencil { Ref 1 Comp notEqual } Yes, the rest will be cleared. No, you do that after the fishtank and before the rest of the rendering: Opaque-3: Fishtank objects Opaque-2: Stencil volume Opaque-1: Clearing plane Opaque: Main rendering