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

Smoothly transition from orthographic projection to perspective projection?

本文关键字:过渡到 透视投影 平滑 投影      更新时间:2023-10-16

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

目前,当我们在两种模式之间切换时,我们会变黑,然后回到新的相机模式。

如何在两者之间顺利过渡?

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

  1. 将所有矩阵元素从一个矩阵排列到另一个矩阵。显然,从各个方面考虑,这都很有效。不过,我不相信这种转变会出现线性。你可以试着给它一个放松函数,而不是线性插值

  2. 在接近0的视场上缩放透视矩阵。你会从正交矩阵弹出到近0透视矩阵,并将中心线指向你的目标,并且可能会在前进的过程中大幅调整近/远平面。相反,您会将lerp设置为0,然后弹出到正交矩阵。这背后的想法是,事物看起来更平坦,中心凹较低,中心凹为0本质上是正交投影。这更复杂,但也可以进行更多的调整。

如果您可以访问可编程管道(也称为着色器),则可以在顶点着色器中进行转换。我发现这非常有效,并且不会引入工件。以下是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是一个vec2(例如float2),它包含一个在[0..1]范围内的值。当设置为0时,您的坐标将使用透视投影(假设您的投影矩阵是透视矩阵)。当设置为1时,坐标将表现为由正交投影矩阵投影。您可以分别对X轴和Y轴执行此操作。

"uNearClipPlane"是近平面距离,它是用于创建透视投影矩阵的值。

当将其转换为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

我在没有显式使用矩阵的情况下做到了这一点。我使用了Java,所以语法不同,但具有可比性。我使用的东西之一就是这个mix()函数。当factor为1时,它返回value1,当factor为0时返回value2,并且对于其间的每个值都有线性转换。

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

当我调用这个函数时,我使用值1表示透视,使用值2表示正交,如下所示:mix(focalLength/voxel.z, orthoZoom, factor)

在确定焦距和正交缩放因子时,知道在整个过渡过程中,距离相机focalLength/orthoZoom距离处的任何东西都将投影到同一点是很有帮助的。

希望这能有所帮助。你可以下载我的程序,看看它看起来怎么样https://github.com/npetrangelo/3rd-Dimension/releases.