【问题标题】:Radius of projected Sphere投影球体半径
【发布时间】:2011-04-12 15:31:00
【问题描述】:

我想完善一个previous question

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

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

【问题讨论】:

    标签: math opengl geometry projection


    【解决方案1】:

    我参加这个聚会有点晚了。但是当我研究同样的问题时,我遇到了这个线程。我花了一天时间研究这个问题,并通过我在这里找到的一些优秀文章进行了研究: 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 是相机到目标边界球的距离)

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

    顺便说一句,你可以从投影矩阵中提取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)
    

    不确定您是否还在乎 - 已经有一段时间了! - 但也许这会对其他人有所帮助。

    【讨论】:

      【解决方案2】:

      更新:见下文。

      既然你有视图和投影矩阵,这里有一种方法,虽然它可能不是最短的:

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

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

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

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

      【讨论】:

      • 其实我现在这样做......但我不认为这是最好的方式:(
      • 啊...这是个好主意...我自己应该有那个:)
      【解决方案3】:

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

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

      公式在this link 给出,但大致我正在做的是:

      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涉及更多的comps。

      【讨论】:

      • Uuuh ... Graverobber you :) 但似乎是一个不错(虽然复杂)的解决方案。
      • 我在第一行之后得到theta = NaN
      • 是的,当我发布这个radius/distance 是 NaN 如果它们都是 0 并且arcsin 将是 NaN 如果 |radius| > |距离| (即当事物比它大时更接近)。所以你需要一些足够的 NaN 守卫。
      • 所以我最终还是在我的代码中使用了cot 解决方案。只要您考虑到 NaN 条件,这两种方法都有效。
      【解决方案4】:

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

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

      也许可以将 FOVy 从矩阵中取出,但我无法计算所需的数学。

      【讨论】:

      • 有人告诉我,只要“不涉及轮换”,就可以轻松提取它……但我没有时间在那里调查。
      • @Florian - 不管是谁告诉你的,问问他们怎么做。 :-)
      • @fho:我猜你可以从矩阵中提取截锥体,然后根据它计算 FOV。这是我能想到的唯一方法。但你为什么要这样做?只需使用您用来创建矩阵的 FOV。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-10
      • 1970-01-01
      • 1970-01-01
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多