【OpenGL編程】攝像機漫遊

作者:憨豆酒(YinDou),聯繫我[email protected],熟悉圖形學,圖像處理領域,本章的源代碼可在此倉庫中找到: https://github.com/douysu/person-summary 如果大家發現錯誤以及不合理之處,還希望多多指出。

OpenGL/ES攝像機漫遊

說在前面:

之前的學習當中只是實現了簡單的XZ平面的攝像機旋轉,對於三維空間的旋轉還需要學習一些新的知識,主要涉及兩方面的知識:1、歐拉角的概念2、三維空間的數學計算。
本節內容主要參考:
LearnOpenGL的攝像機一章中的歐拉角的概念。
https://learnopengl-cn.github.io/01%20Getting%20started/09%20Camera/#_1

什麼是攝像機漫遊?

已下gif實現的即爲三維空間的漫遊。
這裏寫圖片描述

什麼是歐拉角?(借鑑於LearnOpenGL)

歐拉角使可以表示三維空間中任何旋轉的三個值,一共有三種歐拉角,俯仰角,偏航角,和滾轉角。下面的圖片展示了它們的含義:
這裏寫圖片描述

  • 俯仰角(仰視角):繞X軸旋轉的角,在OpenGL/ES中通常表示往上看或者往下看的角,將攝像機抽象成人的頭代表的也就是人的仰視角。
  • 偏航角(左右角):繞Y旋轉的角,在OpenGL/ES中通常表示往左或者往右看的角,代表人的頭部左右旋轉。
  • 滾轉角:繞Z軸旋轉的角,在OpenGL/ES中通常表示可以360度旋轉的角,無法代表人的頭部,因爲人的頭部是無法實現旋轉360度的,通常情況下不會使用使用滾轉角,除非有特許需求。

一個攝像機漫遊例子

在介紹漫遊之前,我們還需要了解設置攝像機的方法,在OpenGL/ES中,爲了方便和簡化攝像機矩陣,我們會設置攝像機相關的3個因素:攝像機位置,目標位置(眼睛的觀察點),和與人頭部保持一個方向的向量(通常叫它UP向量),當然,這些位置向量都是在世界座標系考慮的。通過這三個因素去生成攝像機矩陣(關於生成攝像機矩陣的相關內容,可以參考LearnOpenGL中的章節),下圖比較直觀的表示了攝像機的三個因素。
這裏寫圖片描述

攝像機三維旋轉部分

先給出一段限制俯仰角和偏航角的方法,在這裏傳入的兩個參數分別是y方向的偏移距離(手指豎着划動的距離)和x方向的偏移距離(手指橫着划動的距離)。在這裏,我限制了俯仰角爲090度。偏航角爲0360度。

/*
 * @param yjSpan y方向的偏移距離
 * @param cxSpan x方向的偏移距離
 * */
void CameraUtil::calCamera(float yjSpan, float cxSpan) {
    //限制俯仰角度
    yj = yj + yjSpan;
    if(yj>=90){
        yj=90;
    }if(yj<=0){
        yj=0;
    }
    //限制偏航角角度
    degree = degree + cxSpan;
    if (degree >= 360) {
        degree = degree - 360;
    }
    else if (degree <= 0) {
        degree = degree + 360;
    }
    calCamera();//重新計算攝像機姿態的方法
}

我繪製了相關三維圖像方便理解。首先是動態計算攝像機的位置cx,cy,cz,圖像如下。爲了方便理解,我將目標點放到了原點。攝像機是在以目標點爲中心,半徑爲R的球面上運動的,我繪製的只是某一個狀態,不過這也足夠了。接着就是對位置的分解了,我相信這難不倒大家
這裏寫圖片描述
這就是動態計算攝像機位置的代碼部分,其中涉及到角度轉弧度的知識,我便不再介紹了。這裏便是通過編程實現上面的簡單算法了。

    //計算當前觀察角度下攝像機的位置(基於俯仰角重新計算 X軸爲旋轉軸)
     cy = float(sin(yj*3.1415926535898 / 180)*CAMERA_R + ty);
    float cxz = float(cos(yj*3.1415926535898 / 180)*CAMERA_R);
    //(基於偏航角重新計算攝像機位置 Y軸爲旋轉軸)
     cx = float(sin(degree*3.1415926535898 / 180)*cxz + tx);
     cz = float(cos(degree*3.1415926535898 / 180)*cxz + tz);

然後就是動態計算UP向量的部分了,這與計算攝像機位置的部分大同小異,我就不再進行分解了,幾何圖如下。
這裏寫圖片描述
計算UP向量的代碼。

//計算當前攝像機的UP向量
    float upY = float(cos(yj*3.1415926535898 / 180));
    float upXZ = float(sin(yj*3.1415926535898 / 180));
    float upX = float(-upXZ*sin(degree*3.1415926535898 / 180));
    float upZ = float(-upXZ*cos(degree*3.1415926535898 / 180));

攝像機三維移動部分

通過以上我繪製的幾何圖相信已經很好理解整個過程了,下面是攝像機前後所有移動的相關幾何圖。
這裏寫圖片描述
通過上面的幾何分解就可得到移動的距離了,非常簡單、我直接給出攝像機前後左右的移動代碼。

/*
 * @param goBack 前進或者後退的距離
 * @param leftRight 左右移動的距離
 * */
void CameraUtil::cameraGo(float goBack, float leftRight){
	//計算X方向位移
    float xStep = float(-goBack*sin(degree*3.1415926535898 / 180) - leftRight*sin((degree + 90)*3.1415926535898 / 180)); 
	//計算Z方向位移
    float zStep = float(-goBack*cos(degree*3.1415926535898 / 180) - leftRight*cos((degree + 90)*3.1415926535898 / 180)); 
    tx = tx + xStep;
    tz = tz + zStep;
    calCamera();
}

最後是應用的代碼啦,屏幕上X,Y方向的偏移量的大小與俯仰角和偏航角的大小是成正比的,這裏這是計算了X,Y兩個放向的偏移量,並傳給了攝像機類。如下

				int x=AMotionEvent_getRawX(event,0);
				int y=AMotionEvent_getRawY(event,0);
				int32_t id = AMotionEvent_getAction(event);
				switch(id){
					case AMOTION_EVENT_ACTION_DOWN://按下事件
						xPre=x;
						yPre=y;
						isClick = true;
					break;
					case AMOTION_EVENT_ACTION_MOVE://滑動事件
						xDis=x-xPre;
						yDis=y-yPre;
						if (myabs(xDis)>5 || myabs(yDis)>5){//判斷觸控點位移是否超過閾值
							isClick = false;
						}
						if (!isClick){
							CameraUtil::calCamera(yDis*180.0f / 600, xDis*180.0f / 600);
						}
						xPre=x;
						yPre=y;
					break;
					case AMOTION_EVENT_ACTION_UP://擡起事件
						#define MOVE_SPAN 10
						if (isClick){
							if (x < MyVulkanManager::screenWidth / 4){//左移
								CameraUtil::cameraGo(0, MOVE_SPAN);
							}
							else if (x > MyVulkanManager::screenWidth * 3 / 4){//右移
								CameraUtil::cameraGo(0, -MOVE_SPAN);
							}
							else if (y < MyVulkanManager::screenHeight / 2){//前移
								CameraUtil::cameraGo(MOVE_SPAN, 0);
							}
							else{
								CameraUtil::cameraGo(-MOVE_SPAN, 0);//後移
							}
						}
					break;
				}

寫在最後:

本小節主要基於OpenGL ES介紹了實現攝像機的三維漫遊,如果本節內容有錯誤和不合理之處,還請朋友們多多指出,我會虛心接受每一個建議。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章