我想知道能够缩放到 3D 屏幕空间选择框/窗口的最佳算法。
我目前可以使用它,但是当与目标的距离很大时,它似乎无法正确放大。
目前,它通过计算选择框相对于视口宽度/高度的比例来工作,并将该比例应用于相机与其目标的距离/范围。
其实没那么简单。看这张图(为简单起见,它是一个 2D 投影):
蓝色区域是当前相机的视锥。黄色区域是屏幕矩形选区覆盖的蓝色平截头体的一部分。目的是制作一个最能代表黄色区域的新平截头体。问题是新的平截头体应该以何种方式适合黄色区域。
下图展示了一种可能性:
新相机的视锥为紫色,相机的眼睛位于绿线上。假设新相机与旧相机具有相同的属性(fovy、znear、zfar、aspect),我们可以计算它的新位置和方向。
现在进行一些计算:
近平面的高度和宽度:
h = 2 * tan(fovy/2) * znear
w = aspect * h
矩形的屏幕空间坐标:
rectangle = ( x0, y0, x1, y1 )
矩形的屏幕空间中心:
rcenter = ( (x0+x1)/2, (y0+y1)/2 )
另一张用于阐明下一个计算的图像:
位于近平面上的视图空间向量,从近平面的中心指向矩形的中心:
center = ( (rcenter.x / screen_width - 0.5) * w,
(0.5 - rcenter.y / screen_height) * h, 0 )
然后我们必须将此向量转换为世界空间:
centerWS = center.x * camera_right_dir + center.y * camera_up_dir
相机的新方向(dir2n):
dir1 = camera_dir * znear
dir2 = dir1 + centerWS
dir2n = normalize(dir2)
相机的新位置(pos2):
为了简化计算,我做了一些假设。大约新的和旧的近平面是平行的,所以:
(w, h) / dist = (w * (x1-x0)/screen_width, h * (y1-y0)/screen_height) / znear
(1, 1) / dist = ( (x1-x0)/screen_width, (y1-y0)/screen_height) / znear
(1/dist, 1/dist) = ( (x1-x0)/screen_width, (y1-y0)/screen_height) / znear
因此:
dist = znear * screen_width / (x1-x0)
应该等于:
dist = znear * screen_height / (y1-y0)
仅当矩形与屏幕具有相同的比例时才是正确的,您可以通过在用户绘制矩形时锁定矩形的比例来保证这一点,或者您可以仅使用矩形的宽度(x1-x0)
而忽略其高度(y1-y0)
,反之亦然。
最后:
pos2 = pos1 + dir2n * (dist-znear)
我的理解是,您在屏幕上绘制了一个矩形以与窗口边界对齐,并使该矩形内的所有内容都放大。
如果我是对的,我们在这里试图实现的是将近平面与绘制矩形的假想平面对齐。
为此,我们必须使用一些三角函数。假设我们对齐矩形的顶部和底部:
我们应该将近平面的高度计算为:
H = tan(fovy/2) * nearPlane;
--------(1)
近平面上绘制矩形的高度:
fraction = WindowHeight / rectangle height;
h = H * fraction; ---------(2)
为了使近平面与假想平面对齐,相机必须向前移动。假设D是向前移动的距离,那么按几何,
h / nearPlane = H / (nearPlane+D)
--------(3)
使用等式(2),等式(3)可以简化为:
fraction / nearPlane = 1 / (nearPlane+D)
给出 D 为:
D = (nearPlane / fraction) - nearPlane;
或者
D = nearPlane * (1-fraction)/fraction;
现在将相机向前移动 D. 就可以了。
如果矩形没有居中对齐,计算会稍微复杂一些。