- Home /
Asymmetric view frusta / selective region rendering
There's another question on this, but it seems to be locked down so I can't investigate/confirm. Does anyone have a good technique for telling the render pipeline to only bother with a certain subset of a viewport? Not changing the viewport rect, or changing the field of view, but literally saying "make like you're going to render this whole region, but really, I'm only going to use these pixels of it, so don't bother with the rest"? E.g., due to view frustum geometry, if you're rendering say, a 60-degree FoV to a 100x100 rectangle, there's a difference between the image you get if you take the rightmost 25 columns of that render vs setting up a new camera that's 15 degrees wide and pointing it 22.5 degrees right to render a 25x100 image. I want to get the former. I guess an equivalent technical question is how to make an asymmetric view frustum equivalent to a particular off-center wedge of a standard view frustum. Without having my CG theory fresh enough in my head to know offhand how to construct the right replacement view matrix manually, I'm wondering if anyone here has tried similar effects or has any pointers.
Answer by sean · Sep 12, 2011 at 07:48 PM
http://unity3d.com/support/documentation/ScriptReference/Camera-projectionMatrix.html has, surprise surprise, an example of kinda-close-to-pretty-much the effect I was going for. Building my own off-axis frustum of a part of the screen does not appear to line up precisely with the stock matrix of a fullscreen camera, but I can build two cameras with custom disjoint adjacent off-axis frusta and have them align seamlessly. Just take the provided off-axis projection matrix builder (included here in case the unity docs ever change)
// Set an off-center projection, where perspective's vanishing // point is not necessarily in the center of the screen. // // left/right/top/bottom define near plane size, i.e. // how offset are corners of camera's near plane. // Tweak the values and you can see camera's frustum change.
@script ExecuteInEditMode
var left : float = -0.2; var right : float = 0.2; var top : float = 0.2; var bottom : float = -0.2;
function LateUpdate () { var cam : Camera = camera; var m : Matrix4x4 = PerspectiveOffCenter( left, right, bottom, top, cam.nearClipPlane, cam.farClipPlane ); cam.projectionMatrix = m; }
static function PerspectiveOffCenter( left : float, right : float, bottom : float, top : float, near : float, far : float ) : Matrix4x4 { var x : float = (2.0 near) / (right - left); var y : float = (2.0 near) / (top - bottom); var a : float = (right + left) / (right - left); var b : float = (top + bottom) / (top - bottom); var c : float = -(far + near) / (far - near); var d : float = -(2.0 far near) / (far - near); var e : float = -1.0;
var m : Matrix4x4; m[0,0] = x; m[0,1] = 0; m[0,2] = a; m[0,3] = 0; m[1,0] = 0; m[1,1] = y; m[1,2] = b; m[1,3] = 0; m[2,0] = 0; m[2,1] = 0; m[2,2] = c; m[2,3] = d; m[3,0] = 0; m[3,1] = 0; m[3,2] = e; m[3,3] = 0; return m; }
and patch in your own code to grab the camera's default near/far/aspect/FoV and do up the trig for where to place the top/bottom/left/right.
Edit: actually, this works ~perfectly~ when I remember that aspect ratio multiplies the linear viewport measurement rather than the angular field of view facepalm
Answer by sean · Sep 12, 2011 at 02:48 PM
If I promote this answer later, that'll mean it's working for me, but for now it's just a lead- http://forums.create.msdn.com/forums/p/7821/41594.aspx is answering the same question, and I'm pretty sure I understand the math the thread concludes with:
asymmetricFrustumMatrix = perspectiveProjectionMatrix *dot* fustrumAlteredMatrix
where fustrumAlteredMatrix = |1 0 A 0| |0 1 B 0| |0 0 1 0| |0 0 0 1|
A = xRight + xleft / -2znear (= ((xRight+xLeft)/2)/(-zNear) = frustumCenterXAtZNear/-zNear) B = yTop + yBottom / -2znear (= ((yTop+yBottom)/2)/(-zNear) = frustumCenterYAtZNear/-zNear)
Basically saying that on the x and y axes, take the existing perspective coordinate and set it to itself+f(z) where f(z) is just the per-unit-z distance off-center of the desired skew axis at the nearplane (draw it out on paper as triangles; it makes sense). Iirc, you can retrieve the camera's projection matrix in Unity and set it manually, so it should be possible to let Unity do the projection and then put the matrix, skew it, and drop it back in.
Edit: This theory does hold some water. It's a little finnicky to get the numbers right, but it should be good for some distortion effects. It does not, however, do precisely what I want.
Edit 2: Upon further tweaking, the skew approach also works perfectly, it's just a little mind-bendy to work out how much to skew by for horizontal vs vertical offset regions.
Your answer
Follow this Question
Related Questions
Particles only rendering in Sceneview from an angle and never in game 1 Answer
How can I know if a gameObject is 'seen' by a particular camera? 11 Answers
If I script something to render, can it be frustrum culled? 2 Answers
Screen position out of view frustum error? 2 Answers
How to draw an outline of a camera view along a path ? 0 Answers