定向弹丸保持面向相机

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));

如果你不想使用lookAtinvert,你也可以这样做:

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.