代码之家  ›  专栏  ›  技术社区  ›  fho

投影球面半径

  •  4
  • fho  · 技术社区  · 14 年前

    我想完善一个 previous question :

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

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

    approximate radius on screen[CLIP SPACE] = world radius * cot(fov / 2) / Z
    
    with:
    fov = field of view angle
    Z   = z distance from camera to sphere
    
    result is in clipspace, multiply by viewport size to get size in pixels
    

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

    有人知道如何从投影矩阵中提取FOV吗?

    更新:

    这种近似法在我的例子中效果更好:

    float radius = glm::atan(radius/distance);
    radius *= glm::max(viewPort.width, viewPort.height) / glm::radians(fov);
    
    4 回复  |  直到 14 年前
        1
  •  2
  •   LarsH    14 年前

    更新:见下文。

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

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

    但是,是的,就像布兰多夫说的,如果你能保存相机的变量,比如佛维,那就容易多了。-)

    更新: 上面有一个更有效的变体:对投影矩阵求逆。使用它可以将视区边缘转换回视图空间。那么你就不必把每一个框都投影到屏幕坐标中。

    更好的是,对视图矩阵做同样的操作,并将相机截锥转换回世界空间。这对于比较多个方框会更有效率,但很难搞清楚数学。

        2
  •  3
  •   bootstrapper    11 年前

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

    我用这种方法来确定我用来旋转物体的假想轨迹球的半径。

    btw florian,您可以从投影矩阵中提取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)
    

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

        3
  •  1
  •   bobobobo    9 年前

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

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

    enter image description here

    公式如下: 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来估计球体投影所覆盖的面积,这可能是一个简单的方法。

    我不确定是否能轻易地找到对截锥立体角的更好估计。这种方法涉及的复杂性比 半径*cot(fov/2)/z .

        4
  •  0
  •   Brandorf    14 年前

    FOV不是直接存储在投影矩阵中,而是在调用GluPerspective来构建生成的矩阵时使用。

    最好的方法是简单地将所有摄像机变量保存在它们自己的类中,例如一个截锥类,当您调用GluPerspective或类似的类时,将使用它们的成员变量。

    也许有可能把这辆雪佛兰从矩阵中拿出来,但我对所需要的数学一无所知。