提问人:DavidH 提问时间:5/25/2023 更新时间:5/29/2023 访问量:148
OpenGL 图形中放大/缩小范围较大的渲染精度问题
Rendering Accuracy Problem with Large Zoom in/Out Range in OpenGL Graphics
问:
我有一个简单的基于相机的“现代”OpenGL 3D 图形显示器,用于渲染由指定点、线和曲线(例如立方体、圆柱体等)的集合构建的相对简单的对象。绘制了三条不同颜色的固定长度线,它们在世界空间的中心相互相交,以直观地表示 XYZ 笛卡尔轴。用户可以通过鼠标控制,在视图周围平移,旋转视图并放大/缩小(通过鼠标滚轮移动)。
如果我尝试将轴的原点进行大量放大,使其能够直观地分离一些非常接近原点的渲染点(例如,相距 0.000001 个长度单位),则会出现渲染精度问题:
(1) 三条轴线开始无法在同一点(原点)相互相交。其中两条轴相交,第三条轴线分别与这两条线相交,距离原点一小段距离。第三轴的分离量随观察旋转而略有变化。
和
(2) 例如,打算正好位于其中一个轴上的点不再这样渲染,而是看起来略微偏离轴线(同样,点与轴的分离量会随着视图旋转而略有变化)。
为了提高精度,我从使用默认的“GLfloat”更改为“GLdouble”,并将所有与模型几何相关的代码(例如指定顶点位置、距离等)修改为双精度(即使用“dvec3”而不是默认的“vec3”等)。但这没有区别。[注意:继续使用 GLfloat 而不是 GLdouble 的唯一项目是为渲染的点或线的颜色指定 RGB 值]
如何通过极端放大到非常小的比例来保持渲染的准确性?
答:
您必须仅使用两个远离原点的点来渲染线条,例如 (-1,0,0) 和 (1,0,0)。
当它们投影到放大的视口上时(假设在 S 比例下),它们的浮点坐标会变得非常大(在 S 量级)。然后,光栅器在将它们渲染到屏幕上时需要将它们剪裁到屏幕上,其中视口中的坐标相对较小 (<1)。这导致了精度的损失:因为线的端点不精确(舍入误差为 ε·S),视口中的插值结果将同样不精确(即高达 ε·S 关闭理论值)。
如果光栅器在内部使用 32 位浮点数,我预计栅格化器的精度约为 ε = 2−23 ≈ 0.0000001,这与您开始观察效果的比例一致。请注意,它不受属性精度的影响,而是光栅器内部的,并且可以很好地在电路中硬连线。
解决方案实际上相当简单。您需要做的就是拆分您的行,以便它们显式地通过原点。即将其呈现为两个段:(-1,0,0) 到 (0,0,0) 和 (0,0,0) 到 (1,0,0)。这样,原点将被投影到其确切位置,并且视口外端点的舍入误差对绘制线的其余部分的影响很小。
评论
没有办法(据我所知)直接将 64 位 s 传递给插值器,因为 Vertex 正在截断到 32 位 s(或更少)。double
gl_Position
float
仍然有一些方法可以提高精度:
根据量级将值分隔为更多浮点数
如果您知道里面的范围,则可以执行此操作,请参阅:
用 3 个浮子模拟传球双打
因此,您将双精度尾数(1+52 位)除以二分,并将其存储在 CPU 端的 3 个浮点数(1+23 位)中,并将其传递给顶点着色器:
double x; // 64 bit input float x0,x1,x2; // 3x 32 bit output x0=float(x); x-=x0; x1=float(x); x-=x1; x2=float(x);
然后在 Fragment 端,你重建回来:
double x; // 64 bit output float x0,x1,x2; // 3x 32 bit input x =x0; x+=x1; x+=x2;
使用相对坐标
因此,您知道您的视图以某个点 P0 为中心,因此您只需在应用缩放(或渲染)之前从所有坐标(和相机位置)中减去该值即可。这将大大爱上所需的尾数钻头并克服精度问题(在一定程度上)
例如,这对我解决了这个高变焦问题有所帮助:
此外,这种方式不需要着色器(以防您坚持使用旧的 GL)
请注意,#1,#2 和使用内置几何渲染从未真正帮助过我(可能是着色器编译器在我早已过时的 gfx 卡上所做的),在这种情况下,您需要完全绕过原始光栅器并自行渲染它们,如下所示:
因此,渲染基元和内部着色器的 BBOX QUAD 使用 SDF 指向外部。简单的基元,如直线、三角形等,可以用简单的方程式来决定,然而,上面的链接并非如此(这就是为什么它如此复杂)。discard;
O(1)
评论