OpenGL - gluPerspective / glFrustum - zNear& zFar问题

时间:2011-09-07 17:52:35

标签: opengl perspectivecamera visual-glitch

我正在写一个太空探索应用程序。我决定光年是单位,并准确地模拟了恒星之间的距离。经过修修补补和大量艰苦的工作(主要是学习绳索)后,从穿越宇宙的星舰的角度来看,我的相机工作正常。

最初我没有注意gluPerspective()的zNear参数,直到我处理行星物体。由于我的规模是光年单位,我很快就意识到由于zNear为1.0f,我将无法看到这样的物体。经过实验,我得出了这些数字:

#define POV 45
#define zNear 0.0000001f
#define zFar 100000000.0f
gluPerspective (POV, WinWidth/WinHeight, zNear ,zFar);

这非常有效,因为我能够巡航我的太阳系(位置0,0,0)并靠近行星,这些行星看起来很亮,纹理映射。然而,其他系统(不在位置0,0,0)更难以巡航,因为物体以不寻常的方式远离相机。

然而,我注意到在宇宙中巡航时发生了奇怪的视觉故障。我身后的物体会“环绕”并向前显示,如果我在Y方向上摆动180度,它们也会出现在原来的位置。因此,当在太空中翘曲时,大多数恒星都是正确的并行,但有些出现并向相反的方向行进(至少可以说令人不安)。

通过将zNear更改为0.1f,可立即纠正所有这些毛刺(但也不会解析太阳系对象)。所以我被卡住了。我也试过使用glFrustum,它产生完全相同的结果。

我使用以下内容来查看世界:

glTranslatef(pos_x, pos_y, pos_z);

根据需要定位相关的摄像头代码。即使禁用相机功能也不会改变任何内容。我甚至尝试过gluLookAt()并再次产生相同的结果。

当使用极端zNear / zFar值时,gluPerspective()是否有限制?我试图缩小范围但无济于事。我甚至通过缩放所有内容并使用更大的zNear值将我的世界单位从光年改为公里 - 没有。帮助!

3 个答案:

答案 0 :(得分:5)

问题是你想要同时解决太多问题。你想要观察太阳系的规模,同时也有半银河系规模。这根本不可能。不是实时渲染器。

只有这么多的浮点精度。当你的zNear 令人难以置信地关闭时,你基本上已经破坏了你的深度缓冲区,用于距你的相机超过0.0001的任何东西。

你需要做的是根据距离画出东西。使用一个深度范围(例如,0到0.8),使用一个透视矩阵绘制近物体(在太阳系的尺度内)。然后用不同的透视矩阵和不同的深度范围(0.8到1)绘制更远的物体。这才是你实现这项工作的唯一途径。

此外,您可能需要以双精度数学计算CPU上对象的矩阵,然后将它们转换回单精度以供OpenGL使用。

答案 1 :(得分:3)

OpenGL不应该比zFar更远离相机,或者比zNear更接近相机。

但对于介于两者之间的东西,OpenGL会计算存储在深度缓冲区中的深度值,它用于判断一个对象是否阻挡了另一个对象。不幸的是,深度缓冲区具有有限的精度(通常为16或24位),并且根据this,大约log2(zFar / zNear)精度位丢失。因此,zFar / zNear比率为10 ^ 15(约50位丢失)必然会引起问题。一种选择是稍微增加zNear(如果可以的话)。否则,您将需要查看Split Depth Buffers或Logarithmic Depth Buffers

答案 2 :(得分:1)

Nicol Bolas已经告诉过你一个故事。另一个是,你应该开始考虑一种存储坐标的结构化方法:存储每个对象相对于以重要方式支配它的对象的位置,并使用适当的单位。

所以你有明星。恒星之间的距离是在光年中测量的。恒星被行星绕行。星系中的距离以光照到光照小时来测量。行星由卫星绕轨道运行。行星系统中的距离以光秒为单位进行测量。

要显示此类比例,您需要多次渲染。具有鳞片的物体形成树。首先,您将分支远离关闭,然后首先遍历树深度。对于每个分支级别,您使用适当的投影参数,以便near→far剪裁平面贴合地适合要渲染的对象。渲染每个级别后清除深度缓冲区。