定向弹丸保持面向相机
Oriented projectiles keep facing camera
我正在尝试渲染代表 3d 世界中射弹的 2d 图像,但我很难让射弹在不改变其方向的情况下面对相机。我正在使用 JOML 数学库。
我的工作代码将射弹定向到他的方向
public Quaternionf findRotation(Vector3f objectRay, Vector3f targetRay) {
Vector3f oppositeVector = new Vector3f(-objectRay.x, -objectRay.y, -objectRay.z);
// cas vecteur opposé
if(oppositeVector.x == targetRay.x && oppositeVector.y == targetRay.y && oppositeVector.z == targetRay.z) {
AxisAngle4f axis = new AxisAngle4f((float) Math.toRadians(180), 0, 0, 1);
Quaternionf result = new Quaternionf(axis);
return result;
}
objectRay = objectRay.normalize();
targetRay = targetRay.normalize();
double angleDif = Math.acos(new Vector3f(targetRay).dot(objectRay));
if (angleDif!=0) {
Vector3f orthoRay = new Vector3f(objectRay).cross(targetRay);
orthoRay = orthoRay.normalize();
AxisAngle4f deltaQ = new AxisAngle4f((float) angleDif, orthoRay.x, orthoRay.y, orthoRay.z);
Quaternionf result = new Quaternionf(deltaQ);
return result.normalize();
}
return new Quaternionf();
}
现在我想添加一个 vector3f cameraPosition 参数来仅在其 x 轴上旋转射弹以面向相机,但我不知道该怎么做。
例如,使用此代码,射弹正确地围绕其 x 轴旋转但不面向相机,所以我想知道如何找到正确的角度。
this.lasers[i].getModel().rotate((float) Math.toRadians(5), 1, 0, 0);
我试过在计算角度之前使用变换向量绕 X 轴旋转。
this.lasers[i] = new VisualEffect(this.position, new Vector3f(1,1,1), color, new Vector2f(0,0.33f));
this.lasers[i].setModel(new Matrix4f().scale(this.lasers[i].getScale()));
this.lasers[i].getModel().rotate(rotation);
this.lasers[i].getModel().translateLocal(this.lasers[i].getPosition());
Vector3f vec = new Vector3f(cameraPosition).sub(this.position);
Vector4f vecSpaceModel = this.lasers[i].getModel().transform(new Vector4f(vec, 1.0f));
Vector4f normalSpaceModel = this.lasers[i].getModel().transform(new Vector4f(normal, 1.0f));
float angleX = new Vector2f(vecSpaceModel.y, vecSpaceModel.z).angle(new Vector2f(normalSpaceModel.y, normalSpaceModel.z));
this.lasers[i].getModel().rotate(angleX, 1, 0, 0);
由于您使用的是 JOML,因此可以大大简化整个设置。
让我们假设:
projectilePosition
为弹丸位置,
targetPosition
是弹丸飞行的位置at/towards,
cameraPosition
是“相机”的位置(我们最终希望弹丸面对)
我们还假设射弹的局部坐标系是这样的,它的 +X 轴指向射弹的路径(就像你描述的那样),+Z 轴远离射弹指向观察者,当观看者正“面对”弹丸。因此,弹丸本身在其局部坐标系内被定义为 XY 平面上的四边形。
我们现在必须做的是创建一个基础变换,该变换将有效地变换射弹,使其 X 轴指向“目标”,Z 轴“尽我们所能”指向相机。
这很容易让人想起我们在 OpenGL 中称为“lookAt”的变换。事实上,我们只是要使用它。然而,由于常见的“lookAt”是我们想要做的inverse,我们也将它反转。
所以,总而言之,单个射弹的完整模型 matrix/transformation 将如下所示(在 JOML 中):
Vector3f projectilePosition = ...;
Vector3f cameraPosition = ...;
Vector3f targetPosition = ...;
Vector3f projectileToCamera = new Vector3f(cameraPosition).sub(projectilePosition);
modelMatrix
.setLookAt(projectilePosition, targetPosition, projectileToCamera)
.invert()
.rotateXYZ((float) Math.toRadians(-90), 0, (float) Math.toRadians(90));
如果你不想使用lookAt
和invert
,你也可以这样做:
Vector3f projectileToTarget = new Vector3f(targetPosition).sub(projectilePosition);
modelMatrix
.translation(projectilePosition)
.rotateTowards(projectileToTarget, projectileToCamera)
.rotateXYZ((float) Math.toRadians(-90), 0, (float) Math.toRadians(-90));
产生与上述代码相同的结果。
请注意,我们实际上在任何地方都不需要 角度 或三角函数。当您已经将所有 positions/directions 作为向量给出时,这很常见,您可以简单地使用线性代数而不转换 from/to 角度。
rotateXYZ(90°, 0°, 90°)
的最后一部分是表示我们不希望射弹的 -Z 轴指向目标(默认情况下 lookAt 会这样做),但我们希望X轴指向目标。
还有一种理解方式就是我们这里做的也被称为“圆柱形”或“轴向”广告牌,也可以这样表达:
Vector3f projectileToTarget = new Vector3f(targetPosition).sub(quadPosition).normalize();
modelMatrix
.billboardCylindrical(projectilePosition, cameraPosition, projectileToTarget)
.rotateZ((float) Math.toRadians(90));
(注意这里projectileToTarget
需要是单位!)
一个包含 24 个射弹的简单场景的测试,所有射弹都瞄准“中心”,相机悬停在它们上方将如下所示:
The corresponding simple LWJGL 3 / JOML demo generating this image.
我正在尝试渲染代表 3d 世界中射弹的 2d 图像,但我很难让射弹在不改变其方向的情况下面对相机。我正在使用 JOML 数学库。
我的工作代码将射弹定向到他的方向
public Quaternionf findRotation(Vector3f objectRay, Vector3f targetRay) {
Vector3f oppositeVector = new Vector3f(-objectRay.x, -objectRay.y, -objectRay.z);
// cas vecteur opposé
if(oppositeVector.x == targetRay.x && oppositeVector.y == targetRay.y && oppositeVector.z == targetRay.z) {
AxisAngle4f axis = new AxisAngle4f((float) Math.toRadians(180), 0, 0, 1);
Quaternionf result = new Quaternionf(axis);
return result;
}
objectRay = objectRay.normalize();
targetRay = targetRay.normalize();
double angleDif = Math.acos(new Vector3f(targetRay).dot(objectRay));
if (angleDif!=0) {
Vector3f orthoRay = new Vector3f(objectRay).cross(targetRay);
orthoRay = orthoRay.normalize();
AxisAngle4f deltaQ = new AxisAngle4f((float) angleDif, orthoRay.x, orthoRay.y, orthoRay.z);
Quaternionf result = new Quaternionf(deltaQ);
return result.normalize();
}
return new Quaternionf();
}
现在我想添加一个 vector3f cameraPosition 参数来仅在其 x 轴上旋转射弹以面向相机,但我不知道该怎么做。
例如,使用此代码,射弹正确地围绕其 x 轴旋转但不面向相机,所以我想知道如何找到正确的角度。
this.lasers[i].getModel().rotate((float) Math.toRadians(5), 1, 0, 0);
我试过在计算角度之前使用变换向量绕 X 轴旋转。
this.lasers[i] = new VisualEffect(this.position, new Vector3f(1,1,1), color, new Vector2f(0,0.33f));
this.lasers[i].setModel(new Matrix4f().scale(this.lasers[i].getScale()));
this.lasers[i].getModel().rotate(rotation);
this.lasers[i].getModel().translateLocal(this.lasers[i].getPosition());
Vector3f vec = new Vector3f(cameraPosition).sub(this.position);
Vector4f vecSpaceModel = this.lasers[i].getModel().transform(new Vector4f(vec, 1.0f));
Vector4f normalSpaceModel = this.lasers[i].getModel().transform(new Vector4f(normal, 1.0f));
float angleX = new Vector2f(vecSpaceModel.y, vecSpaceModel.z).angle(new Vector2f(normalSpaceModel.y, normalSpaceModel.z));
this.lasers[i].getModel().rotate(angleX, 1, 0, 0);
由于您使用的是 JOML,因此可以大大简化整个设置。
让我们假设:
projectilePosition
为弹丸位置,targetPosition
是弹丸飞行的位置at/towards,cameraPosition
是“相机”的位置(我们最终希望弹丸面对)
我们还假设射弹的局部坐标系是这样的,它的 +X 轴指向射弹的路径(就像你描述的那样),+Z 轴远离射弹指向观察者,当观看者正“面对”弹丸。因此,弹丸本身在其局部坐标系内被定义为 XY 平面上的四边形。
我们现在必须做的是创建一个基础变换,该变换将有效地变换射弹,使其 X 轴指向“目标”,Z 轴“尽我们所能”指向相机。
这很容易让人想起我们在 OpenGL 中称为“lookAt”的变换。事实上,我们只是要使用它。然而,由于常见的“lookAt”是我们想要做的inverse,我们也将它反转。
所以,总而言之,单个射弹的完整模型 matrix/transformation 将如下所示(在 JOML 中):
Vector3f projectilePosition = ...;
Vector3f cameraPosition = ...;
Vector3f targetPosition = ...;
Vector3f projectileToCamera = new Vector3f(cameraPosition).sub(projectilePosition);
modelMatrix
.setLookAt(projectilePosition, targetPosition, projectileToCamera)
.invert()
.rotateXYZ((float) Math.toRadians(-90), 0, (float) Math.toRadians(90));
如果你不想使用lookAt
和invert
,你也可以这样做:
Vector3f projectileToTarget = new Vector3f(targetPosition).sub(projectilePosition);
modelMatrix
.translation(projectilePosition)
.rotateTowards(projectileToTarget, projectileToCamera)
.rotateXYZ((float) Math.toRadians(-90), 0, (float) Math.toRadians(-90));
产生与上述代码相同的结果。
请注意,我们实际上在任何地方都不需要 角度 或三角函数。当您已经将所有 positions/directions 作为向量给出时,这很常见,您可以简单地使用线性代数而不转换 from/to 角度。
rotateXYZ(90°, 0°, 90°)
的最后一部分是表示我们不希望射弹的 -Z 轴指向目标(默认情况下 lookAt 会这样做),但我们希望X轴指向目标。
还有一种理解方式就是我们这里做的也被称为“圆柱形”或“轴向”广告牌,也可以这样表达:
Vector3f projectileToTarget = new Vector3f(targetPosition).sub(quadPosition).normalize();
modelMatrix
.billboardCylindrical(projectilePosition, cameraPosition, projectileToTarget)
.rotateZ((float) Math.toRadians(90));
(注意这里projectileToTarget
需要是单位!)
一个包含 24 个射弹的简单场景的测试,所有射弹都瞄准“中心”,相机悬停在它们上方将如下所示:
The corresponding simple LWJGL 3 / JOML demo generating this image.