The jonki

呼ばれて飛び出てじょじょじょじょーんき

【openFrameworks】透視投影と平行投影

画像を回転させると場所によって見え方が違う、というのもopenFrameworksだとデフォルトで透視投影だから当たり前ですね。3Dグラフィックスではなく、普通のボタンとかのようなUIに関しては平行投影が便利です。試しに比較アプリを作ったので動画を見てみてください。見え方が場所によってことなるとき(透視投影)と均一のとき(平行投影)が切り替わる様子がわかるかと思います。


それではopenFrameworksのアプリケーション上で2つの比較プログラムを見ていきます。oFだとofCameraクラスが平行投影に関するenableOrthoとかいう関数を持っているのでそれを叩いてやれば動くようになります。簡単ですね。ofCamera内で呼んでいるglOrthoがOpenGLの座標軸想定なのでy軸を180度反転させる必要があります。

testApp.h

//
// 中略
//
private:
    ofCamera    m_cam;     
    ofImage     m_img;      //!< テスト画像
    int         m_num;      //!< 行・列のそれぞれ画像数
    int         m_proj_type;
    enum PROJECTION_TYPE{
      DEFAULT = 0, //!< 透視投影
      ORTHO   = 1, //!< 平行投射
    };

testApp.cpp

//--------------------------------------------------------------
void testApp::setup(){
    ofSetRectMode(OF_RECTMODE_CENTER); //!< 画像の中心で回転するようにする
    ofBackground(ofColor(0, 0, 0));
    
	//!< Orthographic projection settings
	m_cam.setNearClip(-100.f);
	m_cam.setFarClip(100.f);
	m_cam.enableOrtho();
	m_cam.setPosition(0, 0, 0);
    
    m_img.loadImage("oden.jpg");
    m_num = 10;
    m_proj_type = DEFAULT;
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
    int sw = ofGetWidth();
    int sh = ofGetHeight();
    
    int img_w = 60;
    int img_h = 60;
    
    float rotX = (sin(ofGetElapsedTimef())+1.f)/2.f * 360;
    ofSetColor(255, 255, 255);
    switch (m_proj_type) {
        case DEFAULT:
            for(int i=0; i<m_num; i++) {
                for(int j=0; j<m_num; j++) {
                    ofPushMatrix();
                        ofTranslate(img_w, img_h);
                        ofTranslate(i*ofGetWidth()/m_num, j*ofGetHeight()/m_num);
                        ofRotateX(rotX); //!< 画像の回転
                        m_img.draw(0, 0, img_w, img_h);
                    ofPopMatrix();
                }
            }
            ofSetColor(255, 0, 0);
            ofDrawBitmapString("Perspective projection", 100, 100);
            break;
            
        case ORTHO:
            m_cam.begin();
            
            //!< OpenGLの座標軸で書かれているためy軸を180度反転する必要がある
            ofPushMatrix();
            ofTranslate(sw/2, sh/2, 0);
            glRotatef(180.f, 0.f, 1.f, 0.f);
            glRotatef(180.f, 0.f, 0.f, 1.f);
            ofTranslate(-sw/2, -sh/2, 0);
            
            for(int i=0; i<m_num; i++) {
                for(int j=0; j<m_num; j++) {
                    ofPushMatrix();
                        ofTranslate(img_w, img_h);
                        ofTranslate(i*ofGetWidth()/m_num, j*ofGetHeight()/m_num);
                        ofRotateX(rotX); //!< 画像の回転
                        m_img.draw(0, 0, img_w, img_h);
                    ofPopMatrix();
                }
            }
            ofPopMatrix();
            m_cam.end();
            ofSetColor(255, 0, 255);
            ofDrawBitmapString("Orthogonal projection", 100, 100);
            break;
            
        default:
            break;
    }
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){
    switch (key) {
        case 'a':
            m_proj_type = DEFAULT;
            break;
        case 'b':
            m_proj_type = ORTHO;
            break;
            
        default:
            break;
    }
}