OpenGL不对称视锥体用于桌面VR

4
我正在制作一个OpenGL c++应用程序,跟踪用户在屏幕上的位置,然后更新渲染场景以符合用户的视角。这就是所谓的“桌面VR”,或者你可以将屏幕看作是一个景观模型或鱼缸。我对OpenGL还很陌生,目前只定义了一个非常简单的场景,只有一个立方体,最初渲染正确。
问题在于,当我开始移动并想重新渲染立方体场景时,投影平面似乎被平移了,我看不到我认为应该看到的东西。我希望这个平面固定不动。如果我要编写光线追踪器,我的窗口始终固定,但我的眼睛可以漫游。请问有人能解释一下如何实现我想要的效果(固定视窗),同时让我的摄像机/眼睛在非原点坐标处漫游吗?
我找到的所有示例都要求摄像机/眼睛位于原点,但这对我来说在概念上并不方便。此外,因为这是一个“鱼缸”,我将d_near设置为xy平面,其中z = 0。
在屏幕/世界空间中,我将屏幕中心分配为(0,0,0),将其4个角分配为: TL(-44.25, 25, 0) TR( 44.25, 25, 0) BR( 44.25,-25, 0) BL(-44.25,-25, 0) 这些值以厘米为单位,适用于16:9的显示器。
然后我使用POSIT计算用户的眼睛(实际上是我的脸上的网络摄像头),通常位于(+/-40, +/-40, 40-250)的范围内。我的POSIT方法很准确。
我正在定义自己的透视和观察变换矩阵,并使用着色器。
我初始化如下:
float right = 44.25;
float left = -44.25;
float top = 25.00;
float bottom = -25.00; 

vec3 eye = vec3(0.0, 0.0, 100.0);
vec3 view_dir = vec3(0.0, 0.0, -1.0);
vec3 up = vec3(0.0, 1.0, 0.0);
vec3 n = normalize(-view_dir);
vec3 u = normalize(cross(up, n)); 
vec3 v = normalize(cross(n, u));

float d_x = -(dot(eye, u));
float d_y = -(dot(eye, v));
float d_z = -(dot(eye, n));

float d_near = eye.z;
float d_far = d_near + 50;

// perspective transform matrix
mat4 P = mat4((2.0*d_near)/(right-left ), 0, (right+left)/(right-left), 0, 
            0, (2.0*d_near)/(top-bottom), (top+bottom)/(top-bottom), 0,
            0, 0, -(d_far+d_near)/(d_far-d_near), -(2.0*d_far*d_near)/(d_far-d_near),
            0, 0, -1.0, 0);

// viewing transform matrix
mat4 V = mat4(u.x, u.y, u.z, d_x,
              v.x, v.y, v.z, d_y,
              n.x, n.y, n.z, d_z,
              0.0, 0.0, 0.0, 1.0);

mat4 MV = C * V;
//MV = V;

从我在网上查找到的信息来看,我的view_dir和up应该保持不变。这意味着我只需要更新d_near和d_far以及d_x、d_y和d_z即可。我可以在glutIdleFunc( idle )中实现这个功能。

void idle (void) {  

    hBuffer->getFrame(hFrame);
    if (hFrame->goodH && hFrame->timeStamp != timeStamp) {
        timeStamp = hFrame->timeStamp;
        std::cout << "(" << hFrame->eye.x << ", " <<
                    hFrame->eye.y << ", " <<
                    hFrame->eye.z << ") \n";

        eye = vec3(hFrame->eye.x, hFrame->eye.y, hFrame->eye.z);

        d_near = eye.z;
        d_far = eye.z + 50;

        P = mat4((2.0*d_near)/(right-left), 0, (right+left)/(right-left), 0, 
                 0, (2.0*d_near)/(top-bottom), (top+bottom)/(top-bottom), 0,
                 0, 0, -(d_far+d_near)/(d_far-d_near), -(2.0*d_far*d_near)/(d_far-d_near),
                 0, 0, -1.0, 0);

        d_x = -(dot(eye, u));
        d_y = -(dot(eye, v));
        d_z = -(dot(eye, n));

        C = mat4(1.0, 0.0, 0.0, eye.x,
                 0.0, 1.0, 0.0, eye.y,
                 0.0, 0.0, 1.0, 0.0,
                 0.0, 0.0, 0.0, 1.0);

        V = mat4(u.x, u.y, u.z, d_x,
                 v.x, v.y, v.z, d_y,
                 n.x, n.y, n.z, d_z,
                 0.0, 0.0, 0.0, 1.0);

        MV = C * V;
        //MV = V;

        glutPostRedisplay();
    }
}

这是我的着色器代码:

#version 150

uniform mat4 MV;
uniform mat4 P;
in vec4 vPosition;
in vec4 vColor;
out vec4 color;

void 
main() 
{ 
    gl_Position = P * MV * vPosition;
    color = vColor;
}

好的,我已经对我的代码进行了一些更改,但是没有成功。当我在顶点着色器中使用V代替MV时,所有东西看起来都很正常,透视是正确的,物体的大小也是正确的,然而,场景被相机的位移转化。

当使用C和V获取MV时,我的场景从一个直接面向观察者的透视角度渲染,渲染的场景填充窗口,但是眼睛/相机的透视效果丢失了。

实际上,我想要的是通过适当的x和y值将2D像素即投影平面平移与眼睛/相机保持一致,以使对象的中心(其xy中心为(0,0))保持在渲染图像的中心。我参考了教科书“交互式计算机图形学:带有基于着色器的OpenGL(第6版)”中的示例。我正在继续使用该书配套的文件,该文件可在Web上免费获取,采用行主导方法。

以下图片是在不使用矩阵C创建MV时拍摄的。当我使用C创建MV时,所有场景都看起来像下面的第一张图片。我不希望在z轴上进行转换,因此将其设置为0。

由于投影平面和相机平面是平行的,因此从一个坐标系转换到另一个坐标系只需要进行平移并且inv(T) ~ -T。

这是我在(0,0,50)处眼睛的图片: Here is my image for the eye at (0,0,50):

这是我在(56,-16,50)处眼睛的图片: Here is my image for the eye at (56,-16,50):

2个回答

3
解决方法是更新d_x、d_y和d_z,考虑新的眼睛位置,但不要改变u、v或n。此外,必须根据摄像机/眼睛的新位置更新矩阵P中left、right、top和bottom的新值。
我使用以下初始化代码:
float screen_right = 44.25;
float screen_left = -44.25;
float screen_top = 25.00;
float screen_bottom = -25.00; 
float screen_front = 0.0;
float screen_back = -150;

现在我的闲置函数看起来像这样,注意顶部、底部、右侧和左侧的计算:

void idle (void) {  

hBuffer->getFrame(&hFrame);
if (hFrame.goodH && hFrame.timeStamp != timeStamp) {
    timeStamp = hFrame.timeStamp;
    //std::cout << "(" << hFrame.eye.x << ", " <<
    //                  hFrame.eye.y << ", " <<
    //                  hFrame.eye.z << ") \n";

    eye = vec3(hFrame.eye.x, hFrame.eye.y, hFrame.eye.z);

    d_near = eye.z;
    d_far = eye.z + abs(screen_back) + 1;

    float top = screen_top - eye.y;
    float bottom = top - 2*screen_top;
    float right = screen_right - eye.x;
    float left = right - 2*screen_right;

    P = mat4((2.0 * d_near)/(right - left ), 0.0, (right + left)/(right - left), 0.0, 
             0.0, (2.0 * d_near)/(top - bottom), (top + bottom)/(top - bottom), 0.0,
             0.0, 0.0, -(d_far + d_near)/(d_far - d_near), -(2.0 * d_far * d_near)/(d_far - d_near),
             0.0, 0.0, -1.0, 0.0);

    d_x = -(dot(eye, u));
    d_y = -(dot(eye, v));
    d_z = -(dot(eye, n));

    V = mat4(u.x, u.y, u.z, d_x,
             v.x, v.y, v.z, d_y,
             n.x, n.y, n.z, d_z,
             0.0, 0.0, 0.0, 1.0);

    glutPostRedisplay();
}

重新定义透视矩阵可以避免图像的平移。我仍然存在摄像头捕捉和屏幕抓取同步问题,但我能够实时创建用户位置更新后的以下图片:

enter image description here


项目的一些视频链接:背景结果 - Der Luftmensch

0

我找到的所有示例都要求相机/视角在原点

这不是一个要求。OpenGL没有定义相机,它归结为变换。这些通常称为投影和模型视图(模型和视图变换组合)。相机,即视图变换,只是相机在世界空间中定位的反转。因此,假设您为相机构建了一个矩阵C,则应该在模型视图上预乘其逆。

mat4 MV = inv(C) * V

顺便说一下,你的着色器代码是错误的:

gl_Position = P * ( (V * vPosition ) / vPosition.w );
                                    ^^^^^^^^^^^^^^

同质分割不应该在着色器中完成,因为它已经硬编码到渲染管线中。


@DerLuftmensch:在你的更新中,你写了 MV = C * V;我建议使用 inv(C) * MV。无论如何,你能否发布一些截图?另外,请说明你使用的数学库。你的矩阵似乎是按行主序编写的,这有点不寻常。 - datenwolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接