- Home /
(URP) Cameras: Create a custom projection matrix given a bounding rectangle.
Hi,
I'm looking to modify a camera's projection matrix to only render within a certain bounding rectangle.
The specific problem is, given 4 Vector3s which form a 'rectangle' in 3D space, and a camera's projection matrix, I want to compute a new projection matrix which only renders the camera within that bounding rectangle. This is for optimization and culling purposes.
Picture mockup to explain what I'm talking about. I'm using Universal Render Pipeline, 7.1.8.
Answer by markefus · Mar 20, 2020 at 02:43 AM
I can confirm that this C# script to modify the projection matrix to a specific screen-space rectangle still works. Tested in Unity 2019.3.06f with URP 7.1.8.
This sets the projection matrix to not render geometry outside of the rectangle. You can see this works in both the stats panel and the frame debugger.
Entire screen with no scissor, vs. scissor rectangle set to new Rect(0f, 0f, .25f, .25f);
(renders only the bottom left quadrant of the screen).
I've attached the code as a .txt file, and here's the original source.
Well the script does exactly what I told you ^^. It sets the viewport and adjusts the projection matrix to get the same view. As I said the projection matrix can not cull or prevent parts to not be rendered. It's the viewport that actually culls the rendered / rasterized geometry.
Note that your script has way too many commented out code which makes it really hard to read. You even create an additional matrix which isn't used at all (m1). Also I'm pretty sure m2 and m3 could be combined into a simpler modification of the original projection matrix. However I haven't looked through the math yet :).
Of course narrowing down the view frustum can help with frustum culling on the CPU side so certain object don't get drawn at all. Though that's something totally different from what a scissor test does. The scissor test happens on the GPU. Culling objects has to happen in the engine on the CPU side,
Answer by Bunny83 · Mar 20, 2020 at 12:18 AM
Note that changing the projection matrix does not change the area where your scene is drawn to on the screen. This is achieved by changing the viewport rect of the camera. Instead of the viewport rect you can set the pixelRect in screenspace coordinates instead of normalized coordinates. Once you set a view port rect, it depends on what kind of projection you are looking for. If you don't change anything at all your view would simply be inside the specified viewport rect just as if that rect is the whole screen.
If you want an off-center projection so that the actual view is based on the whole screen but only seen in the viewport, you need to adjust the projection matrix accordingly. You can refer to my projection matrix crash course over here. Depending on your needs you probably would calculate your left,right,top,bottom, near and far values yourself (don't forget the aspect ratio). At the end of my matrix crash course I have linked page that explains how to calculate those values based on a fov angle
Hey @Bunny83 thanks for your answer!
To specify any ambiguity, I am not looking for the equivalent of setting the viewport rectangle or pixel rectangle.
I am talking about modifying the camera's frustum, using the custom projection matrix, so that objects are not physically drawn outside of that rectangle. The closest equivalent I can think of is a hardware scissor rectangle, but I am not sure if Unity supports this.
I did find a thread from 2009 regarding emulating a scissor rect by modifying the projection matrix, but I need to test this script thoroughly.
I also need to set my own oblique projection matrix, and also account for stereoscopic projection in VR, so this could be a bit complex of a solution.
Do you know of any better, more recent, methods to support that emulation of a scissor rect?
If you're just looking for a scissor test, that's actually not achieved by changing the projection matrix. Unity doesn't provide any scissor testing since as far as I remember that was part of the fixed function pipeline of OpenGL. Since we have shaders such things are usually done in a shader. As you can even read in the description of the scissor test, that test is performed on a per fragment base. So the exact same geometry will be rendered. However each fragment is tested against the specified scissor rectangle before the fragment is written to the framebuffer. That's similar to depth testing.
A scissor rect can help reduce the fillrate if otherwise you have a lot of overdraw. However if your scene consists mainly of opaque geometry the effect would be $$anonymous$$imal.
If you actually change the viewport + adjust the projection matrix you actually get a different viewing frustum so Unity could discard objects before they are even rendered. So that approach could be much more effective than a simple scissor test.
I have never worked with stereoscopic projection since I don't even have any VR gear ^^. Since a stereoscopic view doesn't really have a "virtual screen" it would be difficult to specify that rectangle properly in terms of left / right eye.
Anyways your task would be to calculate the same projection for a smaller viewport rect. I'm not sure if it's worth to try to take the original projection matrices you already have and work backwards to deter$$anonymous$$e the original l,r,t,b,n,f values, reduce them to the wanted viewport rect and reconstruct the projection matrix from the new values. I'm still not sure what your intentions are and why you want to perform the scissor test.
ps: I just read that you said your defined your "rectangle" with 4 Vector3 values in 3d space. If that's really the case it wouldn't be a scissor test at all unless you always look straight onto that tectangle. As soon as you look at any angle the rectangle would turn into either a trapez-like shape in screen space or just an arbitrary quadrilateral. So what's the exact goal you want to achieve? Is this about portal rendering? If so I highly recomment you watch the Portal problems talk. If it actually is about portal rendering I don't really see the reason for scissors. If you use the stencil buffer to draw the inside of a portal you get the same as if you use a scissor test. Only that the stencil buffer allows for arbitrary shapes as stencil.
I think you abstracted your issue too much. It generally helps if you describe what you actually want to achieve.
Hi @Bunny83, thanks for your answer.
This isn't about showing only a certain rectangle on screen, (in which stencils would indeed be a perfectly viable solution), this was about culling the screen to a specific rectangle.
I can confirm that modifying the projection matrix works, and posted the relevant code!
Thanks again.
You might be looking for something like visportals. However in most cases it's not worth the hassle. $$anonymous$$y test project was just a proof of concept.
Hey,
I think this is similar to what I'm looking for?
However I am most interested in the culling component of the 'visportals' rather than the entire visportal tech itself.
The purpose of modifying the projection matrix like this, is to not render objects outside of that rectangle. Like I said, this is for culling and optimization purposes.
I'm basically looking for a scissor rect equivalent.
Your answer
Follow this Question
Related Questions
Raycast with multiple camera's in scene 0 Answers
Platformer - Pan camera when player moves near the edges? (c#) 0 Answers
capture photo with text 0 Answers
Multiple Cameras with Steam VR, Unity 5.4 3 Answers
"Warning: Display 1: No cameras rendering" when all cameras render to RenderTexture 1 Answer