Oculus Rift + Point Sprites +点大小衰减

时间:2015-01-22 22:47:22

标签: c++ opengl oculus virtual-reality point-sprites

我正在使用Oculus Rfit支持编写一个小项目,我使用点精灵来渲染我的粒子。我根据它们与相机的距离来计算点像素的大小(以像素为单位)。在顶点着色器中。当在默认屏幕上绘图(而不是在Rift上)时,尺寸可以很好地工作,但是当我切换到Rift时我注意到这些现象:

  

左眼上的颗粒很小,很快就会缩小尺寸。   右眼上的颗粒很大,不会改变尺寸。

截图: 已禁用裂痕:http://i.imgur.com/EoguiF0.jpg 启用Rift:http://i.imgur.com/4IcBCf0.jpg

这是顶点着色器:

#version 120

attribute vec3 attr_pos;
attribute vec4 attr_col;
attribute float attr_size;

uniform mat4 st_view_matrix;
uniform mat4 st_proj_matrix;
uniform vec2 st_screen_size;

varying vec4 color;

void main()
{
    vec4 local_pos = vec4(attr_pos, 1.0);
    vec4 eye_pos = st_view_matrix * local_pos;
    vec4 proj_vector = st_proj_matrix * vec4(attr_size, 0.0, eye_pos.z, eye_pos.w);
    float proj_size = st_screen_size.x * proj_vector.x / proj_vector.w;

    gl_PointSize = proj_size;
    gl_Position = st_proj_matrix * eye_pos;

    color = attr_col;
}

st_screen_size uniform是视口的大小。因为我在Rift上渲染时使用单个frambuffer(每只眼睛一半),st_screen_size的值应该是(frabuffer_width / 2.0,frambuffer_height)。

这是我的平局电话:

    /*Drawing starts with a call to ovrHmd_BeginFrame.*/
    ovrHmd_BeginFrame(game::engine::ovr_data.hmd, 0);

    /*Start drawing onto our texture render target.*/
    game::engine::ovr_rtarg.bind();
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   //Update the particles.
    game::engine::nuc_manager->update(dt, get_msec());

    /*for each eye... */
    for(unsigned int i = 0 ; i < 2 ; i++){
        ovrEyeType eye = game::engine::ovr_data.hmd->EyeRenderOrder[i];
        /* -- Viewport Transformation --
         * Setup the viewport to draw in the left half of the framebuffer when we're
         * rendering the left eye's view (0, 0, width / 2.0, height), and in the right half
         * of the frambuffer for the right eye's view (width / 2.0, 0, width / 2.0, height)
         */
        int fb_width = game::engine::ovr_rtarg.get_fb_width();
        int fb_height = game::engine::ovr_rtarg.get_fb_height();

        glViewport(eye == ovrEye_Left ? 0 : fb_width / 2, 0, fb_width / 2, fb_height);

      //Send the Viewport size to the shader.
      set_unistate("st_screen_size", Vector2(fb_width /2.0 , fb_height));

        /* -- Projection Transformation --
         * We'll just have to use the projection matrix supplied but he oculus SDK for this eye.
         * Note that libovr matrices are the transpose of what OpenGL expects, so we have to
         * send the transposed ovr projection matrix to the shader.*/
        proj = ovrMatrix4f_Projection(game::engine::ovr_data.hmd->DefaultEyeFov[eye], 0.01, 40000.0, true);

      Matrix4x4 proj_mat;
      memcpy(proj_mat[0], proj.M, 16 * sizeof(float));

      //Send the Projection matrix to the shader.
      set_projection_matrix(proj_mat);

        /* --view/camera tranformation --
         * We need to construct a view matrix by combining all the information provided by
         * the oculus SDK, about the position and orientation of the user's head in the world.
         */
         pose[eye] = ovrHmd_GetHmdPosePerEye(game::engine::ovr_data.hmd, eye);

         camera->reset_identity();

         camera->translate(Vector3(game::engine::ovr_data.eye_rdesc[eye].HmdToEyeViewOffset.x,
          game::engine::ovr_data.eye_rdesc[eye].HmdToEyeViewOffset.y,
          game::engine::ovr_data.eye_rdesc[eye].HmdToEyeViewOffset.z));

         /*Construct a quaternion from the data of the oculus SDK and rotate the view matrix*/
         Quaternion q = Quaternion(pose[eye].Orientation.w, pose[eye].Orientation.x,
                                   pose[eye].Orientation.y, pose[eye].Orientation.z);
         camera->rotate(q.inverse().normalized());


         /*Translate the view matrix with the positional tracking*/
         camera->translate(Vector3(-pose[eye].Position.x, -pose[eye].Position.y, -pose[eye].Position.z));

       camera->rotate(Vector3(0, 1, 0), DEG_TO_RAD(theta));

       //Send the View matrix to the shader.
       set_view_matrix(*camera);



         game::engine::active_stage->render(STAGE_RENDER_SKY | STAGE_RENDER_SCENES | STAGE_RENDER_GUNS |
          STAGE_RENDER_ENEMIES | STAGE_RENDER_PROJECTILES, get_msec());
         game::engine::nuc_manager->render(RENDER_PSYS, get_msec());
       game::engine::active_stage->render(STAGE_RENDER_COCKPIT, get_msec());
    }

    /* After drawing both eyes into the texture render target, revert to drawing directly to the display,
     * and we call ovrHmd_EndFrame, to let the Oculus SDK draw both images properly, compensated for lens
     * distortion and chromatic abberation onto the HMD screen.
     */
    game::engine::ovr_rtarg.unbind();

    ovrHmd_EndFrame(game::engine::ovr_data.hmd, pose, &game::engine::ovr_data.fb_ovr_tex[0].Texture);

这个问题困扰了我很多天......我觉得我已经走到了尽头。我可以使用广告牌四边形.....但我不想轻易放弃:)加点精灵更快。 在Rift上渲染时,基于距离变化来计算点大小衰减吗? 我不考虑一些事情吗? 数学不是(至少)我最强的观点。 :)非常感谢任何见解!

PS:如果我发布的代码需要任何其他信息,我很乐意提供。

2 个答案:

答案 0 :(得分:1)

我可以推荐一些故障排除技巧。

首先,修改你的代码以自动编写渲染的第一帧的截图(或者如果这不方便,只需要一个静态布尔值,导致主绘制跳过除开始/结束帧之外的所有内容在第一次运行之后调用.SDK有时会搞乱OpenGL状态机,如果发生这种情况,那么你所看到的可能是由于在ovrHmd_EndFrame()中完成的工作搞砸了你的渲染在渲染循环中的后续遍历中。渲染代码中的其他内容(在粒子渲染之后)可能无意中恢复了所需的状态,这就是为什么第二只眼睛渲染看起来很好。

其次,我会尝试将渲染的眼睛分成两个帧缓冲区。也许你的代码中有一些东西出乎意料地对整个帧缓冲区(比如清除深度缓冲区)做了一些导致差异的东西。根据您的顶级代码,您可能会在帧缓冲区中使用与您预期不同的状态。分成两个帧缓冲区将告诉你是否是这种情况。

您可能运行的另一个测试,类似于第二个测试是重构您的渲染代码,以允许您使用默认的帧缓冲区传递此循环,并且不使用Oculus SDK调用。这是另一种技术,可以帮助您确定问题是在SDK中还是在您自己的渲染代码中。只需将两个眼睛视图渲染到屏幕的两半,而不是两半的屏幕外帧缓冲区。

答案 1 :(得分:0)

  

vec4 local_pos = vec4(attr_pos, 1.0);
vec4 eye_pos = st_view_matrix * local_pos;
vec4 proj_voxel = st_proj_matrix * vec4(attr_size, 0.0, eye_pos.z, eye_pos.w);
float proj_size = st_screen_size.x * proj_voxel.x / proj_voxel.w;

gl_PointSize = proj_size;

基本上,您首先将您的观点转换为视图空间,以便在视图空间中找出它的Z坐标(距观察者的距离),然后您构建一个与X轴对齐的矢量粒子大小,并投影以查看它在投影和视口转换时所覆盖的像素数(sortof)。

这是完全合理的,假设您的投影矩阵是对称的。在处理裂痕时,这种假设是错误的。我已经绘制了一个图表来更好地说明问题:

http://i.imgur.com/vm33JUN.jpg

正如你所看到的,当截锥体是非对称的,这对于裂缝来说肯定是这种情况,使用距离屏幕中心的投影点的距离将为每只眼睛提供截然不同的值,当然也不同于&#34;正确&#34;您正在寻找的投影尺寸。

你必须做的是投射两个点,比如说(0,0,z,1)和(attr_size,0,z,1),使用相同的方法,并计算它们在屏幕空间上的差异(投影后) ,透视划分和视口)。