5

我想完善以前的问题

如何将球体投影到屏幕上?

(2)给出一个简单的解决方案:

屏幕上的近似半径[CLIP SPACE] = 世界半径 * cot(fov / 2) / Z

和:
fov = 视场角
Z = z 相机到球体的距离

结果在剪辑空间中,乘以视口大小以获得以像素为单位的大小

现在我的问题是我没有 FOV。只有视图和投影矩阵是已知的。(以及视口大小,如果有帮助的话)

任何人都知道如何从投影矩阵中提取 FOV?

更新:

这种近似在我的情况下效果更好:

float radius = glm::atan(radius/distance);
radius *= glm::max(viewPort.width, viewPort.height) / glm::radians(fov);
4

4 回答 4

4

我参加这个聚会有点晚了。但是当我研究同样的问题时,我遇到了这个线程。我花了一天的时间研究这个问题,并通过我在这里找到的一些优秀文章进行了工作: http ://www.antongerdelan.net/opengl/virtualcamera.html

我最终从投影矩阵开始并向后工作。我得到了您在上面的帖子中提到的相同公式。(其中 cot(x) = 1/tan(x) )

radius_pixels = (radius_worldspace / {tan(fovy/2) * D}) * (screen_height_pixels / 2)

(其中 D 是相机到目标边界球的距离)

我正在使用这种方法来确定用于旋转对象的假想轨迹球的半径。

顺便说一句,您可以从 Projection 矩阵中提取 fovy,如下所示:

如果您从投影矩阵中获取 Sy 分量,如下所示:

Sx  0   0   0
0   Sy  0   0
0   0   Sz  Pz
0   0  -1   0

where Sy = near / range

and where range = tan(fovy/2) x near

(您可以在我上面链接的页面上找到这些定义)

如果您在上面的 Sy eqn 中替换范围,您将得到:

Sy = 1 / tan(fovy/2) = cot(fovy/2)

重新排列:

tan(fovy/2) = 1 / Sy

取两边的arctan(tan的倒数),我们得到:

fovy/2 = arctan(1/Sy)

所以,

fovy = 2 x arctan(1/Sy)

不确定你是否还在乎 - 已经有一段时间了!- 但也许这会帮助别人。

于 2013-11-07T18:39:56.487 回答
2

更新:见下文。

由于您拥有视图和投影矩阵,因此这是一种方法,尽管它可能不是最短的:

  • 使用视图矩阵将球体的中心转换为视图空间:调用结果点 C
  • 将球体表面上的一个点,例如世界坐标中的 C+(r, 0, 0),其中 r 是球体的世界半径,转换到视图空间;调用结果点 S
  • 计算 rv = 从 C 到 S 的距离(在视图空间中)
  • 让视图坐标中的点 S1 为 C + (rv, 0, 0) - 即视图空间中球体表面上的另一个点,线 C -> S1 垂直于“look”向量
  • 使用投影矩阵 Cs 和 S1s 将 C 和 S1 投影到屏幕坐标中
  • 计算屏幕半径 = Cs 和 S1s 之间的距离

但是,是的,就像 Brandorf 说的那样,如果你可以保留相机变量,比如 FOVy,那就容易多了。:-)

更新: 这是上面的一个更有效的变体:对投影矩阵进行逆运算。使用它将视口边缘转换回视图空间。这样您就不必将每个框都投影到屏幕坐标中。

更好的是,对视图矩阵执行相同的操作并将相机平截头体转换回世界空间。这对于比较许多盒子来说会更有效;但更难弄清楚数学。

于 2010-09-15T15:52:48.007 回答
1

在您的链接上发布的答案radiusClipSpace = radius * cot(fov / 2) / Z,其中 fov 是视场角,Z 是到球体的 z 距离,绝对有效。但是,请记住,radiusClipSpace必须乘以视口的宽度才能获得像素测量值。如果对象适合屏幕,则在 radiusClipSpace 中测量的值将是一个介于 0 和 1 之间的值。

另一种解决方案可能是使用球体的立体角。天空中一个球体对着的立体角基本上是它投影到单位球体时所覆盖的区域。

在此处输入图像描述

公式在此链接中给出,但大致我正在做的是:

if( (!radius && !distance) || fabsf(radius) > fabsf(distance) )
  ; // NAN conditions. do something special.

theta=arcsin( radius/distance )
sphereSolidAngle = ( 1 - cosf( theta ) ) ; // not multiplying by 2PI since below ratio used only
frustumSolidAngle = ( 1 - cosf( fovy / 2 ) ) / M_PI ; // I cheated here. I assumed
// the solid angle of a frustum is (conical), then divided by PI
// to turn it into a square (area unit square=area unit circle/PI)

numPxCovered = 768.f*768.f * sphereSolidAngle / frustumSolidAngle ; // 768x768 screen
radiusEstimate = sqrtf( numPxCovered/M_PI ) ; // area=pi*r*r

这与 的数字大致相同radius * cot(fov / 2) / Z。如果您只想估计球体投影所覆盖的区域(以 px 为单位),这可能是一种简单的方法。

我不确定是否可以轻松找到对截锥体立体角的更好估计。这种方法涉及比radius * cot(fov / 2) / Z.

于 2013-09-05T13:33:10.630 回答
0

FOV 不直接存储在投影矩阵中,而是在您调用 gluPerspective 构建结果矩阵时使用。

最好的方法是简单地将所有相机变量保存在它们自己的类中,例如平截头体类,在调用 gluPerspective 或类似方法时会使用其成员变量。

有可能将 FOVy 从矩阵中取出,但所需的数学运算却让我望而却步。

于 2010-09-15T15:27:21.690 回答