カメラ変換


まず始めに、このプログラムにはウソが含まれているかもしれません。
使用する場合は自己責任でお願いします

基本

3次元ベクトルの構造体
struct FVECTOR3{
    float x,y,z;
};
ベクトル基本関数
Normalize() 正規化
Cross(const FVECTOR3 &n); 外積計算


4x4マトリクスの構造体
struct FMATRIX{
    float m[4][4];
};

マトリクス基本関数
Identity() 単位行列を代入
Invert(); 逆行列を求める

一般的なカメラの構造体
struct EXP_CAMERA{
    FVECTOR3 dir;
    FVECTOR3 up;
    FVECTOR3 pos;
    float dist;
    float fov;
    float nearz;
    float farz;
    int ortho;
};
dir 前方向
up 上方向
pos 位置
dist 注視点までの距離
fov 画角
nearz ニア面
farz ファー面
ortho 1で平行投影


メタセコカメラの構造体
struct MQOSCENE{
    FVECTOR3 pos;
    FVECTOR3 lookat;
    float pich;
    float head;
    float zoom2;
    int ortho;
};
pos 位置
lookat 注視点
pich ピッチ
head ヘッド
zoom2 ズーム
ortho 1で平行投影


ビューマトリクス、プロジェクションマトリクス





メタセコのカメラ→マトリクス変換
/*=============================================================
;    マトリクス3x3にピッチ、ヘッド回転設定
;    x,y軸回転(pich,head)
=============================================================*/
static void SetRotXY(FMATRIX &mt,float rx,float ry){
    float sx,cx,sy,cy;
    sx=sinf(rx);
    cx=cosf(rx);
    sy=sinf(ry);
    cy=cosf(ry);
    mt.m[0][0]= cy;
    mt.m[0][1]= sy*sx;
    mt.m[0][2]= sy*cx;
    mt.m[1][0]= 0.0f;
    mt.m[1][1]= cx;
    mt.m[1][2]=-sx;
    mt.m[2][0]= sy;
    mt.m[2][1]=-cy*sx;
    mt.m[2][2]=-cy*cx;
}
/*=============================================================
;    メタセコカメラからマトリクスへ変換
;    in)     mqoscene      MQOシーンパラメータ
;            ap            アスペクト比
;    ret)    vmt           ビューマトリクス
;            pmt           プロジェクションマトリクス
=============================================================*/
void ConvertMQCamera_ViewPers(FMATRIX &vmt,FMATRIX &pmt,const MQOSCENE *mqoscene,float ap){
    FVECTOR pos   =mqoscene->pos;
    FVECTOR lookat=mqoscene->lookat;
    float pich    =mqoscene->pich;
    float head    =mqoscene->head;
    float zoom2   =mqoscene->zoom2;
    int ortho     =mqoscene->ortho;
    float dist    =pos.z;                    //距離
    float nearz   =dist*0.15f;                //ニア面
    float farz    =dist*(4.0f*PI);            //ファー面
    //vmt,pmt初期化
    vmt.Identity();
    pmt.Identity();
    //ピッチ、ヘッド回転設定
    SetRotXY(vmt,pich,head);
    //視点、注視点設定
    FVECTOR v;
    v=vmt*(-lookat)+pos;
    vmt.m[3][0]=v.x;
    vmt.m[3][1]=v.y;
    vmt.m[3][2]=v.z;
    //FOV計算
    const float fov_scl=0.001f;                //FOV倍率
    float ww=fov_scl*zoom2;
    float hh=ww*ap;
    if(ortho==0){
    //Persの場合
        pmt.m[0][0]=ww*dist;
        pmt.m[1][1]=hh*dist;
        pmt.m[2][2]=1.0f;
        pmt.m[3][2]=-nearz;
        pmt.m[2][3]=1.0f;
    }else{
    //Orthoの場合
        pmt.m[0][0]=ww;
        pmt.m[1][1]=hh;
        pmt.m[2][2]=1.0f/farz;
        pmt.m[3][2]=0.0f;
        pmt.m[2][3]=0.0f;
    }
}

マトリクス→メタセコのカメラ変換
/*=============================================================
;    マトリクスからメタセコカメラへ変換
;    in)     vmt           ビューマトリクス
;            pmt           プロジェクションマトリクス
;    ret)    mqoscene      MQOシーンパラメータ
=============================================================*/
void ConvertViewPers_MQCamera(MQOSCENE *mqoscene,const FMATRIX &vmt,const FMATRIX &pmt){
    FVECTOR pos,lookat;
    float dist,nearz,farz,zoom2,fov,pich,head;
    int ortho;
    const float fov_scl=0.001f;                //FOV倍率
    //FOV計算
    float fov_ww=fabs(pmt.m[0][0]);
    float fov_hh=fabs(pmt.m[1][1]);
    if(fov_ww>fov_hh){
        fov=fov_ww;
    }else{
        fov=fov_hh;
    }
    //方向計算
    pich=asinf(-vmt.m[1][2]);
    head=asinf( vmt.m[2][0]);
    //
    if(pmt.m[2][3]!=0.0f){
    //pers
        ortho=0;
        nearz=-pmt.m[3][2];
        dist =nearz/0.15f;
        farz =dist*(4.0f*PI);
        zoom2=fov/(dist*fov_scl);
    }else{
    //ortho
        ortho=1;
        farz=1.0f/pmt.m[2][2];
        dist=farz/(4.0f*PI);
        nearz=dist*0.15f;
        zoom2=fov/fov_scl;
    }
    //位置計算
    lookat.x=0;
    lookat.y=0;
    lookat.z=-(vmt.m[3][2]-dist)/vmt.m[2][2];
    pos.x=vmt.m[3][0]-(vmt.m[0][2]*(-lookat.z));
    pos.y=vmt.m[3][1]-(vmt.m[1][2]*(-lookat.z));
    pos.z=vmt.m[3][2]-(vmt.m[2][2]*(-lookat.z));
    //
    mqoscene->pos=pos;
    mqoscene->lookat=lookat;
    mqoscene->zoom2=zoom2;
    mqoscene->head =head;
    mqoscene->pich =pich;
    mqoscene->ortho=ortho;
}

一般的なカメラ→マトリクス変換
/*=============================================================
;    一般的なカメラからマトリクスへ変換
;    in)     exp_camera    一般的なカメラ
;            ap            アスペクト比
;    ret)    vmt           ビューマトリクス
;            pmt           プロジェクションマトリクス
=============================================================*/
void ConvertExpCamera_ViewPers(FMATRIX &vmt,FMATRIX &pmt,IEXP_CAMERA *exp_camera,float ap){
    FVECTOR3 zd =exp_camera->dir;
    FVECTOR3 yd =exp_camera->up;
    FVECTOR3 pos=exp_camera->pos;
    float fov   =exp_camera->fov;
    float dist  =exp_camera->dist;
    float nearz =exp_camera->nearz;
    float farz  =exp_camera->farz;
    int ortho   =exp_camera->ortho;
    //方向設定
    vmt.Identity();
    pmt.Identity();
    vmt.m[0][2]=zd.x;
    vmt.m[1][2]=zd.y;
    vmt.m[2][2]=zd.z;
    FVECTOR xd=zd.Cross(yd);
    xd.Normalize();
    yd=xd.Cross(zd);
    yd.Normalize();
    vmt.m[0][1]=yd.x;
    vmt.m[1][1]=yd.y;
    vmt.m[2][1]=yd.z;
    vmt.m[0][0]=xd.x;
    vmt.m[1][0]=xd.y;
    vmt.m[2][0]=xd.z;
    //位置設定
    float x=-pos.x;
    float y=-pos.y;
    float z=-pos.z;
    vmt.m[3][0]=xd.x*x+xd.y*y+xd.z*z;
    vmt.m[3][1]=yd.x*x+yd.y*y+yd.z*z;
    vmt.m[3][2]=zd.x*x+zd.y*y+zd.z*z;
    //FOV設定
    float fov_w=fov;
    float fov_h=fov*ap;
    float Q=1.0f;
    float w,h;
    w=1.0f/tanf(fov_w*0.5f);
    h=1.0f/tanf(fov_h*0.5f);
    if(ortho==0){
    //Pers
        pmt.m[0][0]= w;
        pmt.m[1][1]= h;
        pmt.m[2][2]= Q;
        pmt.m[3][2]=-Q*nearz;
        pmt.m[2][3]=1.0f;
    }else{
    //Ortho
        pmt.m[0][0]= w/dist;
        pmt.m[1][1]= h/dist;
        pmt.m[2][2]=1.0f/farz;
        pmt.m[3][2]=0.0f;
        pmt.m[2][3]=0.0f;
    }
}

マトリクス→一般的なカメラ変換
/*=============================================================
;    マトリクスから一般的なカメラへ変換
;    in)     vmt           ビューマトリクス
;            pmt           プロジェクションマトリクス
;    ret)    exp_camera    一般的なカメラ
=============================================================*/
void ConvertViewPers_ExpCamera(EXP_CAMERA *exp_camera,const FMATRIX &vmt,const FMATRIX &pmt){
    FVECTOR pos,zd,yd;
    float dist;
    //位置計算
    pos.x= ( vmt.m[1][0]*vmt.m[2][1]*vmt.m[3][2]
            +vmt.m[1][1]*vmt.m[2][2]*vmt.m[3][0]
            +vmt.m[1][2]*vmt.m[2][0]*vmt.m[3][1]
            -vmt.m[1][0]*vmt.m[2][2]*vmt.m[3][1]
            -vmt.m[1][1]*vmt.m[2][0]*vmt.m[3][2]
            -vmt.m[1][2]*vmt.m[2][1]*vmt.m[3][0]);
    pos.y=-( vmt.m[0][0]*vmt.m[2][1]*vmt.m[3][2]
            +vmt.m[0][1]*vmt.m[2][2]*vmt.m[3][0]
            +vmt.m[0][2]*vmt.m[2][0]*vmt.m[3][1]
            -vmt.m[0][0]*vmt.m[2][2]*vmt.m[3][1]
            -vmt.m[0][1]*vmt.m[2][0]*vmt.m[3][2]
            -vmt.m[0][2]*vmt.m[2][1]*vmt.m[3][0]);
    pos.z= ( vmt.m[0][0]*vmt.m[1][1]*vmt.m[3][2]
            +vmt.m[0][1]*vmt.m[1][2]*vmt.m[3][0]
            +vmt.m[0][2]*vmt.m[1][0]*vmt.m[3][1]
            -vmt.m[0][0]*vmt.m[1][2]*vmt.m[3][1]
            -vmt.m[0][1]*vmt.m[1][0]*vmt.m[3][2]
            -vmt.m[0][2]*vmt.m[1][1]*vmt.m[3][0]);
    //方向計算
    zd.x =vmt.m[0][2];
    zd.y =vmt.m[1][2];
    zd.z =vmt.m[2][2];
    yd.x =vmt.m[0][1];
    yd.y =vmt.m[1][1];
    yd.z =vmt.m[2][1];
    zd.Normalize();
    yd.Normalize();
    //距離
    dist=vmt.m[3][2];
    //
    exp_camera->pos =pos;
    exp_camera->dir =zd;
    exp_camera->up  =yd;
    exp_camera->dist=dist;
    //
    if(pmt.m[2][3]!=0.0f){
    //Pers
        float w=pmt.m[0][0];
        float h=pmt.m[1][1];
        float fov_w=atanf(1.0f/w)*2.0f;
        float fov_h=atanf(1.0f/h)*2.0f;
        exp_camera->ortho=0;
        exp_camera->fov  =fov_w;
        exp_camera->nearz=-pmt.m[3][2]/pmt.m[2][2];
    }else{
    //Ortho
        float w=pmt.m[0][0]*dist;
        float h=pmt.m[1][1]*dist;
        float fov_w=atanf(1.0f/w)*2.0f;
        float fov_h=atanf(1.0f/h)*2.0f;
        exp_camera->ortho=1;
        exp_camera->fov  =fov_w;
        exp_camera->farz =1.0f/pmt.m[2][2];
    }
}