如何平滑过渡从正交投影到透视投影?

7

我正在开发一个由两个阶段组成的游戏,其中一个阶段采用正交投影,另一个阶段采用透视投影。

目前,当我们在两种模式之间切换时,会淡入黑色,然后以新的摄像机模式重新出现。

我该如何平滑地过渡到这两种模式之间?


1
我只是猜测,但如果P1(x)是一个投影中的坐标,而P2(x)是另一个投影中的坐标,那么你可以通过平滑地从0到1改变f来获得平滑的过渡f P1(x) + (1-f) P2(x) - 463035818_is_not_a_number
这个问题很有趣且清晰,但是从写作风格来看,它似乎暗示了你想让别人替你完成工作。你应该增加更多细节,如正交矩阵和透视变换矩阵等。正如其他评论所提到的,如果你的正交投影是4x4矩阵,而透视矩阵也是如此,你可以尝试两者的线性组合。 - Jared Updike
3个回答

5

可能有几种方法可以实现这一点,我发现似乎最好的两种方法是:

  1. 从一个矩阵到另一个矩阵中所有元素进行线性插值。考虑到所有因素,这个方法似乎相当不错。但我认为这个过渡不会呈现出线性效果,你可以尝试使用缓动函数来代替线性插值。

  2. 在透视矩阵上进行dolly zoom,从近0视场跳回正交矩阵,将视野拉出到目标位置,并在过程中大量调整近/远平面。反向操作是将视野插值为0,然后跳回正交矩阵。这个想法的背后是物体在较低的视野下看起来更扁平,而视野为0本质上是一个正交投影。这种方法更加复杂,但也可以进行更多的调整。


太好了,谢谢!我曾考虑过“德利变焦”,但我没有想到可以在透视视图中使用0视野来“复制”正交视图。因此,理论上,我只需要调整相机位置并将其设置为0视野即可重新创建正交场景,然后插值视野和相机位置即可。 - Jackson Rushing
从理论上讲没问题。有很多Unity特定的结果,但是理论或数学基础是相同的 - http://answers.unity3d.com/questions/58306/transition-a-camera-from-ortographic-to-perspectiv.html - Robert Rouhani
天真地线性插值确实无法产生任何视觉上令人满意的结果。似乎将指数衰减函数应用于插值因子会有很大帮助,例如 proj = lerp(ortho, perspective, 100_000 ** -factor) - undefined

2
如果您有可编程管线(即着色器)的访问权限,您可以在顶点着色器中进行转换。我发现这样做效果非常好,不会引入伪影。以下是GLSL代码片段:
#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;
}

在代码中,uPerspToOrtho是一个包含在[0..1]范围内的值的vec2(例如float2)。当设置为0时,您的坐标将使用透视投影(假设您的投影矩阵是透视矩阵)。当设置为1时,您的坐标将表现得像是由正交投影矩阵投影而成。您可以为X轴和Y轴单独执行此操作。 NearClipPlane是近剪裁面距离,这是您用于创建透视投影矩阵的值。
在将其转换为HLSL时,您可能需要使用view.z而不是-view.z,但我可能是错的。
希望你会发现这很有用。
编辑:您还可以从投影矩阵中提取近剪裁面距离,对于OpenGL,如下所示:
float zNear = 2.0 * uProjectionMatrix[3][2] / ( 2.0 * uProjectionMatrix[2][2] - 2.0 );

编辑2:您可以通过同时对x和y进行缩放来优化代码:
view.xy = mix( view.xy, view.xy * ( -view.z / uNearClipPlane ), uPerspToOrtho.xy );

为了消除这个分割,你可以乘以近平面距离的倒数:
uniform float uInvNearClipPlane; // = 1.0 / zNear

1
我成功地完成了这个任务,而没有显式地使用矩阵。我使用的是Java语言,因此语法不同但可比较。我使用了mix()函数之一。当factor为1时,它返回value1;当factor为0时,它返回value2,并且对于介于两者之间的每个值都有线性过渡。请保留html标签。
private double mix(double value1, double value2, double factor)
{
    return (value1 * factor) + (value2 * (1 - factor));
}

当我调用这个函数时,我使用value1作为透视和value2作为正交参数,如下所示:mix(focalLength/voxel.z, orthoZoom, factor) 在确定焦距和正交缩放因子时,有一个有用的提示:任何距离相机focalLength/orthoZoom的地方,在整个转换期间都将投影到同一点。
希望这可以帮助您。您可以下载我的程序查看其效果https://github.com/npetrangelo/3rd-Dimension/releases

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接