internal void ClearStencil(ScriptableRenderContext context, Material nonSharedStencilMaterial, StencilTestOptions stencilTestOptions) { if (m_stencilTestState != StencilTestState.None) { if ((stencilTestOptions & StencilTestOptions.ClearStencil) == StencilTestOptions.ClearStencil) { CommandBuffer commandBuffer = CommandBufferPool.Get(); if (m_stencilTestState == StencilTestState.BackfaceOnly) { if ((stencilTestOptions & StencilTestOptions.PreventOverwriting) == 0) { commandBuffer.DrawMesh(GetFrustumMesh(), transform.localToWorldMatrix, nonSharedStencilMaterial, 0, 1); } else { ClearFullscreenStencil(commandBuffer); StencilMaskAllocator.Init(StencilMaskAllocator.availableBits); // reset stencil allocator } } else { commandBuffer.DrawMesh(GetFrustumMesh(), transform.localToWorldMatrix, nonSharedStencilMaterial, 0, 3); } context.ExecuteCommandBuffer(commandBuffer); CommandBufferPool.Release(commandBuffer); } else { // allocate new stencil bit for the next projector StencilMaskAllocator.AllocateSingleBit(); } m_stencilTestState = StencilTestState.None; } }
private void Draw(DrawState state, Vector2 scale, byte clipDepth) { Element parent = this.parent; Matrix matrix; GraphicsDevice device = null; if (parent == null) { if (state.DrawTarget.MultiSampleType != MultiSampleType.None) { device = state.BeginGetGraphicsDevice(StateFlag.None); device.RenderState.MultiSampleAntiAlias = false; } this.clipTestActive = false; DeviceRenderState rstate = new DeviceRenderState(); rstate.DepthColourCull.DepthWriteEnabled = false; rstate.DepthColourCull.DepthTestEnabled = false; state.PushRenderState(ref rstate); if (camera == null) { camera = state.UserValues[cameraID] as Xen.Camera.Camera2D; if (camera == null) { camera = new Xen.Camera.Camera2D(true); state.UserValues[cameraID] = camera; } } state.PushCamera(camera); } else { this.clipTestActive = parent.clipTestActive | parent.ClipsChildren; } StencilTestState stencilState = new StencilTestState(); if (clipTestActive) { stencilState.Enabled = true; stencilState.ReferenceValue = clipDepth; stencilState.StencilFunction = CompareFunction.Equal; stencilState.StencilPassOperation = StencilOperation.Keep; } bool clearStencil = false; if (this.ClipsChildren) { clearStencil = clipDepth == 255; clipDepth--; if (!clipTestActive) { //check there actually is a stencil buffer #if DEBUG DepthFormat format = state.DrawTarget.SurfaceDepthFormat ?? DepthFormat.Unknown; if (format != DepthFormat.Depth24Stencil8) { throw new InvalidOperationException("ElementRect.ClipChildren requires the DrawTarget has a valid Depth Buffer with an 8bit Stencil Buffer"); } #endif stencilState.Enabled = true; stencilState.ReferenceValue = clipDepth; stencilState.StencilPassOperation = StencilOperation.Replace; } else { stencilState.StencilPassOperation = StencilOperation.Decrement; } } if ((scale.X != 0 && scale.Y != 0)) { Vector2 size = ElementSize; GetDisplayMatrix(out matrix, scale, ref size); state.PushWorldMatrixMultiply(ref matrix); BindShader(state, false); state.RenderState.AlphaBlend = blend; state.RenderState.StencilTest = stencilState; if (!UseSize) { size = new Vector2(1, 1); } else if (IsNormalised) { size *= scale; } PreDraw(size); DrawElement(state); List <Element> children = Children; if (children != null) { foreach (Element child in children) { if (((IDraw)child).CullTest(state)) { child.Draw(state, size, clipDepth); } } } if (clearStencil) { BindShader(state, true); stencilState = new StencilTestState(); stencilState.Enabled = true; stencilState.StencilFunction = CompareFunction.Never; stencilState.StencilFailOperation = StencilOperation.Zero; state.RenderState.StencilTest = stencilState; DrawElement(state); } state.PopWorldMatrix(); } if (parent == null) { state.PopRenderState(); state.PopCamera(); } if (device != null) { device.RenderState.MultiSampleAntiAlias = true; state.EndGetGraphicsDevice(); } }
internal void WriteFrustumStencil(ScriptableRenderContext context, Camera camera, Material nonSharedStencilMaterial) { int stencilMask = StencilMaskAllocator.GetCurrentBit(); if (stencilMask == 0) { m_stencilTestState = StencilTestState.None; Debug.LogError("Couldn't use stencil test. No stencil bits available. Please change Stencil Mask value in Projector Renderer Feature."); return; } // check if the frustum intersect with camera near plane. Vector3 cameraLocalPos = transform.InverseTransformPoint(camera.transform.position); Vector3 nearClipRect = camera.nearClipPlane * 2.0f * Mathf.Tan(Mathf.Deg2Rad * camera.fieldOfView) * new Vector3(camera.aspect, 1.0f, 0.0f); nearClipRect.z = camera.nearClipPlane; Matrix4x4 localToCamera = camera.worldToCameraMatrix * transform.localToWorldMatrix; bool clipped = true; for (int i = 0; i < 6; ++i) { int face = i >> 1; int n = i & 1; int axis1 = (face + 1 + n) % 3; int axis2 = (face + 2 - n) % 3; int vtx1 = n * (1 << face); int vtx2 = vtx1 + (1 << axis1); int vtx3 = vtx1 + (1 << axis2); Plane frustumPlane = new Plane(GetProjectorFrustumVertex(vtx1), GetProjectorFrustumVertex(vtx2), GetProjectorFrustumVertex(vtx3)); Vector3 offset = Vector3.Scale(nearClipRect, localToCamera.MultiplyVector(frustumPlane.normal)); if (Mathf.Abs(offset.x) + Mathf.Abs(offset.y) + Mathf.Max(0, offset.z) < frustumPlane.GetDistanceToPoint(cameraLocalPos)) { clipped = false; break; } } CommandBuffer commandBuffer = CommandBufferPool.Get(); if (StencilMaskAllocator.loopFlag) { StencilMaskAllocator.ClearLoopFlag(); ClearFullscreenStencil(commandBuffer); } nonSharedStencilMaterial.SetFloat(s_shaderPropIdStencilRef, stencilMask); nonSharedStencilMaterial.SetFloat(s_shaderPropIdStencilMask, stencilMask); Mesh frustumMesh = GetFrustumMesh(); if (clipped) { m_stencilTestState = StencilTestState.BackfaceOnly; commandBuffer.DrawMesh(frustumMesh, transform.localToWorldMatrix, nonSharedStencilMaterial, 0, 2); } else { m_stencilTestState = StencilTestState.BothSide; commandBuffer.DrawMesh(frustumMesh, transform.localToWorldMatrix, nonSharedStencilMaterial, 0, 0); commandBuffer.DrawMesh(frustumMesh, transform.localToWorldMatrix, nonSharedStencilMaterial, 0, 1); } context.ExecuteCommandBuffer(commandBuffer); CommandBufferPool.Release(commandBuffer); }