草庐IT

c++ - 用于桌面 VR 的 OpenGL 非对称截锥体

coder 2024-02-22 原文

我正在制作一个 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) 对于 16x9 显示器,这些值以厘米为单位。

然后,我使用 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 及更高版本将保持不变。这意味着我只需要更新 d_near 和 d_far 以及 d_x、d_y 和 d_y?我在 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 版)的自顶向下方法”中的示例为指导。使用网络上免费提供的与本书配对的文件,我将继续使用行主要方法。

以下图像是在不使用矩阵C创建MV时拍摄的。当我确实使用 C 创建 MV 时,所有场景看起来都像下面的第一张图片。我不希望在 z 中进行翻译,因此我将其保留为 0。

因为投影平面和我的相机平面是平行的,所以从一个坐标系到另一个坐标系的转换只是简单的平移和inv(T) ~ -T。

这是我在 (0,0,50) 处的眼睛图像:

这是我在 (56,-16,50) 时的眼睛图像:

最佳答案

解决方案是更新 d_x、d_y 和 d_z,考虑新的眼睛位置,但永远不要改变 u、v 或 n。此外,必须用左、右、上和下的新值更新矩阵 P,因为它们与相机/眼睛的新位置有关。

我用这个初始化:

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

}

透视矩阵的这种重新定义可防止渲染图像发生平移。我仍然有相机捕捉和屏幕抓取同步问题,但我能够创建如下图像,为用户的位置实时更新:

关于c++ - 用于桌面 VR 的 OpenGL 非对称截锥体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16071984/

有关c++ - 用于桌面 VR 的 OpenGL 非对称截锥体的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  4. ruby - inverse_of 是否适用于 has_many? - 2

    当我使用has_one时,它​​工作得很好,但在has_many上却不行。在这里您可以看到object_id不同,因为它运行了另一个SQL来再次获取它。ruby-1.9.2-p290:001>e=Employee.create(name:'rafael',active:false)ruby-1.9.2-p290:002>b=Badge.create(number:1,employee:e)ruby-1.9.2-p290:003>a=Address.create(street:"123MarketSt",city:"SanDiego",employee:e)ruby-1.9.2-p290

  5. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  6. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  7. ruby - "undefined method"用于 rails 模型 - 2

    我正在使用带有Rails的Devise,我想添加一个方法“getAllComments”,所以我这样写:classUser在我的Controller中:defdashboard@user=current_user@comments=@user.getAllComments();end当我访问我的url时,我得到了undefinedmethod`getAllComments'for#我做错了什么?谢谢 最佳答案 因为getAllComments是一个类方法,而您正试图将其作为实例方法访问。您要么需要访问它:User.getAllCom

  8. Ruby on Rails regexp equals-tilde 与 array include 用于检查选项列表 - 2

    我正在使用Rails3.2.3和Ruby1.9.3p0。我发现我经常需要确定某个字符串是否出现在选项列表中。看来我可以使用Ruby数组.includemethod:或正则表达式equals-tildematchshorthand用竖线分隔选项:就性能而言,一个比另一个好吗?还有更好的方法吗? 最佳答案 总结:Array#include?包含String元素,在接受和拒绝输入时均胜出,对于您的示例只有三个可接受的值。对于要检查的更大的集合,看起来Set#include?和String元素可能会获胜。如何测试我们应该根据经验对此进行测试

  9. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  10. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

随机推荐