Math OpenGL摄像头

Math OpenGL摄像头,math,opengl,matrix,Math,Opengl,Matrix,我的OpenGL渲染器中有一个摄影机对象。它很好用。但是,我需要将其设置为父节点的父节点,以便父节点可以像在AdobeAfterEffects中使用Null对象那样操纵摄影机。如果您不熟悉AE,下面是它的工作原理。空对象是空容器。如果摄影机是其子对象,且对象本身位于目标位置,则当旋转空对象时,将其关注点(也称为注视点)置于目标位置的摄影机将围绕目标定向。这是问题的核心。在我的实现中,当我旋转父对象时,它将摄影机作为子对象并位于目标位置,摄影机不会保持锁定在父对象的位置,但其注视方向也会改变。以下

我的OpenGL渲染器中有一个摄影机对象。它很好用。但是,我需要将其设置为父节点的父节点,以便父节点可以像在AdobeAfterEffects中使用Null对象那样操纵摄影机。如果您不熟悉AE,下面是它的工作原理。空对象是空容器。如果摄影机是其子对象,且对象本身位于目标位置,则当旋转空对象时,将其关注点(也称为注视点)置于目标位置的摄影机将围绕目标定向。这是问题的核心。在我的实现中,当我旋转父对象时,它将摄影机作为子对象并位于目标位置,摄影机不会保持锁定在父对象的位置,但其注视方向也会改变。以下是描述该问题的屏幕截图:

左边的屏幕截图是错误的行为:摄影机的父对象位于中心,但摄影机的方向是旋转的,而不是摄影机。在右边的屏幕截图上,这就是它应该是什么样子以及它在AE中的工作方式:旋转空对象将围绕空对象中心轴旋转相机。 我肯定我在这里做了一些愚蠢的错误矩阵顺序的事情。下面是我在代码中的操作:

我计算相机的注视矩阵如下:

 public void lookAt(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, Vec3 upPt) {

    _eye.set(eyeX, eyeY, eyeZ);
    _center.set(centerX, centerY, centerZ);
    _up = upPt;

    _direction = Vec3.sub(_center, _eye).normalize();

    _viewMatr = Glm.lookAt(_eye, _center, _up);

    _transform.setModel(Mat4.mul(rotMat, _viewMatr));
///rotMat is rotation matrix cached from rotation method call.

 }
 public static Mat4 lookAt(Vec3 eye, Vec3 center, Vec3 up) {
    Vec3 f = normalize(Vec3.sub(center, eye));
    Vec3 u = normalize(up);
    Vec3 s = normalize(cross(f, u));
    u = cross(s, f);

    Mat4 result = new Mat4(1.0f);
    result.set(0, 0, s.x);
    result.set(1, 0, s.y);
    result.set(2, 0, s.z);
    result.set(0, 1, u.x);
    result.set(1, 1, u.y);
    result.set(2, 1, u.z);
    result.set(0, 2, -f.x);
    result.set(1, 2, -f.y);
    result.set(2, 2, -f.z);

    return translate(result, new Vec3(-eye.x,-eye.y,-eye.z));
  }

GLM:LooAccess是C++ GLM数学库中的端口,它看起来像:

 public void lookAt(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, Vec3 upPt) {

    _eye.set(eyeX, eyeY, eyeZ);
    _center.set(centerX, centerY, centerZ);
    _up = upPt;

    _direction = Vec3.sub(_center, _eye).normalize();

    _viewMatr = Glm.lookAt(_eye, _center, _up);

    _transform.setModel(Mat4.mul(rotMat, _viewMatr));
///rotMat is rotation matrix cached from rotation method call.

 }
 public static Mat4 lookAt(Vec3 eye, Vec3 center, Vec3 up) {
    Vec3 f = normalize(Vec3.sub(center, eye));
    Vec3 u = normalize(up);
    Vec3 s = normalize(cross(f, u));
    u = cross(s, f);

    Mat4 result = new Mat4(1.0f);
    result.set(0, 0, s.x);
    result.set(1, 0, s.y);
    result.set(2, 0, s.z);
    result.set(0, 1, u.x);
    result.set(1, 1, u.y);
    result.set(2, 1, u.z);
    result.set(0, 2, -f.x);
    result.set(1, 2, -f.y);
    result.set(2, 2, -f.z);

    return translate(result, new Vec3(-eye.x,-eye.y,-eye.z));
  }
这是第一部分,我创建相机“模型”矩阵。下一步是创建世界矩阵,考虑相机的父节点:

  Mat4 world=   Mat4.mul( this.getTransform().parentMatr, this.getTransform().getModel());


    this.getTransform().setView(world);
稍后在管道中,摄影机的视图矩阵(我刚刚使用setView设置)由每个要渲染的几何体对象访问,然后计算模型、视图、投影矩阵,然后发送到顶点着色器

奇怪的是,如果我在将世界矩阵传递到
setView()
方法之前反转它,并且在GLM中不求反眼向量,那么它就工作了!但在这种情况下,它在相机未设置父对象的模式下不工作。
请不要建议我使用OpenGL 4.0 Core的基于固定管道的解决方案。

有关数学知识,请阅读以下内容。要获得更实际的方法,请一直走到最后

您面临的问题是,仿射变换矩阵有效地描述了另一个笛卡尔坐标系中的笛卡尔坐标系。因此,通过将它们链接在一起,您总是相对于父对象工作。现在,如果将一个“摄影机”(OpenGL当然没有摄影机,但是为了本文的目的,让我们假设一个
lookAt
是一个摄影机)设置为某个坐标系的父对象,则所有变换都是相对于该坐标系的。因此,
lookAt
在本地坐标系的框架内运行

让我们从数学上把它分解:

有一个全球坐标空间,我们称之为世界。由于世界是一种中心轴心点,所以不会对世界空间中的坐标应用任何变换,即变换是恒等式,或者简单地说是i

“摄像机”是通过将整个世界移动到另一个位置来实现的。在OpenGL中没有摄影机,但视点始终位于原点
=(0,0,0)
。这可以被描述为整个世界转变成一个位置,从而使视点最终到达原点。假设V^-1描述了摄像机在世界原点到所需位置的变换,然后是V^-1的倒数,即

V^-1^-1=V

是视图转换。现在,在我们的例子中,具有相对变换L的相机是另一个对象的子对象,该变换由,比方说F来描述,所以总视图变换由

V^-1=F·L

L是变换
lookAt
产生的倒数。现在问题来了:
lookAt
在空间F内运行,这意味着传递给它的所有向量都必须是相对于F的。所以对于这项工作,你实际上也反转了F,所以

V^-1=(L^-1·F^-1)^-1

现在假设你有另一个在M范围之外的对象,你想看看。它由,比如说,相对于世界的G来描述。为了观察这个物体,我们必须知道它相对于F的位置,这很容易。我们首先“向前”从G走向世界,然后“向后”走向G,即(F^-1^-1·G=F·G

假设对象G以原点为中心,您必须计算F·G·(0,0,0,1),这归结为用F变换G的第四列。结果是您应该用作
注视的目标位置。这就是我的意思,把对象矩阵放进去



然而,整个方法可能会因复杂的转换链而中断。我建议做一件简单得多的事情:只需将相机和目标对象的位置转换为世界坐标,然后在世界空间上应用
lookAt
。要获得更实际的方法,请一直走到最后

您面临的问题是,仿射变换矩阵有效地描述了另一个笛卡尔坐标系中的笛卡尔坐标系。因此,通过将它们链接在一起,您总是相对于父对象工作。现在,如果将一个“摄影机”(OpenGL当然没有摄影机,但是为了本文的目的,让我们假设一个
lookAt
是一个摄影机)设置为某个坐标系的父对象,则所有变换都是相对于该坐标系的。因此,
lookAt
在本地坐标系的框架内运行

让我们从数学上把它分解:

有一个全球坐标空间,我们称之为世界。由于世界是一种中心轴心点,所以不会对世界空间中的坐标应用任何变换,即变换是恒等式,或者简单地说是i

“摄像机”是通过将整个世界移动到另一个位置来实现的。在OpenGL中没有摄影机,但视点始终位于原点
=(0,0,0)
。这可以是des