我的OpenGL渲染器中有一个相机对象。它工作正常。但是,我需要将其父级节点父节点,以便父级可以操作相机,就像在具有Null对象的Adobe AfterEffects中一样。如果您不熟悉AE,那么它是如何工作的。 Null Object是一个空容器。如果摄像机是其父级并且对象本身位于目标位置,那么当空对象旋转时,在目标处具有其感兴趣点(也称为lookAt)的摄像机将围绕目标定向。这是问题的核心。在我的实现中,当我旋转父级时,相机作为子级并位于目标位置,相机不会保持锁定在父级位置,但其lookAt方向也会改变。以下是描述该问题的屏幕截图:
左侧屏幕截图是错误的行为:相机的父级位于中心,但相机的方向是旋转而不是相机。在右侧屏幕截图中,它应该是如何以及它在AE中的工作原理:旋转Null对象会围绕零对象中心轴旋转相机。 我确信我在这里做了一些愚蠢的错误矩阵顺序。所以这就是我在代码中的表现:
我像这样计算相机的外观矩阵:
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.
}
Glm:lookAt是来自C ++ GLM math lib的端口,它看起来像这样:
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));
}
这是第一部分,我创建了相机“模型”矩阵.Nex步骤是创建考虑到相机父节点的世界矩阵:
Mat4 world= Mat4.mul( this.getTransform().parentMatr, this.getTransform().getModel());
this.getTransform().setView(world);
稍后在管道中,将要渲染的每个几何对象访问摄像机的视图矩阵(我刚刚使用setView设置),并计算模型,视图,投影矩阵,然后将其发送到顶点着色器。
奇怪的是,如果我在将世界矩阵传递给setView()
方法之前将其反转并且不在GLM中否定眼睛向量,那么它可以工作!但在这种情况下它不能在模式下工作相机不是父母的地方。
请不要建议我使用OpenGL 4.0 Core的固定管道解决方案。
答案 0 :(得分:1)
对于数学,请阅读以下内容。对于更实用的方法,直到最后。
您面临的问题是,仿射变换矩阵有效地描述了另一个笛卡尔坐标系内的笛卡尔坐标系。因此,通过将它们链接在一起,您总是相对于父母工作。现在,如果你的父母是一个“相机”(OpenGL当然没有相机,但是为了这个文本,我们假设一个lookAt
是一个相机)到某个坐标系,所有的变换都是相对于这个坐标系的。因此lookAt
在该局部坐标系的框架中运行。
让我们以数学方式解决这个问题:
有一个全球坐标空间,我们称之为世界。由于世界是中心枢轴点,因此世界空间中的坐标不应用变换,即变换是身份,或者只是我。
通过将整个世界移动到另一个位置来实现“相机”。在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的第4列。结果是你的应该用作lookAt
的目标位置。这就是我将对象矩阵放入其中的意思。
然而,整个方法可能会因复杂的转换链而破裂。我建议更简单的方法:只需将相机和目标物体的位置转换为世界坐标,并将lookAt
应用于世界空间。