After working for about 1,5 years with XNA I’m now trying to learn Unity. Great experience so far! Really powerful engine/editor combo.
Some things in Unity aren’t very obvious though. As a starter, the GUI system is really too simple and I find it hard to create effective custom controls. Converting an old XNA UI control has been a pain in the behind and it still isn’t working like I want. Drawing a simple 2D line (with LineRenderer) was quite the job for an Unity-newb like me.
Anyway, here’s my question:
I want to use a scissor rectangle that cuts off everything outside of a certain rectangle. I want my custom control to be able to draw everywhere (this includes 2D GUI drawing), but only use the top 300px of the screen of what’s drawn, and ‘cut-off’ the rest. What would be the best way to accomplish this?
I am having trouble getting this to work when the camera needs to have a varying near clip plane. (Otherwise, it works very well.) Solution?
(The Editor is showing the near clip plane updating perfectly in the Scene View. the Game View doesn’t reflect that, however.)
To be clear, I need one camera to match another in all respects, except only for a scissored portion of the first’s viewport rectangle, and with a different near clip plane. This camera to be matched is “tot.camera”, here, and it does not take up the full height of the screen.
I’ll post it here in case anyone else has some ideas ready at hand. Thank you so much for having a look. I don’t understand camera matrices well yet, so any schooling is appreciated.
In this “Scene” The relationship of the “red camera” to the “red circle” mimics the relationship of the “blue camera” to the “blue circle”.
The “background camera” displays (in green) the area above and below the other cameras. It illustrates that the rect of the “red camera” does not go past the boundaries of the “blue camera” It renders at the depth of -100.
As it is saved, the scene begins with the red camera disabled. Enabling it (with RedCamera.useProjectionMatrix enabled) displays how the projection matrix of the blue camera is mimicked, albeit in a smaller viewport rectangle. However, you can see how the near clip plane of the red camera, shown in the Editor, is not accurate, because the blue cube (that which should not be seen!) is visible in the Game View. Turning RedCamera.useProjectionMatrix off fixes the near clip issue, but of course, the projection matrix is all wrong then.
Also, if you have any advice about how to avoid the pixel or two of error on the “red camera” rect, please let me know.
The expected result is that the red camera sees the red circle as the blue camera sees the blue circle. But along with requiring a “scissor rectangle”, the red camera needs its near clip plane adjusted so that it doesn’t see anything closer to it than the red circle. (If you move or rotate the blue camera around, the red camera is updated automatically.)
Ideally, I wouldn’t be using the near clip plane for this anyway. Using the near clip method, there is an area of space near the red circle, closer to the red camera, in which the camera can see things. However, I don’t know if or how the ideal method could be accomplished.
(I handwrote this fast, sorry. The captions below are “red circle”, "ideal “frustum” ", “near clip method frustum”, and “red camera”.)
That’s fantastic, thank you. I will look at it today.
However, I need both that and a scissor rectangle. The red camera is not permitted to see anything outside of the bounds of the red circle. That is much more important than the oblique projection.
Just use the original script I attached and add it to “red camera”. You can set the scissor rect either via the scissorRect property or if you want to call the script manually, then you can call Scissor.SetScissorRect( , );
If you need it to match the bounds of the red circle, then get the world coordinates of the top left/right, and bottom left/right and use Camera.WorldToViewportPoint() to get the custom scissor rect coords.
That’s already happening in the RedCamera script. You can see it illustrated by the solid color that the red camera renders as its background after enabling the camera. The problem, again, is that your code doesn’t seem to account for near clip plane changing. I wasn’t aware that the clipping planes were “built in” to the camera’s projection matrix, but that would seem the case, from Aras’s page.
That’s orthogonality at work If you want to set an oblique near clip plane, you would call CalculateObliqueMatrix() from Aras’ page first on the camera. If you want a scissor rect, you would call SetScissorRect after. Both modify the projection matrix, however, the concepts are separate.
I guess before I get into this, I want to be sure:
Is there any way to render a camera on screen in another type of quad than a rectangle? (I know how to block the view with “Colormask 0” triangles on the near clip plane, but I’d prefer a cleaner method.)
I want to be able to match the camera’s viewport to a rectangular area in world space (like a window). In perspective, the rectangle can end up looking like a trapezium. I’d prefer for the camera’s viewport to match that, instead of being a screen-aligned rectangle that fully contains it.
You can think of it like Portal, but I can’t use render textures because this is for the iPhone.
It might be easier to render what’s behind the window/portal first using an oblique near plane and then render the window container at a later stage (which would provide the trapezium mask that you are looking for). A stencil buffer would do the trick in a single pass, however, we don’t have that support in Unity.
I suppose there are some other tricks you could try like prepping the depth buffer.
I’m having success with oblique projections these days, and I had been having success with the script from the first page of this thread. However, as soon as I made the camera that the scissor camera is based on have an assymetical normalized viewport rectangle, it stopped working well.
In that script, this is used:
cam.rect = new Rect (0,0,1,1);
cam.ResetProjectionMatrix ();
However, I don’t want the scissor camera to be based on a full-screen rendering. I want it based on a camera that actually needs to render. Is it simple to make the relevant code more robust, to account for this?
I’m not entirely sure, however, you could try just setting the rect to the portion of the screen that you want and then calling reset. I forget why I did that to begin with. Either it will blow up in your face or maybe point you in the right direction, if not just work outright
Well, in your case, you’re basing the scissor rect on a full screen rendering. That is, the field of view parameter that you give to the camera will be used, but relate to the top and bottom of the physical screen. Then, a portion of that rendering is used.
I don’t need to call reset, because I already have another camera that “renders normally” based on the Unity Editor’s parameters. But it only uses the lower 90% of the screen’s vertical space.
Although your code has worked perfectly for me, when used as you wrote it, it doesn’t work for all normalized viewport rectangles. It’s really designed to work with 0-1 in both axes, and I don’t understand it well enough yet, to know how to modify the code to account for alternatives.
I think my workaround for the time being will be to render the main camera using a stored scissor rect. Both it and the second camera will be based off the same full-screen potential render, so it should work perfectly. The problem is that the field of view will be greater below the camera than above it, given my main camera’s viewport rect of (0-1 in x; 0-.9 in Y). If you ever come up with something that would allow for basing the scissor rects on alternatives to 0-1, I would definitely appreciate if you could share that. I’m sure it’s possible, but I don’t yet know how to modify the matrix calculations you wrote to account for it.