1

Oculus Rfit をサポートする小さなプロジェクトをコーディングしています。ポイント スプライトを使用してパーティクルをレンダリングしています。頂点シェーダーの「カメラ」からの距離に基づいて、ポイント スプライトのサイズをピクセル単位で計算します。デフォルトの画面 (Rift ではなく) で描画すると、サイズは完全に機能しますが、Rift に切り替えると、次の現象に気付きます。

左目の粒子は小さく、非常に急速にサイズが小さくなります。右目のパーティクルは巨大で、サイズは変わりません。

スクリーンショット: リフト無効: http://i.imgur.com/EoguiF0.jpg リフト有効: 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 ユニフォームはビューポートのサイズです。Rift でレンダリングするときに frambuffer を 1 つ使用しているため (片目ごとに 1 つの半分)、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: 投稿したコードについて追加情報が必要な場合は、喜んで提供します。

4

2 に答える 2

1

いくつかのトラブルシューティング手法をお勧めします。

最初に、レンダリングされた最初のフレームのスクリーンショットを自動的に書き込むようにコードを変更します (または、それが不便な場合は、最初の実行後に開始/終了フレーム呼び出し以外のすべてをメイン ドローでスキップさせる静的ブール値を用意します。 SDK は OpenGL ステート マシンを台無しにすることがありますが、それが発生している場合、表示されているのはおそらく ovrHmd_EndFrame() で行われた作業の結果であり、レンダリング ループを介した後続のパスでレンダリングを台無しにします。コード (パーティクル レンダリングに続く) は、誤って目的の状態を復元している可能性があります。これが、レンダリングされた 2 番目の目がきれいに見える理由です。

次に、レンダリングされた目を 2 つのフレームバッファに分割してみます。おそらく、違いを引き起こしているコードに、フレームバッファー全体に対して予期せず何かを行っているもの (深度バッファーをクリアするなど) が含まれている可能性があります。トップレベルのコードに基づく予想とは異なるフレームバッファの状態で、2 つ目の目を実行している可能性があります。2 つのフレーム バッファに分割すると、そうであるかどうかがわかります。

実行できる別のテストは、2 番目のテストと同様に、レンダリング コードをリファクタリングして、デフォルトのフレームバッファを使用し、Oculus SDK 呼び出しなしでこのループを通過できるようにすることです。これは、問題が SDK にあるのか、独自のレンダリング コードにあるのかを判断するのに役立つもう 1 つの手法です。オフスクリーン フレームバッファの 2 つの半分ではなく、2 つの目のビューを画面の 2 つの半分にレンダリングするだけです。

于 2015-01-23T00:46:40.227 に答える
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 軸に沿ったベクトルを目的の粒子サイズで構築し、それを投影してどのように表示するかを確認します。投影およびビューポート変換 (並べ替え) 時にカバーする多くのピクセル。

射影行列が対称であると仮定すると、これは完全に合理的です。裂け目を扱うとき、この仮定は間違っています。問題をよりよく説明するために図を描きました。

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

ご覧のように、錐台が非対称である場合 (確かに裂け目の場合)、画面の中心から投影された点までの距離を使用すると、それぞれの目に対して大きく異なる値が得られ、「正しい」値とは確実に異なります。 「お探しの投影サイズ。

代わりに、同じ方法を使用して (0, 0, z, 1) AND (attr_size, 0, z, 1) などの 2 つのポイントを投影し、スクリーン スペースの差を計算します (投影後、パースペクティブ分割、およびビューポート)。

于 2015-01-24T02:09:40.787 に答える