投射球的半径

时间:2010-09-15 11:41:37

标签: math opengl geometry projection

我想要优化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 个答案:

答案 0 :(得分:3)

我参加这个派对有点晚了。但是当我调查同样的问题时,我遇到了这个问题。我花了一天的时间研究这个,并在我找到的一些优秀文章中工作: 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,你可以从Projection矩阵中提取fovy如下:

如果从Projection矩阵中获取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)

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

答案 1 :(得分:2)

更新:见下文。

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

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

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

<强>更新 以上是上述更有效的变体:制作投影矩阵的逆矩阵。使用它将视口边缘转换回视图空间。然后,您不必将每个框都投影到屏幕坐标中。

更好的是,对视图矩阵执行相同操作,将相机视锥反射回到世界空间。这对于比较许多盒子来说会更有效率;但更难弄清楚数学。

答案 2 :(得分:1)

答案发布在您的链接radiusClipSpace = radius * cot(fov / 2) / Z,其中fov是视野的角度,Z是球体的z距离,绝对有效。但请注意,radiusClipSpace必须乘以视口的宽度才能获得像素度量。如果对象适合屏幕,则radiusClipSpace中测量的值将介于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中所覆盖的面积,这可能是一种简单的方法。

我不确定是否可以轻易找到更好的估算平截头体的立体角。此方法涉及的补偿比radius * cot(fov / 2) / Z更多。

答案 3 :(得分:0)

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

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

有可能将FOVy从矩阵中取出,但所需的数学计算不了我。