世界空间到屏幕空间(透视投影)

问题描述 投票:0回答:3

我正在使用 3d 引擎,需要使用透视投影在 3d 世界空间和 2d 屏幕空间之间进行转换,因此我可以将 2d 文本标签放置在 3d 空间中的项目上。 我看过一些关于这个问题的各种答案的帖子,但他们似乎使用了我没有的组件。

我有一个Camera对象,只能设置它的当前位置和观察位置,它不能滚动。相机沿着路径移动,某些目标物体可能会出现在其视野中然后消失。 我只有以下值

  • 看位置
  • 位置
  • 垂直视场
  • Z远
  • Z 近
  • 显然还有目标物体的位置。

任何人都可以给我一个仅使用这些组件即可完成此操作的算法吗?

非常感谢。

camera 3d
3个回答
9
投票

所有图形引擎都使用矩阵在不同坐标系统之间进行转换。事实上 OpenGL 和 DirectX 使用它们,因为它们是标准方式。

相机通常使用您拥有的参数构建矩阵:

  • 视图矩阵(将世界变换为从相机位置观察的方式的位置),它使用观察位置和相机位置(也是向上向量,通常为 0,1,0)

  • 投影矩阵(从3D坐标转换为2D坐标),它使用fov、near、far和aspect。

您可以在互联网上搜索创建矩阵的 opengl 函数,找到如何构造矩阵的信息:

  • gluLookat 创建一个视图矩阵

  • gluPerspective:创建投影矩阵

但是我无法想象一个引擎不允许你获取这些矩阵,因为我可以向你保证它们在某个地方,引擎正在使用它。

一旦获得这些矩阵,将它们相乘即可得到视图投影矩阵。该矩阵从世界坐标变换到屏幕坐标。因此,只需将矩阵与您想知道的位置相乘(以向量 4 格式,即 4° 分量 1.0)。

但是等等,结果将是齐次坐标,你需要将结果向量的 X,Y,Z 除以 W,然后你就得到了归一化屏幕坐标中的位置(0 表示中心,1 表示右侧,-1意味着向左,等等)。

从这里可以很容易地转换乘以宽度和高度。

我有一些幻灯片解释了这一切:https://docs.google.com/presentation/d/13crrSCPonJcxAjGaS5HJOat3MpE0lmEtqxeVr4tVLDs/present?slide=id.i0

祝你好运:)

P.S:当你使用 3D 时,理解三个矩阵(模型、视图和投影)非常重要,否则你每次都会绊倒。


1
投票

这样我就可以在项目上放置二维文本标签 在 3D 空间

你查过“广告牌”技巧吗?有时,您只需要知道要搜索的正确术语即可。这是指始终面向相机的多边形(通常是矩形),无论相机位置或方向如何。


0
投票

假设您知道相机在世界空间中的方向,并使用 unit 向量表示相机框架向前/向右/向上方向,您可以:

  • 找到相机和目标物体之间的线与近平面相交的位置
  • 找出该点距离近平面中心有多远
  • 使用该距离作为近平面尺寸的一部分,以了解标签应远离屏幕中心的屏幕空间的一部分

您需要知道近平面的世界大小:

#define DEGTORAD 0.01745329252

float aspectRatio = w / (float)h; // window/viewport dimensions

float nearPlaneHeight = 2 * tan(fovY * 0.5f * DEGTORAD) * nearPlaneDist;
float nearPlaneWidth = nearPlaneHeight * aspectRatio;

如果给定的世界点位于近平面前面,则此函数返回 true:

bool getScreenPos(vec3 worldPos, vec2& screenPos) {

    vec3 nearPlaneCenter = camera.location + nearPlaneDist * camera.forward;

    float dot1 = dot(camera.forward, camera.location);
    float dot2 = dot(camera.forward, nearPlaneCenter);
    float dot3 = dot(camera.forward, worldPos);

    if ( (dot3 < dot2 && dot1 < dot2) || (dot3 > dot2 && dot1 > dot2)) {
        return false; // camera and target are on same side of near plane
    }

    // distance from camera to near plane, as a fraction of distance from camera to target
    float fraction = (dot2 - dot1) / (dot3 - dot1);

    vec3 cameraToTarget = worldPos - camera.location;
    vec3 pointOnNearPlane = camera.location + fractionToNearPlane * cameraToTarget;

    // this is a line on the near plane, from its center to where the ray intersects
    vec3 projOnNearPlane = pointOnNearPlane - nearPlaneCenter;

    // these are the component-wise magnitudes of that line in the camera frame
    float rightDot = dot(projOnNearPlane, camera.right);
    float upDot =    dot(projOnNearPlane, camera.up);

    screenPos.x = w/2 + w * rightDot / nearPlaneWidth;
    screenPos.y = h/2 - h * upDot    / nearPlaneHeight;

    return true;
}
© www.soinside.com 2019 - 2024. All rights reserved.