マウス・レイの計算 2013


やあ子どもたち。以前、数年前にマウスクリック・レイの計算についての記事を書いたが、GLSLの勉強やglmなど使いながら数年分の成長を見せたおじさんが再度送る、よりスマートなマウスクリックレイの実装を今日は子どもたちにお届けするぞ。
マウスクリックレイについての説明は上記リンクから辿れる前回の記事を見てほしい。(ところで、今後はマウスクリック・レイ改め、マウス・レイと呼ぶことにします。クリックした時だけ発生するわけではないので。)
さてコードの紹介に入ろう。(今時こういうmyコードの公開はgithubでやれよ!という話もあるがそこはどうか気にしないで欲しい)

// マウス・レイの計算 2013
//
//
// 引数説明:
// ●proj_mat: 
// 透視投影行列(glm::perspective/orthoなどにより作成)を指定。
// ●view_mat:
// ビューマトリックス(カメラのモデルマトリックスの逆変換)を指定。
// ●screen_width/height: 
// 対象画面(ビュー)のサイズ(単位:画面ピクセル)を指定。
// ●click_pos_x/y:
// マウスクリックが起きたビュー内における座標を指定。
// (処理系によってはY座標は上下反転する必要があります)
// ■ray_org/dir:
// 出力のマウス・レイ情報です。
//
//
// calc_mouse_ray_perspective ////////////////////
void lc_calc_mouse_ray_perspective(
                             const glm::mat4& proj_mat,
                             const glm::mat4& view_mat,
                             const int screen_width, const int screen_height,
                             const int click_pos_x, const int click_pos_y,
                             glm::vec3* ray_org, glm::vec3* ray_dir)
{
    const auto inv_view = glm::inverse( view_mat );
    glm::vec4 pos_nc;
    {
        const float xn = 2*click_pos_x/(float)screen_width -1;
        const float yn = 2*click_pos_y/(float)screen_height -1;
        pos_nc = glm::inverse( proj_mat ) * glm::vec4( xn, yn, -1, 1 );
        pos_nc = inv_view * glm::vec4( pos_nc / pos_nc.w );
    }
    const auto pos_org = inv_view * glm::vec4( 0,0,0,1);
    *ray_org = glm::vec3( pos_org );
    *ray_dir = glm::normalize( glm::vec3( pos_nc - pos_org ) );
    return;
}
//
//
// calc_mouse_ray_ortho ////////////////////
void lc_calc_mouse_ray_ortho(
                               const glm::mat4& proj_mat,
                               const glm::mat4& view_mat,
                               const int screen_width, const int screen_height,
                               const int click_pos_x, const int click_pos_y,
                               glm::vec3* ray_org, glm::vec3* ray_dir)
{
    const auto inv_view = glm::inverse( view_mat );
    glm::vec4 pos_org;
    {
        const float xn = 2*click_pos_x/(float)screen_width -1;
        const float yn = 2*click_pos_y/(float)screen_height -1;
        pos_org = glm::inverse( proj_mat ) * glm::vec4( xn, yn, -1, 1 );
        pos_org = inv_view * glm::vec4( pos_org / pos_org.w );
    }
    const auto pos_dir = inv_view * glm::vec4( 0,0,-1,0);
    *ray_org = glm::vec3( pos_org );
    *ray_dir = glm::normalize( glm::vec3( pos_dir ) );
    return;
}

まあglm::unprojectとかもあるけどな。zに何指定すればいいのかとか悩むのやだから自分なりにマウス・レイの計算をまとめて持っておきたい気分なんだなおじさんは。
じゃ、今日はこれでお別れだよ。上記コードを記述するために必要となるNDC(NormalizedDeviceCoordinates)やHCS(HomogeneousCoordinateSystem)に関する理解や、各投影行列の説明記事についてはまた今度だ。チャオ!