投影すること

projection.png

3次元のものを2次元に投影するってどういうことだろう?
3Dコンピュータグラフィックスの基本です。
projection射影ともいう
射影とは

物体に光を当ててその影を映すこと、またその影のこと

である
コンピュータのモニターは2次元の平面である。
なので、OpenGLによってレンダリングされる3Dの情景は
コンピュータースクリーンに投影されて最終的に2次元の絵にならなければならないのである。
もっと言えば、画面の幅×高さ個のピクセルの2次元配列にならないといけないのである。
カメラ=原点の状態であるカメラ座標系からクリップ座標系に変換する。
OpenGLの投影行列では
カメラ座標系クリップ座標系正規化デバイス座標までの変換をいっきにやってくれる。

カメラと比較

nearクリッピング面は、投影される面になる。つまり、焦点となる

用語の定義

用語 意味 座標系
ビュー平面 投影面。あなたが見てる画面のこと ウィンドウとも呼ばれる
VRP ビューリファレンス点 View Reference Point VRCの原点
VPN View Plane Normalビュー平面垂線。ビュー平面の法線のこと
VUP View Up Vector 表示ベクトル
VRC View Reference Coordinate 3次元ビューリファレンス座標
CW ウィンドウの中心
DOP Direction Of Projection 投影方向
PRP 投影の基準点 VRC座標系
透視投影 平行投影
投影中心 PRP(投影基準点)→CW(ウィンドウ中心) DOP

参考にした書籍

glProjectベタ書きバージョン

How to do picking in modern OpenGL without gluUnProject?

int glhProjectf(float objx, float objy, float objz, float *modelview, float *projection, int *viewport, float *windowCoordinate)
{
    //Transformation vectors
    float fTempo[8];
    //Modelview transform
    fTempo[0]=modelview[0]*objx+modelview[4]*objy+modelview[8]*objz+modelview[12];  //w is always 1
    fTempo[1]=modelview[1]*objx+modelview[5]*objy+modelview[9]*objz+modelview[13];
    fTempo[2]=modelview[2]*objx+modelview[6]*objy+modelview[10]*objz+modelview[14];
    fTempo[3]=modelview[3]*objx+modelview[7]*objy+modelview[11]*objz+modelview[15];
    //Projection transform, the final row of projection matrix is always [0 0 -1 0]
    //so we optimize for that.
    fTempo[4]=projection[0]*fTempo[0]+projection[4]*fTempo[1]+projection[8]*fTempo[2]+projection[12]*fTempo[3];
    fTempo[5]=projection[1]*fTempo[0]+projection[5]*fTempo[1]+projection[9]*fTempo[2]+projection[13]*fTempo[3];
    fTempo[6]=projection[2]*fTempo[0]+projection[6]*fTempo[1]+projection[10]*fTempo[2]+projection[14]*fTempo[3];
    fTempo[7]=-fTempo[2];
    //The result normalizes between -1 and 1
    if(fTempo[7]==0.0)        //The w value
    return 0;
    fTempo[7]=1.0/fTempo[7];
    //Perspective division
    fTempo[4]*=fTempo[7];
    fTempo[5]*=fTempo[7];
    fTempo[6]*=fTempo[7];
    //Window coordinates
    //Map x, y to range 0-1
    windowCoordinate[0]=(fTempo[4]*0.5+0.5)*viewport[2]+viewport[0];
    windowCoordinate[1]=(fTempo[5]*0.5+0.5)*viewport[3]+viewport[1];
    //This is only correct when glDepthRange(0.0, 1.0)
    windowCoordinate[2]=(1.0+fTempo[6])*0.5;  //Between 0 and 1
    return 1;
}
    /*!
    @param _device_pos デバイス座標
    @param _objZ 求めたいオブジェクト座標のz平面をどこにするかの指定
    */
    template <typename T>    
    vec3<T> unproject(T _deviceX,T _deviceY,T _objZ,const mat4<T>& _modelView,const mat4<T>& _proj,vec2<int>& _winsize,T _near,T _far){
        //デバイス座標→正規化デバイス座標
        vec4<T> normalized_device_pos;
        normalized_device_pos.x=_deviceX*2.0/(T)_winsize.x-1.0;
        normalized_device_pos.y=_deviceY*2.0/(T)_winsize.x-1.0;
        normalized_device_pos.z=(0.0-(_near+_far)*0.5)/(_far-_near);//deviceZ=0とみなした値
        //正規化デバイス座標→クリップ座標
        //wの値 投影行列の三列目は常に[0,0,-1,0]なので、結局モデルビューの3行目にマイナスかけたものになる
        T w=_modelView.m[3]*x+_modelView.m[7]*y+_modelView.m[11]*_objZ+_modelView[15];
        vec4<T> clip_pos;//クリップ座標での位置
        clip_pos.x=normalized_device_pos.x*w;
        clip_pos.y=normalized_device_pos.y*w;
        clip_pos.z=normalized_device_pos.z*w;
        clip_pos.w=w;
        vec4<T> view_pos;//視点が原点な座標系
        view_pos=_proj.inv()*clip_pos;
        vec4<T> obj_pos=_modelView.inv()*view_pos;
    }
Bibliography

viewing-pipeline

サポートサイト Wikidot.com viewing-pipeline