c++ - 使用 glFrustum 进行离轴投影

标签 c++ algorithm opengl 3d openframeworks

我正在尝试使用 OpenGL 对场景进行离轴投影,并且我阅读了文档给 Robert Kooima's off-axis projection并且现在对实际必须做的事情有了更好的了解,但仍有一些我在这里发现棘手的部分。我了解到 OpenGL 的离轴投影代码如下:

代码 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 时,glFrustum 的顶部、底部坐标将计算为:(假设 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);

因此,顶部和底部(否定顶部的值)都是为 glFrustum 参数获得的。左边的现在是左/右。
Now, Aspect Ratio, r = ofGetWidth()/ofGetHeight();
Right = n * (fFov * r); , where r is the aspect ratio [Edit1>> Was written tanValue*r earlier here]

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

double msX = (double)ofGetMouseX();
double msY = (double)ofGetMouseY();
double scrWidth = (double)ofGetWidth();
double scrHeight = (double)ofGetHeight();
headX = (msX/scrWidth) - 0.5;
headY = ((scrHeight - msY)/scrHeight) - 0.5;
headZ = -2.0;

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

问题 2) headX 和 y 是如何计算的,从上面减去 -0.5 有什么用?我观察到它使 x 值变为(-0.5 到 0.5)和 y 值到(0.5 到 -0.5),而 msX 和 msY 发生变化。

问题 3) 在上面的代码(代码 1)中,headY 是如何添加到计算的 tan(fov/2) 值中的?
-fFov + headY
fFov + headY

这个值(value)为我们提供了什么? -fFov 是 theta/2 的计算 tan 但如何直接添加 headY ?
-fFov * ratio + headX
-fFov * ratio + headX

abvoe 如何给我们一个 vlaue,乘以 n(近值)给我们左右为非对称 glFrustum 调用离轴投影?

问题 4)我知道必须为 View Point 执行 glLookAt 才能将视锥体的顶点移动到用户眼睛所在的位置(在这种情况下是鼠标所在的位置)。注意上面代码中的一行:

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

怎么样headX*headZ给我眼睛的 xPosition,headY*headZ给我我可以在 gluLookAt() 中使用的眼睛的 yPosition这里?

编辑:在此处添加完整的问题描述: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
h = tan(tanValue) * n
FOV 是用于屏幕的水平跨度还是垂直跨度取决于视场跨度 H 与纵横比的缩放方式。

How is the headX and y being computed and what is the use subtracting -0.5 from the above? I observed that it brings the x-value to (-0.5 to 0.5) and y-value to (0.5 to -0.5) with msX and msY varying.


您给出的计算假设屏幕空间坐标在 [0, screenWidth] × [0, screenHeight] 范围内。然而,由于我们在标准化范围 [-1, 1]² 内进行视锥体计算,我们希望将设备绝对鼠标坐标带到标准化中心相对坐标。这允许然后指定相对于归一化近平面尺寸的轴偏移。这是它在 0 偏移时的样子(这张图片中的网格有 0.1 个单位的距离):
frustum center projection
应用 -0.5 的 X 偏移后,它看起来像这样(橙色轮廓),正如您所看到的,近平面的左边缘已移动到 -0.5。
frustum shifted projection
现在简单地想象一下,网格是你的屏幕,你的鼠标指针会像这样在平面边界附近的投影平截头体周围拖动。

What does this value provide us with? -fFov was the calculated tan of theta/2 but how can headY be added to directly?


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

How is headXheadZ giving me the xPosition of the eye, headYheadZ giving me the yPosition of the eye which I can use in gluLookAt() here?


您引用的代码似乎是该帐户的临时解决方案,以强调效果。在真正的头部跟踪立体系统中,您的做法略有不同。从技术上讲, headZ 应该用于计算近平面距离或从中导出。
无论如何,主要思想是,头部位于距投影平面一定距离的位置,中心点以投影的相对单位移动。因此,您必须使用实际头部到投影平面的距离来缩放相对 headX、headY,以便进行顶点校正。
由于评论/请求而更新
到目前为止,在将视野 (fov) 转换为屏幕跨度时,我们只研究了一个维度。为了使图像不失真,近剪裁平面的 [左、右]/[底、顶] 范围的纵横比必须与视口(viewport)宽度/高度的纵横比匹配。
如果我们选择将 FoV 角度定义为垂直 FoV,则近剪裁平面范围的水平大小是垂直近剪裁平面范围的大小,与高宽比成比例。
这对于离轴投影并没有什么特别之处,但可以在每个透视投影辅助函数中找到;对比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] 内,但必须在范围 [ -方面,方面]也是如此。
如果您查看链接的论文,您会发现对于每个屏幕,跟踪器报告的头部位置都转换为相对于屏幕的绝对坐标。

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

关于c++ - 使用 glFrustum 进行离轴投影,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16446092/

相关文章:

c++ - MFC显示透明背景的png

C++如何在外部类中声明的内部类中使用变量

java - 找到相交轨迹的最佳方法

c++ - 使用 This 指针分配内存

c++ - 可以针对不完整类型检查概念吗

java - lwjgl isKeyDown 取消其他键

c++ - 不正确的 OpenGL 缠绕

c++ - 我们需要序列化 ​​VAO 和 VBO

algorithm - 是否可以计算O(1)中Number中的Set位数?

algorithm - 是否可以在没有锁的情况下创建线程安全的集合?