如何正确在相机后面投射点?


10

我正在制作3D游戏,在其中将感叹号放在关注点上方。

为了找出应该在2D屏幕中放置标记的位置,我手动将3D点投影到标记应放置的位置。

看起来像这样:

标记看起来很棒

看起来还不错 当标记位于屏幕之外时,我只需剪切坐标即可使其适合屏幕。看起来像这样:

标记看起来更棒

到目前为止,这个想法进展顺利。但是,当关注点在相机后面时,得到的X,Y坐标将反转(正/负),并且我将标记显示在屏幕的对角,如下所示:

标记不太好

(投影然后夹紧的点是标记的尖端。不要介意标记的旋转)

这是有道理的,因为平截头体后面的坐标在X和Y中是反转的。所以我要做的是在它们位于摄影机后面时反转它们的坐标。但是,我仍然不知道将坐标反转时的确切条件是什么。

当前,这是我的投影代码的样子(在带有SharpDX的C#中):

    public override PointF ProjectPosition(float viewportWidth, float viewportHeight, float y)
    {
        var projectionMatrix = Matrix.PerspectiveFovRH(GetCalibratedFOV(Camera.FOV, viewportWidth, viewportHeight), viewportWidth / viewportHeight, Camera.Near, Camera.Far);
        var viewMatrix = Matrix.LookAtRH(new Vector3(Camera.PositionX, Camera.PositionY, Camera.PositionZ), new Vector3(Camera.LookAtX, Camera.LookAtY, Camera.LookAtZ), Vector3.UnitY);
        var worldMatrix = Matrix.RotationY(Rotation) * Matrix.Scaling(Scaling) * Matrix.Translation(PositionX, PositionY, PositionZ);
        var worldViewProjectionMatrix = worldMatrix * viewMatrix * projectionMatrix;

        Vector4 targetVector = new Vector4(0, y, 0, 1);
        Vector4 projectedVector = Vector4.Transform(targetVector, worldViewProjectionMatrix);

        float screenX = (((projectedVector.X / projectedVector.W) + 1.0f) / 2.0f) * viewportWidth;
        float screenY = ((1.0f - (projectedVector.Y / projectedVector.W)) / 2.0f) * viewportHeight;
        float screenZ = projectedVector.Z / projectedVector.W;

        // Invert X and Y when behind the camera
        if (projectedVector.Z < 0 ||
            projectedVector.W < 0)
        {
            screenX = -screenX;
            screenY = -screenY;
        }

        return new PointF(screenX, screenY);
    }

如您所见,我当前的想法是在Z或W坐标为负时反转坐标。它在大多数情况下都可以使用,但是仍然有一些非常特定的相机位置无法使用。特别是这一点显示一个坐标有效,而另一坐标不工作(正确的位置应在右下角):

标记很棒

我尝试在以下情况下反转:

  • Z 是负面的(这对我来说最有意义)
  • W 为负(我不了解负W值的含义)
  • 无论是ZW为负(什么是目前工作的大部分时间)
  • Z并且W具有不同的符号,又名:(Z / W < 0对我来说很有意义。虽然不起作用)

但仍未找到正确投影所有点的一致方法。

有任何想法吗?

Answers:


2

怎么做有点不同?

照常开始,并在视口中渲染所有标记。如果标记不在视口中,请继续:

  1. 获取标记的位置 A
  2. 获取相机的位置B(也许在相机的前面)
  3. 计算AB这两者之间的3D矢量
  4. 将该矢量转换并归一化为2D屏幕边界(A将位于视图中心并B触及视图边框)

在此处输入图片说明

彩色线是AB矢量。黑色细线是字符高度。右边是2D屏幕

  1. 也许要增加一条规则,即摄像机后面的所有内容始终位于底部边缘
  2. 根据标记的大小和重叠程度在屏幕空间中绘制标记

您可能需要尝试查看离开视口的标记是否会在位置之间跳转。如果发生这种情况,您可以尝试在标记接近视口边界时锁定位置。


第4步如何工作?
熊猫睡衣

@PandaPajama:我已经添加了一些答案。现在更清楚了吗?
Kromster

并不是的。您如何“转换和规范化”向量?如果是通过应用投影矩阵,那么你做我在做同样的事情
熊猫睡衣

@PandaPajama:据我了解,您正在相机空间中进行操作(因此存在负ZW问题)。我建议在世界空间中执行此操作,然后再转换为屏幕空间。
Kromster

但是,通过计算AB,我不是将目标位置从世界坐标转换为相机坐标吗?
熊猫睡衣

1

给定三个向量[camera position][camera direction][object position]。计算的点积[camera direction][object position]-[camera position]。如果结果是否定的,则该物体在摄像机后面。

鉴于我理解您的代码正确,它将是:

if((PositionX - Camera.PositionX) * Camera.LookAtX
    + (PositionY - Camera.PositionY) * Camera.LookAtY
    + (PositionZ - Camera.PositionZ) * Camera.LookAtZ
    < 0)

YPositionY + (y * Scaling)。今晚我会尝试一下
熊猫睡衣

有了这个,我确实可以知道该点何时位于相机后面。但是,这不会阻止投影在Z = 0处达到奇点。
2015年

@PandaPajama这是一个实际的问题吗?Z准确0出现的可能性应该很小,大多数玩家将永远不会体验到,并且在少数情况下对游戏玩法的影响是可以忽略的单帧视觉故障。我想说,您的时间最好花在其他地方。
aaaaaaaaaaaa 2015年

0

仅测试(预测的W <0),而不测试(Z <0 || W <0)。

在某些剪辑空间公式中(咳嗽 OpenGL 咳嗽),可见范围包括正Z值和负Z值,而W始终在相机前面为正,而在后面始终为负。


我试过只有W <0,但是仍然有一些地方投影不正确。
熊猫睡衣

0
float screenX = (((projectedVector.X / abs(projectedVector.W)) + 1.0f) / 2.0f) * viewportWidth;
float screenY = ((1.0f - (projectedVector.Y / abs(projectedVector.W))) / 2.0f) * viewportHeight;

并删除这部分

// Invert X and Y when behind the camera
if (projectedVector.Z < 0 || projectedVector.W < 0)
{
    screenX = -screenX;
    screenY = -screenY;
}

-2

尝试使用广告牌,该广告牌将使图片自动面对相机,并将图片自动绘制到屏幕上的正确位置


3
你看过问题了吗?
Kromster

抱歉,让我解释一下-可以获取广告牌的输出,摆脱缩放比例(因此是2D,但遵循3d位置),然后使用一些矩阵数学将其保持在屏幕范围内。
Michael Langford

问题特别是关于“然后使用一些矩阵数学将其保留在屏幕范围内
Kromster
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.