带glFrustum的离轴投影 [英] Off-axis projection with glFrustum

查看:181
本文介绍了带glFrustum的离轴投影的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用OpenGL做一个场景的离轴投影,我给文档读取 Robert Kooima的离轴投影,现在有一个更好的想法,实际上必须做的,但仍有一些我发现棘手的这里。我知道OpenGL的离轴投影代码有一些如下:



代码1:

  glMatrixMode(GL_PROJECTION); 
glLoadIdentity();
glFrustum(fNear *( - fFov * ratio + headX),
fNear *(fFov * ratio + headX),
fNear *( - fFov + headY) (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);

如果这是用户在屏幕中央的正常透视投影,

 屏幕
|
| h = H / 2
|
x ----- n -----------
|
| h = H / 2
|

用户在x和屏幕距离为n时,顶部,底部坐标为 glFrustum 将被计算为:(假设θ是视场(fov),我假设它为 30度

  h = n * tan(θ/ 2); 
tanValue = DEG_TO_RAD * theta / 2;
:fFov = tan(tanValue);
h = n * tan(tanValue);

因此,顶部和底部(顶部的值取反)都是从 glFrustum 参数。左边的是现在左/右。

 现在,Aspect Ratio,r = ofGetWidth()/ ofGetHeight 
Right = n *(fFov * r); ,其中r是宽高比[Edit1>>已在此处写为tanValue * r]

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



(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是什么用法?我观察到,它带来了msX和msY变化的x值(-0.5到0.5)和y值到(0.5到-0.5)。



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

  -fFov + headY 
fFov + headY

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

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

如何帮助我们

问题 4)我理解,对于离轴投影的非对称glFrustum调用,必须对视点执行glLookAt,以将视锥体的顶点移动到用户眼睛的位置(在这种情况下,鼠标在)。注意上面代码中的行:



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



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



EDIT:完整的问题描述添加在此处: pastebin.com/BiSHXspb

解决方案

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

视野定义为角度<在屏幕线的两个尖端B,B'和点x之间形成的角度θcos = f os = angle((x,B),(x,B'))三角函数 (tan)定义为

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

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



< H /(2·n)== tan(fov)== tan(angle((x,B),(x,B'))由于在三角学中,角度以弧度给出,但大多数(x,



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

  tanValue = DEG_TO_RAD * theta / 2; 

然后使用,我们通过

计算 h

  h = tan(tanValue)* n 


$ b b

如果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单位距离在这张图片):





当X偏移为-0.5时,它看起来像这样(橙色轮廓)看到近平面的左边缘已经移动到-0.5。





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


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


因为fFov不是一个角度, span ASCII码艺术图片中的 H / 2 = h


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


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



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



/ request



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



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

这对离轴投影没有什么特别的,但可以在每个透视投影辅助函数中找到;比较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);
}

如果我们认为近剪裁平面范围是[ ]×[-1,1],那么当然头部x位置不在归一化范围[-1,1]中,而必须在范围[-aspect,aspect]中给出。 / p>

如果你看看你链接的论文,你会发现,对于每个屏幕,由跟踪器报告的头位置被转换为绝对






两个星期前,我有机会测试一个名为Z space的显示系统偏振立体显示器与头部跟踪器组合,产生与显示器前面的物理头部位置匹配的离轴视锥/外观组合。它还提供了一个笔与你的3D前景的场景进行交互。这是我在过去几年看到的最令人印象深刻的事情之一,我目前正在乞求我的老板买我们一个:)


I am trying to do an off-axis projection of a scene with OpenGL and I gave a read to the document to Robert Kooima's off-axis projection and have a much better idea now of what actually has to be done but there are still some pieces which I am finding tricky here. I got to know of the off-axis projection code for OpenGL to be somewhat as follows:

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

Had this been a normal perspective projection with the user at the center of the screen, it is fairly easy to understand as I comprehend.

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

With the user at x and distance from screen being n, the top, bottom coordinates for glFrustum would be calculated as: (assume theta is the Field of View (fov) which I suppose is assumed as 30 degrees)

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

Hence, top and bottom (negating top's value) are both obtained for glFrustum arguments. Left one's are left/right for now.

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

Question 1) Is the above (tanValue*r) getting the horizontal fov angle and then applying the same to get left/right value?

   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;

Now, consider the projection off-axis and we have the headX and headY position computed (using mouse here instead of actual user's head):

Question 2) 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.

Question 3) In the above code (Code 1), how is headY being added to the calculated to the tan(fov/2) value?

-fFov + headY
fFov + headY

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

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

How does the abvoe give us a vlaue which wehn multiplied by n (near value) gives us left and right for the assymetric glFrustum call for off-axis projection?

Question 4) I understand that the glLookAt has to be done for View Point to shift the apex of the frustum to where the eye of the user is (in this case where the mouse is). Notice the line in the above code:

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

How is headX*headZ giving me the xPosition of the eye, headY*headZ giving me the yPosition of the eye which I can use in gluLookAt() here?

EDIT: Full problem description added here: pastebin.com/BiSHXspb

解决方案

You have made this nice picture of ASCII art

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

The field of view is defined as the angle fov = angle((x,B), (x,B')) formed between the two tips B, B' of the screen "line" and the point x. The trigonometric function Tangens (tan) is defines as

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

And since length(A, B) == length(A, B') == h == H/2 we know that

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

Since in trigonometry angles are given in radians, but most people are more comfortable with degrees you may have to convert from degress to radians.

So we're interested in only half of the screen span (= h) we've to half the angle. And if we want to accept degress also convert it to radians. That's what this expression is meant for.

tanValue = DEG_TO_RAD * theta/2;

Using that we then calculate h by

h = tan(tanValue) * n

If the FOV is for horizontal or vertical span of the screen depends on the way how the field span H is scaled with the aspect ratio.

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.

The calculations you gave assume that screen space coordinates are in a range [0, screenWidth] × [0, screenHeight]. However since we're doing our frustum calculations in a normalized range [-1, 1]² we want to bring the device absolute mouse coordinates to normalized center relative coordinates. This allows then to specify the axis offset relative to the normalized near plane size. This is how it looks with 0 offset (the grid has 0.1 units distance in this picture):

And with a X offset of -0.5 applied it looks like this (orange outline), as you can see the left edge of the near plane has been shifted to -0.5.

Now simply imagine that the grid was your screen, and your mouse pointer would drag around the projection frustum near plane bounds like that.

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

Because fFov is not an angle but the span H/2 = h in your ASCII art picture. And headX and headY are relative shifts in the normalized near projection plane.

How is headX*headZ giving me the xPosition of the eye, headY*headZ giving me the yPosition of the eye which I can use in gluLookAt() here?

The code you're quoted seems to be an ad-hoc solution on that account to emphase the effect. In a real head tracking stereoscopic system you do slightly different. Technically headZ should be either used to calculated the near plane distance or be derived from it.

Anyway the main ideas is, that the head is located at some distance from the projection plane, and the center point is shifted in relative units of the projection. So you must scale relative headX, headY with the actual head distance to the projection plane to make the apex correction work.

Update due to comment/request

So far we've looked at only one dimension when converting field of view (fov) to screen span. For the image to be undistorted the aspect ratio of the [left, right] / [bottom, top] extents of the near clipping plane must match the aspect ratio of the viewport width/height.

If we choose to define the FoV angle to be the vertical FoV, then the horizontal size of the near clipping plane extents is the size of the vertical near clipping plane extents scaled with the with/height aspect ratio.

This is nothing special about off-axis projection, but can be found in every perspective projection helper function; compare the source code of gluPerspective for reference:

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

And if we consider the near clipping plane extents to be [-aspect, aspect]×[-1, 1] then of course the headX position is not in the normalized range [-1, 1] but must be given in the range [-aspect, aspect] as well.

If you look at the paper you linked, you'll find that for each screen the head position as reported by the tracker is transformed in absolute coordinates relative to the screen.


Two weeks ago I had the opportunity to test a display system called "Z space" where a polarized stereo display had been combined with a head tracker creating an off-axis frustum / look-at combination that matched your physical head position in front of the display. It also offers a "pen" to interact with the 3D scene in front of you. This is one of the most impressive things I've seen in the last few years and I'm currently begging my boss to buy us one :)

这篇关于带glFrustum的离轴投影的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆