使用 glFrustum 进行离轴投影

2024-04-02

我正在尝试使用 OpenGL 对场景进行离轴投影,并且我阅读了该文档罗伯特·库伊马的离轴投影 http://csc.lsu.edu/~kooima/pdfs/gen-perspective.pdf现在对实际需要做什么有了更好的了解,但仍然有一些部分我觉得很棘手。我了解到OpenGL的离轴投影代码如下:

Code 1:

glMatrixMode(GL_PROJECTION);  
    glLoadIdentity();            
    glFrustum(fNear*(-fFov * ratio + headX),  
              fNear*(fFov * ratio + headX),  
              fNear*(-fFov + headY),  
              fNear*(fFov + headY),  
              fNear, fFar);  
          
    glMatrixMode(GL_MODELVIEW);  
    glLoadIdentity();  
    gluLookAt(headX*headZ, headY*headZ, 0, headX*headZ, headY*headZ, -1, 0, 1, 0);
    glTranslatef(0.0,0.0,headZ);

如果这是一个正常的透视投影,用户位于屏幕的中心,那么据我理解,这是相当容易理解的。

               Screen  
                   |
                   |  h = H/2
                   |  
x----- n -----------
                   |
                   |  h = H/2
                   |

当用户位于 x 且距屏幕的距离为 n 时,顶部、底部坐标为圆台计算如下:(假设 theta 是视野 (fov)我想假设为30度)

h = n * tan (theta/2);
tanValue = DEG_TO_RAD * theta/2;
[EDIT Line additon here>>]: fFov = tan(tanValue);
h = n * tan (tanValue);

因此,顶部和底部(否定顶部的值)都是为圆台论据。左边的现在是左/右。

Now, Aspect Ratio, r = ofGetWidth()/ofGetHeight();
Right = n * (fFov * r); , where r is the aspect ratio [Edit1>> Was written tanValue*r earlier here]

Question1) 上面的 (tanValue*r) 是否得到水平视场角然后应用相同的方法来获得左/右值?

   双 msX = (double)ofGetMouseX();
双 msY = (double)ofGetMouseY();
双 scrWidth = (double)ofGetWidth();
双 scrHeight = (double)ofGetHeight();

headX = (msX / scrWidth) - 0.5;
headY = ((scrHeight - msY) / scrHeight) - 0.5;
头Z = -2.0;

现在,考虑离轴投影,我们有headX and headY计算的位置(此处使用鼠标而不是实际用户的头部):

Question2) headX 和 y 是如何计算的?从上面减去 -0.5 有什么用?我观察到,随着 msX 和 msY 的变化,它使 x 值达到(-0.5 到 0.5),y 值达到(0.5 到 -0.5)。

Question3)在上面的代码(代码1)中,headY是如何添加到计算出的tan(fov/2)值中的?

-fFov + headY
fFov + headY

这个价值给我们带来了什么? -fFov是计算出的theta/2的tan值,但是如何直接添加headY呢?

-fFov * ratio + headX
-fFov * ratio + headX

上面如何给我们一个值,当乘以 n(近值)时,我们可以得到离轴投影的不对称 glFrustum 调用的左和右?

Question4)我知道必须为视点完成 glLookAt 将平截头体的顶点移动到用户眼睛所在的位置(在本例中为鼠标所在的位置)。注意上面代码中的一行:

gluLookAt(headX*headZ, headY*headZ, 0, headX*headZ, headY*headZ, -1, 0, 1, 0);

How is headX*headZ给我眼睛的展示,headY*headZ给我可以使用的眼睛的 y 位置gluLookAt() here?

编辑:此处添加完整的问题描述:Pastebin.com/BiSHXspb http://pastebin.com/BiSHXspb


你已经制作了这张漂亮的 ASCII 艺术图

               Screen  
                   B
                   |  h = H/2
                   |  
x----- n ----------A
                   |
                   |  h = H/2
                   B'

视野定义为角度fov = angle((x,B), (x,B'))在屏幕“线”的两个尖端 B、B' 和点 x 之间形成。三角函数Tangens(tan) 定义为

h/n = tan( angle((x,A), (x,B)) )

自从length(A, B) == length(A, B') == h == H/2我们知道

H/(2·n) == tan( fov ) == tan( angle((x,B), (x,B')) ) == tan( 2·angle((x,A), (x,B)) )

由于在三角学中角度以弧度给出,但大多数人更喜欢度数,因此您可能必须将度数转换为弧度。

所以我们只对屏幕跨度的一半感兴趣(=h)我们必须将角度减半。如果我们想接受度数,请将其转换为弧度。这就是这个表达的意思。

tanValue = DEG_TO_RAD * theta/2;

然后我们用它来计算h by

h = tan(tanValue) * n

FOV 是针对屏幕的水平还是垂直跨度取决于场跨度的方式H随纵横比缩放。

headX 和 y 是如何计算的?从上面减去 -0.5 有什么用?我观察到,随着 msX 和 msY 的变化,它使 x 值达到(-0.5 到 0.5),y 值达到(0.5 到 -0.5)。

您给出的计算假设屏幕空间坐标在 [0, screenWidth] × [0, screenHeight] 范围内。然而,由于我们在标准化范围 [-1, 1]² 中进行平截头体计算,我们希望将设备绝对鼠标坐标转换为标准化中心相对坐标。这允许指定相对于标准化近平面尺寸的轴偏移。这是偏移量为 0 时的样子(图中网格的距离为 0.1 单位):

应用 X 偏移 -0.5 后,它看起来像这样(橙色轮廓),您可以看到近平面的左边缘已移动到 -0.5。

现在,简单地想象网格是您的屏幕,您的鼠标指针将像这样在平面边界附近的投影平截头体周围拖动。

这个价值给我们带来了什么? -fFov是计算出的theta/2的tan值,但是如何直接添加headY呢?

因为 fFov 不是角度而是跨度H/2 = h在你的 ASCII 艺术图片中。和headX and headY是标准化近投影平面中的相对位移。

headX怎么样headZ 给我眼睛的展示,headYheadZ 给了我眼睛的 yPosition,我可以在 gluLookAt() 中使用它吗?

您引用的代码似乎是该帐户的临时解决方案,以强调效果。在真实的头部跟踪立体系统中,您的做法略有不同。从技术上来说headZ应该用于计算近平面距离或从中导出。

无论如何,主要思想是,头部位于距投影平面一定距离的位置,并且中心点以投影的相对单位移动。所以你必须相对缩放头X、头Y与实际头部到投影平面的距离进行顶点校正。

根据评论/请求进行更新

到目前为止,我们在将视场 (fov) 转换为屏幕跨度时仅考虑了一维。为了使图像不失真,近剪裁平面的[左,右]/[下,上]范围的纵横比必须与视口宽度/高度的纵横比匹配。

如果我们选择将 FoV 角度定义为垂直 FoV,则近裁剪平面范围的水平尺寸就是用 with/height 纵横比缩放的垂直近裁剪平面范围的尺寸。

这对于离轴投影来说没什么特别的,但可以在每个透视投影辅助函数中找到;对比一下gluPerspective的源码供参考:

void GLAPIENTRY
gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
{
   GLdouble xmin, xmax, ymin, ymax;

   ymax = zNear * tan(fovy * M_PI / 360.0); // M_PI / 360.0 == DEG_TO_RAD
   ymin = -ymax;

   xmin = ymin * aspect;
   xmax = ymax * aspect;

   glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
}

如果我们认为近裁剪平面范围为 [-aspect, aspect]×[-1, 1] 那么当然headX位置不在标准化范围 [-1, 1] 内,但也必须在范围 [-aspect,aspect] 内给出。

如果您查看链接的论文,您会发现对于每个屏幕,跟踪器报告的头部位置都会转换为absolute相对于屏幕的坐标。


两周前,我有机会测试了一种名为“Z 空间”的显示系统,其中偏振立体显示器与头部跟踪器相结合,创建了一个离轴平截头体/观察组合,与您在显示器前面的物理头部位置相匹配。展示。它还提供了一支“笔”来与您面前的 3D 场景进行交互。这是我过去几年见过的最令人印象深刻的事情之一,我目前正在恳求我的老板给我们买一件:)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 glFrustum 进行离轴投影 的相关文章