使用glFrustum进行轴外投影

use*_*679 5 c++ opengl algorithm 3d openframeworks

我正在尝试使用OpenGL对场景进行离轴投影,并且我将文档读取到了Robert Kooima的离轴投影,现在对于实际需要做的事情有了更好的想法,但仍然有一些部分我觉得这里很棘手.我知道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);
Run Code Online (Sandbox Code Playgroud)

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

               Screen  
                   |
                   |  h = H/2
                   |  
x----- n -----------
                   |
                   |  h = H/2
                   |
Run Code Online (Sandbox Code Playgroud)

当用户在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);
Run Code Online (Sandbox Code Playgroud)

因此,glFrustum参数都获得了top和bottom(否定top值).左边一个人现在是左/右.

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

问题 1)上面的(tanValue*r)是否获得水平 fov 角度,然后应用它来获得左/右值?

   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;

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

问题 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
Run Code Online (Sandbox Code Playgroud)

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

-fFov * ratio + headX
-fFov * ratio + headX
Run Code Online (Sandbox Code Playgroud)

abvoe如何为我们提供一个乘以n(近似值)的值,我们左右为不对称的glFrustum调用离轴投影?

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

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

如何headX*headZ给我眼睛的x位置,headY*headZ给我眼睛的y位置,我可以在gluLookAt()这里使用?

编辑:这里添加完整的问题描述:pastebin.com/BiSHXspb

dat*_*olf 6

你已经制作了这个很好的ASCII艺术图片了

               Screen  
                   B
                   |  h = H/2
                   |  
x----- n ----------A
                   |
                   |  h = H/2
                   B'
Run Code Online (Sandbox Code Playgroud)

视场被定义为fov = angle((x,B), (x,B'))在屏幕"线"的两个尖端B,B'和点x之间形成的角度.三角函数Tangens(tan)定义为

h/n = tan( angle((x,A), (x,B)) )
Run Code Online (Sandbox Code Playgroud)

因为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)) )
Run Code Online (Sandbox Code Playgroud)

因为在三角学中,角度以弧度给出,但是大多数人对度数更加舒适,您可能必须从degress转换为弧度.

所以我们只对屏幕跨度的一半(= h)感兴趣,我们只有角度的一半.如果我们想接受degress也将它转换为弧度.这就是这个表达的意思.

tanValue = DEG_TO_RAD * theta/2;
Run Code Online (Sandbox Code Playgroud)

然后用它来计算h by

h = tan(tanValue) * n
Run Code Online (Sandbox Code Playgroud)

如果FOV用于屏幕的水平或垂直跨度取决于字段跨度H如何用纵横比缩放的方式.

如何计算headX和y以及从上面减去-0.5的用途是什么?我观察到它使x值变为(-0.5到0.5),y值变为(0.5到-0.5),msX和msY变化.

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

平截头体中心投影

并且在施加-0.5的X偏移时,它看起来像这样(橙色轮廓),因为您可以看到近平面的左边缘已经移动到-0.5.

视锥体移位投影

现在简单地想象网格就是你的屏幕,你的鼠标指针会在平面边界附近的投影平截头体周围拖动.

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

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

headX headZ如何给我眼睛的xPosition,headY headZ给我眼睛的yPosition,我可以在gluLookAt()这里使用?

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

无论如何,主要思想是,头部位于距投影平面一定距离处,并且中心点以投影的相对单位移位.因此,您必须将相对headX,headY与实际头部距离缩放到投影平面,以使顶点校正起作用.

由于评论/请求而更新

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

如果我们选择将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);
}
Run Code Online (Sandbox Code Playgroud)

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

如果您查看您链接的纸张,您会发现,对于每个屏幕,跟踪器报告的头部位置将相对于屏幕以绝对坐标进行变换.


两周前,我有机会测试了一个名为"Z space"的显示系统,其中一个偏振立体显示器与一个头部跟踪器相结合,创建了一个离轴平截头体/观察组合,与你前面的物理头部位置相匹配.显示.它还提供了一个"笔",可以与您面前的3D场景进行交互.这是我在过去几年看到的最令人印象深刻的事情之一,我现在乞求我的老板给我们买一个:)