Jackson Rushing
Jackson Rushing

Reputation: 97

Smoothly transition from orthographic projection to perspective projection?

I'm developing a game that consists of 2 stages, one of these has an orthographic projection, and the other stage has a perspective projection.

Currently when we go between modes we fade to black, and then come back in the new camera mode.

How would I go about smoothly transitioning between the two?

Upvotes: 8

Views: 4368

Answers (3)

Nathan Petrangelo
Nathan Petrangelo

Reputation: 11

I managed to do this without the explicit use of matrices. I used Java so the syntax is different but comparable. One of the things I used was this mix() function. It returns value1 when factor is 1 and value2 when factor is 0, and has a linear transition for every value in between.

private double mix(double value1, double value2, double factor)
{
    return (value1 * factor) + (value2 * (1 - factor));
}

When I call this function, I use value1 for perspective and value2 for orthographic, like so:mix(focalLength/voxel.z, orthoZoom, factor)

When determining your focal length and orthographic zoom factor, it is helpful to know that anything at distance focalLength/orthoZoom away from the camera will project to the same point throughout the transition.

Hope this helps. You can download my program to see how it looks at https://github.com/npetrangelo/3rd-Dimension/releases.

Upvotes: 1

Paul Houx
Paul Houx

Reputation: 2043

If you have access to a programmable pipeline (a.k.a. shaders), you can do the transition in the vertex shader. I have found that this works very well and does not introduce artifacts. Here's a GLSL code snippet:

#version 150

uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;

uniform float uNearClipPlane = 1.0;
uniform vec2  uPerspToOrtho = vec2( 0.0 );

in vec4 inPosition;

void main( void )
{    
    // Calculate view space position.
    vec4 view = uViewMatrix * uModelMatrix * inPosition;

    // Scale x&y to 'undo' perspective projection.
    view.x = mix( view.x, view.x * ( -view.z / uNearClipPlane ), uPerspToOrtho.x );
    view.y = mix( view.y, view.y * ( -view.z / uNearClipPlane ), uPerspToOrtho.y );

    // Output clip space coordinate.
    gl_Position = uProjectionMatrix * view;
}

In the code, uPerspToOrtho is a vec2 (e.g. a float2) that contains a value in the range [0..1]. When set to 0, your coordinates will use perspective projection (assuming your projection matrix is a perspective one). When set to 1, your coordinates will behave as if projected by an orthographic projection matrix. You can do this separately for the X- and Y-axes.

'uNearClipPlane' is the near plane distance, which is the value you used to create the perspective projection matrix.

When converting this to HLSL, you may need to use view.z instead of -view.z, but I could be wrong.

I hope you find this useful.

Edit: instead of passing in the near clip plane distance, you can also extract it from the projection matrix. For OpenGL, this is how:

float zNear = 2.0 * uProjectionMatrix[3][2] / ( 2.0 * uProjectionMatrix[2][2] - 2.0 );

Edit 2: you can optimize the code by doing the scaling on x and y at the same time:

view.xy = mix( view.xy, view.xy * ( -view.z / uNearClipPlane ), uPerspToOrtho.xy );

To get rid of the division, you could multiply by the inverse near plane distance:

uniform float uInvNearClipPlane; // = 1.0 / zNear

Upvotes: 2

Robert Rouhani
Robert Rouhani

Reputation: 14678

There are probably a handful of ways of accomplishing this, the two I found that seemed like they would work the best were:

  1. Lerping all the matrix elements from one matrix to the other. Apparently this works pretty well all things considered. I don't believe this transition will appear linear, though. You could try to give it an easing function instead of doing the interpolation linearly

  2. A dolly zoom on the perspective matrix going to/from a near 0 field of view. You would pop from the orthographic matrix to the near 0 perspective matrix and lerp the fov out to your target, and probably be heavily tweaking the near/far planes as you go. In reverse you would lerp to 0 and then pop to the orthographic matrix. The idea behind this being that things appear flatter with a lower fov and that a fov of 0 is essentially an orthographic projection. This is more complex but can also be tweaked a whole lot more.

Upvotes: 5

Related Questions