使用glFrustum进行轴外投影

时间:2013-05-08 16:51:33

标签: 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)是否获得水平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;

现在,考虑离轴投影,我们计算了 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

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

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

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

1 个答案:

答案 0 :(得分:5)

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

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

视场被定义为在屏幕“线”的两个尖端B,B'和点x之间形成的角度fov = angle((x,B), (x,B'))。三角函数 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)) )

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

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

tanValue = DEG_TO_RAD * theta/2;

使用它然后按

计算 h
h = tan(tanValue) * n

如果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单位):

frustum center projection

如果施加了-0.5的X偏移,它看起来像这样(橙色轮廓),因为你可以看到近平面的左边缘已经移动到-0.5。

frustum shifted projection

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

  

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

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

  

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);
}

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

如果你查看你链接的纸张,你会发现,对于每个屏幕,跟踪器报告的头部位置在相对于屏幕的绝对坐标中进行转换。


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