根据视平面计算透视投影矩阵
Calculating the perspective projection matrix according to the view plane
我正在使用 openGL,但这基本上是一道数学题。
我正在尝试计算投影矩阵,我在视图平面 R(x,y,z) 和该平面的法向量 N(n1,n2,n3) 上有一个点。
我也知道眼睛位于 (0,0,0),我猜用技术术语来说它是透视参考点。
我怎样才能从这些数据中得出透视投影?我知道如何以常规方式获取 FOV、宽高比以及近平面和远平面。
投影矩阵必须在(0,0,0)
并且在Z+
或Z-
方向观察
这是必须的 因为 OpenGL 中的很多东西都依赖于它,比如 FOG、光照...所以如果你的方向或位置不同,那么你需要将它移动到相机矩阵。假设您所说的焦点是 (0,0,0)
,而法向量是 (0,0,+/-1)
Z附近
是焦点到投影平面的距离,所以znear
是平面的垂直距离,(0,0,0)
。如果假设是正确的那么
znear=R.z
否则你需要计算它。我想你已经得到了你需要的一切
- 从
R
投线,方向 N
- 找到离焦点最近的点
(0,0,0)
- 然后z near就是那个点到
R
的距离
远
由深度缓冲位宽和z near决定
zfar=znear*(1<<(cDepthBits-1))
这是最大可用 zfar
(出于我的目的)如果你需要更高的精度然后降低一点不要忘记精度在 znear
附近更高而在 [=25 附近更差=]. zfar
通常设置为最大视距,znear
从中计算或设置为最小对焦范围。
视角
我主要使用 60 度视图。 zang=60.0
[度]
我所在地区的普通男性可以看到高达 90 度的视野,但这是包括 60 度视野在内的周边视野,观看起来更舒适。
女性的视野有点开阔……但我从来没有听到她们对 60 度视角有任何抱怨,所以让我们假设这对她们来说也很舒服……
看点
宽高比由您的 OpenGL window 尺寸决定 xs,ys
aspect=(xs/ys)
我是这样设置投影矩阵的:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(zang/aspect,aspect,znear,zfar);
// gluPerspective has inacurate tangens so correct perspective matrix like this:
double perspective[16];
glGetDoublev(GL_PROJECTION_MATRIX,perspective);
perspective[ 0]= 1.0/tan(0.5*zang*deg);
perspective[ 5]=aspect/tan(0.5*zang*deg);
glLoadMatrixd(perspective);
deg = M_PI/180.0
perspective
是投影矩阵副本我用它来进行鼠标位置转换等...
如果您不校正矩阵,那么在使用诸如重叠更多平截头体以获得高精度深度范围之类的高级方法时,您将无法使用。我用它来获得 <0.1m,1000AU>
具有 24 位深度缓冲区的平截头体,不准确会导致图像无法完美匹配 ...
[备注]
如果焦点不是真正的 (0,0,0)
或者您没有在 Z 轴上查看(就像您没有相机矩阵而是使用投影矩阵那样)那么基本 scenes/techniques 你不会有问题。他们从使用高级图形开始。如果你使用 GLSL 那么你可以毫无问题地处理这个问题,但是固定的 OpenGL 函数不能正确处理这个问题。这也叫PROJECTION_MATRIX abuse
[edit1]几个links
如果您的视图是标准截锥体,那么您可以自己编写矩阵 gluPerspective otherwise look here Projections 了解如何构建它的一些想法
[edit2]
从你的评论来看,我是这样看的:
f
是你的视点(坐标轴是全球坐标轴)
f'
是观察点,如果 R
是屏幕的中心
因此为 f'
位置创建投影矩阵(如上所述),创建变换矩阵以将 f'
变换为 f
。转换后的 f
必须具有与 f'
中相同的 Z 轴,其他轴可以通过叉积获得并将其用作相机或一起乘以 booth 并用作滥用的投影矩阵
我之前的评论
中的了解变换矩阵link中解释了如何构建矩阵
我认为您将这个问题放在 "opengl" 标签下造成了一些混乱。问题在于,在计算机图形学中,术语 projection 不能从严格的数学意义上理解。
在数学中,投影被定义为(以下不是确切的数学定义,只是我自己的解释)当应用两次时不会进一步改变结果。想想看。当您将 3d space 中的一个点投影到 2d 平面(仍在那个 3d space 中)时,每个点的投影最终都会落在该平面上。但是已经在该平面上的点不再移动,因此您可以根据需要多次应用它,而无需进一步改变结果。
计算机图形学中的经典 "projection" 矩阵不会这样做。他们 从 转换 space 的方式是将一般的平截头体映射到立方体(或长方体)。为此,您基本上需要所有参数来描述视锥体,通常是纵横比、视野角、到近平面和远平面的距离,以及投影方向和中心点(后两者通常是隐含的)按惯例定义)。对于一般情况,还有水平和垂直不对称分量(可以把它想象成投影仪的 "lens shift")。所有这些都是计算机图形学中典型的投影矩阵所代表的。
根据您提供的参数构建这样的矩阵是不可能的,因为您缺少很多参数。另外 - 我认为这有点揭示 - 你已经给出了一个 视图平面 。但是到目前为止讨论的投影矩阵没有定义一个视图平面 - 任何平行于近平面或远平面并且在相机前面的平面都可以想象为观察平面(在相机后面也可以,但是图像会被镜像), 如果你需要的话。但从严格意义上讲,如果所有投影点也将最终落在该平面上,它只会是 "view plane"——计算机图形透视矩阵明确不会这样做。相反,它保留了它们的 3d 距离信息——这也意味着操作是可逆的,而经典数学投影通常不是。
综上所述,我只是猜测您正在寻找的是从 3D space 到 2D 平面的透视投影,而不是用于计算机图形的透视变换。您需要的所有参数只是视点和平面。请注意,这 正是 您给出的内容:投影中心应为原点,R
和 N
定义平面。
这样的投影也可以用4x4的齐次矩阵表示。您的问题中没有定义一件事:法线的方向。我再次假设标准数学约定并假设视图平面定义为 <N,x> + d = 0
。通过在该等式中使用 R
,我们可以得到 d = -N_x*R_x - N_y*R_y - N_z*R_z
。所以投影矩阵就是
( 1 0 0 0 )
( 0 1 0 0 )
( 0 0 1 0 )
(-N_x/d -N_y/d -N_z/d 0 )
这个矩阵有几个属性。有一个零列,所以它是不可逆的。另请注意,对于应用此方法的每个点 (s*x, s*y, s*z, 1)
,无论 s
是什么,结果(当然除以结果 w
后)都是相同的 - 所以每个点原点和 (x,y,z)
之间的一条线将导致相同的投影点 - 这就是透视投影应该做的。最后注意 w=(N_x*x + N_y*y + N_z*z)/-d
,因此对于满足上述平面方程的每个点,都会得到 w= -d/-d = 1
。结合其他维度的恒等变换,就是说这样一个点是不变的。
我正在使用 openGL,但这基本上是一道数学题。 我正在尝试计算投影矩阵,我在视图平面 R(x,y,z) 和该平面的法向量 N(n1,n2,n3) 上有一个点。 我也知道眼睛位于 (0,0,0),我猜用技术术语来说它是透视参考点。 我怎样才能从这些数据中得出透视投影?我知道如何以常规方式获取 FOV、宽高比以及近平面和远平面。
投影矩阵必须在
(0,0,0)
并且在Z+
或Z-
方向观察这是必须的 因为 OpenGL 中的很多东西都依赖于它,比如 FOG、光照...所以如果你的方向或位置不同,那么你需要将它移动到相机矩阵。假设您所说的焦点是
(0,0,0)
,而法向量是(0,0,+/-1)
Z附近
是焦点到投影平面的距离,所以
znear
是平面的垂直距离,(0,0,0)
。如果假设是正确的那么znear=R.z
否则你需要计算它。我想你已经得到了你需要的一切
- 从
R
投线,方向N
- 找到离焦点最近的点
(0,0,0)
- 然后z near就是那个点到
R
的距离
- 从
远
由深度缓冲位宽和z near决定
zfar=znear*(1<<(cDepthBits-1))
这是最大可用
zfar
(出于我的目的)如果你需要更高的精度然后降低一点不要忘记精度在znear
附近更高而在 [=25 附近更差=].zfar
通常设置为最大视距,znear
从中计算或设置为最小对焦范围。视角
我主要使用 60 度视图。
zang=60.0
[度]我所在地区的普通男性可以看到高达 90 度的视野,但这是包括 60 度视野在内的周边视野,观看起来更舒适。
女性的视野有点开阔……但我从来没有听到她们对 60 度视角有任何抱怨,所以让我们假设这对她们来说也很舒服……
看点
宽高比由您的 OpenGL window 尺寸决定
xs,ys
aspect=(xs/ys)
我是这样设置投影矩阵的:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(zang/aspect,aspect,znear,zfar);
// gluPerspective has inacurate tangens so correct perspective matrix like this:
double perspective[16];
glGetDoublev(GL_PROJECTION_MATRIX,perspective);
perspective[ 0]= 1.0/tan(0.5*zang*deg);
perspective[ 5]=aspect/tan(0.5*zang*deg);
glLoadMatrixd(perspective);
deg = M_PI/180.0
perspective
是投影矩阵副本我用它来进行鼠标位置转换等...
如果您不校正矩阵,那么在使用诸如重叠更多平截头体以获得高精度深度范围之类的高级方法时,您将无法使用。我用它来获得 <0.1m,1000AU>
具有 24 位深度缓冲区的平截头体,不准确会导致图像无法完美匹配 ...
[备注]
如果焦点不是真正的 (0,0,0)
或者您没有在 Z 轴上查看(就像您没有相机矩阵而是使用投影矩阵那样)那么基本 scenes/techniques 你不会有问题。他们从使用高级图形开始。如果你使用 GLSL 那么你可以毫无问题地处理这个问题,但是固定的 OpenGL 函数不能正确处理这个问题。这也叫PROJECTION_MATRIX abuse
[edit1]几个links
如果您的视图是标准截锥体,那么您可以自己编写矩阵 gluPerspective otherwise look here Projections 了解如何构建它的一些想法
[edit2]
从你的评论来看,我是这样看的:
f
是你的视点(坐标轴是全球坐标轴)
f'
是观察点,如果 R
是屏幕的中心
因此为 f'
位置创建投影矩阵(如上所述),创建变换矩阵以将 f'
变换为 f
。转换后的 f
必须具有与 f'
中相同的 Z 轴,其他轴可以通过叉积获得并将其用作相机或一起乘以 booth 并用作滥用的投影矩阵
我之前的评论
中的了解变换矩阵link中解释了如何构建矩阵我认为您将这个问题放在 "opengl" 标签下造成了一些混乱。问题在于,在计算机图形学中,术语 projection 不能从严格的数学意义上理解。
在数学中,投影被定义为(以下不是确切的数学定义,只是我自己的解释)当应用两次时不会进一步改变结果。想想看。当您将 3d space 中的一个点投影到 2d 平面(仍在那个 3d space 中)时,每个点的投影最终都会落在该平面上。但是已经在该平面上的点不再移动,因此您可以根据需要多次应用它,而无需进一步改变结果。
计算机图形学中的经典 "projection" 矩阵不会这样做。他们 从 转换 space 的方式是将一般的平截头体映射到立方体(或长方体)。为此,您基本上需要所有参数来描述视锥体,通常是纵横比、视野角、到近平面和远平面的距离,以及投影方向和中心点(后两者通常是隐含的)按惯例定义)。对于一般情况,还有水平和垂直不对称分量(可以把它想象成投影仪的 "lens shift")。所有这些都是计算机图形学中典型的投影矩阵所代表的。
根据您提供的参数构建这样的矩阵是不可能的,因为您缺少很多参数。另外 - 我认为这有点揭示 - 你已经给出了一个 视图平面 。但是到目前为止讨论的投影矩阵没有定义一个视图平面 - 任何平行于近平面或远平面并且在相机前面的平面都可以想象为观察平面(在相机后面也可以,但是图像会被镜像), 如果你需要的话。但从严格意义上讲,如果所有投影点也将最终落在该平面上,它只会是 "view plane"——计算机图形透视矩阵明确不会这样做。相反,它保留了它们的 3d 距离信息——这也意味着操作是可逆的,而经典数学投影通常不是。
综上所述,我只是猜测您正在寻找的是从 3D space 到 2D 平面的透视投影,而不是用于计算机图形的透视变换。您需要的所有参数只是视点和平面。请注意,这 正是 您给出的内容:投影中心应为原点,R
和 N
定义平面。
这样的投影也可以用4x4的齐次矩阵表示。您的问题中没有定义一件事:法线的方向。我再次假设标准数学约定并假设视图平面定义为 <N,x> + d = 0
。通过在该等式中使用 R
,我们可以得到 d = -N_x*R_x - N_y*R_y - N_z*R_z
。所以投影矩阵就是
( 1 0 0 0 )
( 0 1 0 0 )
( 0 0 1 0 )
(-N_x/d -N_y/d -N_z/d 0 )
这个矩阵有几个属性。有一个零列,所以它是不可逆的。另请注意,对于应用此方法的每个点 (s*x, s*y, s*z, 1)
,无论 s
是什么,结果(当然除以结果 w
后)都是相同的 - 所以每个点原点和 (x,y,z)
之间的一条线将导致相同的投影点 - 这就是透视投影应该做的。最后注意 w=(N_x*x + N_y*y + N_z*z)/-d
,因此对于满足上述平面方程的每个点,都会得到 w= -d/-d = 1
。结合其他维度的恒等变换,就是说这样一个点是不变的。