修复了3D相机向其面向的方向移动的问题?简短版本详细信息和示例评论中问题的答案
简短版本
我有一个附加到a和运动工作正常,只要的旋转/轴与世界对齐。但是,当一个物体旋转以“看”不同的方向并被告知“向前”移动时,它不会沿着新的“向前”方向移动。相反,它继续沿应用旋转之前所面向的同一方向移动。Camera
SceneNode
SceneNode
详细信息和示例
我有一个场景图来管理3D场景。该图是对象的树,这些对象知道它们相对于其父项和世界的转变。SceneNode
根据TL;DR;片段,假设你有一个零旋转(例如朝北),然后围绕+Y“向上”轴向左旋转90度,即让它向西看。到目前为止,一切都很好。如果你现在试图移动“前进”,现在是向西移动,而是移动,就好像“前进”仍然朝北一样。cameraNode
cameraNode
cameraNode
cameraNode
简而言之,它就像它从未被旋转过一样移动。
下面的代码显示了我最近尝试过的内容,以及我(当前)在缩小最有可能与问题相关的区域方面的最佳猜测。
相关成员SceneNode
该实现具有以下字段(仅显示与此问题相关的字段):SceneNode
class GenericSceneNode implements SceneNode {
// this node's parent; always null for the root scene node in the graph
private SceneNode parentNode;
// transforms are relative to a parent scene node, if any
private Vector3 relativePosition = Vector3f.createZeroVector();
private Matrix3 relativeRotation = Matrix3f.createIdentityMatrix();
private Vector3 relativeScale = Vector3f.createFrom(1f, 1f, 1f);
// transforms are derived by combining transforms from all parents;
// these are relative to the world --in world space
private Vector3 derivedPosition = Vector3f.createZeroVector();
private Matrix3 derivedRotation = Matrix3f.createIdentityMatrix();
private Vector3 derivedScale = Vector3f.createFrom(1f, 1f, 1f);
// ...
}
将 a 添加到场景仅意味着它被附加到图形中的 a。由于 没有自己的位置/旋转信息,因此客户端只需处理 所附加到的,仅此而已。Camera
SceneNode
Camera
SceneNode
Camera
除了这个问题中提到的问题外,其他所有内容似乎都按预期工作。
SceneNode
译本
在特定方向上转换节点的数学很简单,基本上可以归结为:
currentPosition = currentPosition + normalizedDirectionVector * offset;
实现如下:SceneNode
@Override
public void moveForward(float offset) {
translate(getDerivedForwardAxis().mult(-offset));
}
@Override
public void moveBackward(float offset) {
translate(getDerivedForwardAxis().mult(offset));
}
@Override
public void moveLeft(float offset) {
translate(getDerivedRightAxis().mult(-offset));
}
@Override
public void moveRight(float offset) {
translate(getDerivedRightAxis().mult(offset));
}
@Override
public void moveUp(float offset) {
translate(getDerivedUpAxis().mult(offset));
}
@Override
public void moveDown(float offset) {
translate(getDerivedUpAxis().mult(-offset));
}
@Override
public void translate(Vector3 tv) {
relativePosition = relativePosition.add(tv);
isOutOfDate = true;
}
除了这个问题中提到的问题之外,事情也如预期的那样。
SceneNode
旋转
客户端应用程序按如下方式轮换:cameraNode
final Angle rotationAngle = new Degreef(-90f);
// ...
cameraNode.yaw(rotationAngle);
而且实现也相当简单:SceneNode
@Override
public void yaw(Angle angle) {
// FIXME?: rotate(angle, getDerivedUpAxis()) accumulates other rotations
rotate(angle, Vector3f.createUnitVectorY());
}
@Override
public void rotate(Angle angle, Vector3 axis) {
relativeRotation = relativeRotation.rotate(angle, axis);
isOutOfDate = true;
}
旋转的数学/代码封装在 3x3 矩阵对象中。请注意,在测试期间,您可以看到场景围绕相机旋转,因此确实应用了旋转,这使这个问题更加困扰我。
方向矢量
相对于世界,方向向量只是从派生的 3x3 旋转矩阵中获取的列:
@Override
public Vector3 getDerivedRightAxis() {
return derivedRotation.column(0);
}
@Override
public Vector3 getDerivedUpAxis() {
return derivedRotation.column(1);
}
@Override
public Vector3 getDerivedForwardAxis() {
return derivedRotation.column(2);
}
计算派生变换
如果相关,则说明转换是如何组合以计算实例的派生转换的:parentNode
this
private void updateDerivedTransforms() {
if (parentNode != null) {
/**
* derivedRotation = parent.derivedRotation * relativeRotation
* derivedScale = parent.derivedScale * relativeScale
* derivedPosition = parent.derivedPosition + parent.derivedRotation * (parent.derivedScale * relativePosition)
*/
derivedRotation = parentNode.getDerivedRotation().mult(relativeRotation);
derivedScale = parentNode.getDerivedScale().mult(relativeScale);
Vector3 scaledPosition = parentNode.getDerivedScale().mult(relativePosition);
derivedPosition = parentNode.getDerivedPosition().add(parentNode.getDerivedRotation().mult(scaledPosition));
} else {
derivedPosition = relativePosition;
derivedRotation = relativeRotation;
derivedScale = relativeScale;
}
Matrix4 t, r, s;
t = Matrix4f.createTranslationFrom(relativePosition);
r = Matrix4f.createFrom(relativeRotation);
s = Matrix4f.createScalingFrom(relativeScale);
relativeTransform = t.mult(r).mult(s);
t = Matrix4f.createTranslationFrom(derivedPosition);
r = Matrix4f.createFrom(derivedRotation);
s = Matrix4f.createScalingFrom(derivedScale);
derivedTransform = t.mult(r).mult(s);
}
这用于在场景图中传播变换,以便子项可以考虑其父项的变换。SceneNode
其他/相关问题
在发布此问题之前的过去3周内,我已经在SO内部和外部浏览了几个答案(例如,这里,这里,这里和这里,以及其他几个)。显然,虽然相关,但它们对我的情况真的没有帮助。
评论中问题的答案
您确定在计算时已经计算了父母的计算吗?
derivedTransform
derivedTransform
是的,在更新子级之前,始终会更新父项。逻辑是:SceneNode
update
@Override
public void update(boolean updateChildren, boolean parentHasChanged) {
boolean updateRequired = parentHasChanged || isOutOfDate;
// update this node's transforms before updating children
if (updateRequired)
updateFromParent();
if (updateChildren)
for (Node n : childNodesMap.values())
n.update(updateChildren, updateRequired);
emitNodeUpdated(this);
}
@Override
public void updateFromParent() {
updateDerivedTransforms(); // implementation above
isOutOfDate = false;
}
本文调用上一节中的私有方法。