【Modern OpenGL】摄像机系统 Camera
說明:跟著learnopengl的內(nèi)容學(xué)習(xí),不是純翻譯,只是自己整理記錄。?
強烈推薦原文,無論是內(nèi)容還是排版。?原文鏈接?
本文地址:http://blog.csdn.net/aganlengzi/article/details/50448469
攝像機 Camera
在前面的教程中,我們討論了視口矩陣和我們可以怎樣利用視口矩陣讓繪制的場景移動(我們在上次教程中成功將那個二維平面稍稍向后移動了一點)。OpenGL本身對攝像機這個概念并不熟悉,但是我們可以通過移動場景中所有的對象(就好像反方向移動一個攝像機一樣)來模擬一個。
在本次教程中,我們將會討論我們怎樣在OpenGL中建立攝像機。我們將會創(chuàng)建一個幀率攝像機,允許你在三維場景中自由移動。在這個教程中,我們還會討論一些關(guān)于鍵盤和鼠標(biāo)輸入的只是。最終會形成一個定制的攝像機類。
攝像機/視口坐標(biāo)系
三維場景中的一個攝像機主要由它在世界坐標(biāo)系中的位置、它的朝向一個指向右側(cè)和一個指向上方的向量來做決定。細心的你可能已經(jīng)發(fā)現(xiàn)了:我們實際上在利用這些量創(chuàng)建一個三個坐標(biāo)軸相互垂直以所在位置為原點的坐標(biāo)系。
1.攝像機的位置
得到一個攝像機的位置是十分簡單的。攝像機的位置從根本上來說就是一個指向攝像機位置的向量。我們通過如下代碼將攝像機的位置,也就是我們想要創(chuàng)建的坐標(biāo)系的原點設(shè)置在和我們上次教程相同的位置。
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);不要忘記,z軸的正方向是垂直于屏幕并且指向我們的。所以,當(dāng)我們想把攝像機向后移動,實際上就是把攝像機向z軸的正方向移動,也就是增大攝像機z軸的坐標(biāo)值。
2.攝像機的方向
確定了攝像機的位置(即坐標(biāo)系的原點),接下來需要確定的是攝像機的朝向。目前,我們先默認攝像機指向我們世界坐標(biāo)系的原點,也就是(0,0,0)。實際上我們是知道攝像機應(yīng)該指向z軸的負方向的,那么它的相反方向?qū)嶋H上就是其位置坐標(biāo)和世界坐標(biāo)系原點的插值(兩個向量之差決定了其方向),我們定義這個向量值為方向向量。如下面的代碼所示:
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f); glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);實際上方向向量這個說法是不太合適的,因為這個方向向量命名是攝像機正面朝向的相反方向!
3.向右的坐標(biāo)軸
按照一開始的說法,接下來需要的向量是向右的方向向量。它實際上代表的是我們想要創(chuàng)建的攝像機坐標(biāo)系的x軸正方向。怎樣獲得向右指向的方向向量呢?還記的前面教程中關(guān)于向量的叉乘的內(nèi)容嗎?兩個向量的叉乘得到的是垂直于這兩個向量決定的平面的向量(滿足右手坐標(biāo)系)。所以我們利用2中得到的z軸正向向量和世界坐標(biāo)系中的向上的方向向量做叉乘,便得到了指向右側(cè)的方向向量,它就是我們想創(chuàng)建的攝像機坐標(biāo)系的x軸正向。如下面代碼所示:
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));4.向上的坐標(biāo)軸
通過以上三步,我們已經(jīng)得到了建立一個坐標(biāo)系所需要的原點坐標(biāo)、x軸,z軸向量。現(xiàn)在就是缺少y軸方向的向量了,我們通過將x軸和z軸的方向向量做叉乘的方式得到向上的坐標(biāo)軸方向(也就是我們的y軸方向向量)。如下面的代碼所示:
glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);利用上面得到的攝像機坐標(biāo)系,我們現(xiàn)在可以創(chuàng)建我們的LookAt矩陣了,這個矩陣對于創(chuàng)建一個攝像機是十分有用和必要的。
Look At
使用矩陣的一大好處是:當(dāng)你利用三個相互垂直的坐標(biāo)軸建立了一個坐標(biāo)系,你可以通過這三個坐標(biāo)軸加上一個轉(zhuǎn)換向量創(chuàng)建一個矩陣,這個矩陣可以將任何向量轉(zhuǎn)換到你所定義的那個坐標(biāo)系空間中。而具體轉(zhuǎn)換的方式就是左乘這個矩陣。這也正是我們的Look At矩陣將要做的事情。我們現(xiàn)在就用上面得到的三個相互垂直的坐標(biāo)軸和一個位置向量來定義一個攝像機空間(攝像機坐標(biāo)系)。并通過定義一個矩陣(Look At矩陣)來將世界坐標(biāo)系中的坐標(biāo)值轉(zhuǎn)換到這個空間中。如下所示:
?
其中,R表示右向向量,U表示向上的向量,D代表方向向量,P是攝像機的位置向量。需要注意的是,這里的位置向量是上面講的偽“方向向量”的反方向,也就是攝像機朝向的方向向量。為什么要這么做?因為我們向左移動攝像機那么世界坐標(biāo)系中的對象看上去實際上是向右移動的。使用這個Look At矩陣作為我們的視口矩陣能夠高效地將世界坐標(biāo)系中的坐標(biāo)轉(zhuǎn)換到我們剛剛定義的視口坐標(biāo)系中。這樣,Look At矩陣也就名符其實了:它創(chuàng)建了一個朝向給定目標(biāo)的視口矩陣。
幸運的是,GLM早就為我們封裝了以上的過程。我們只需要指定一個攝像機的位置,一個目標(biāo)位置和一個代表世界坐標(biāo)系中向上方向的向量作為參數(shù)就可以創(chuàng)建這個Look At矩陣了。我們使用這個創(chuàng)建好的Look At矩陣作為我們的視口矩陣:
glm::mat4 view; view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));函數(shù)glm::LookAt的三個參數(shù)分別就是上面提到的三個參數(shù),得到的view和我們上面通過手工一步步創(chuàng)建得到的矩陣是一模一樣的。
在深入研究用戶輸入之前,讓我們首先利用攝像機的旋轉(zhuǎn)來產(chǎn)生一些酷炫的效果。我們設(shè)置場景的目標(biāo)向量是(0,0,0)。
我們將我們的攝像機放在一個半徑為10.0f的圓周上(這個圓周在世界坐標(biāo)系的z軸和x軸決定的平面上),并且隨著時間的推移,攝像機的位置不斷改變(通過三角函數(shù)實現(xiàn),主要是要保證攝像機總是在圓周上)。這樣在每次改變后得到的矩陣作為我們的視口矩陣。然后來看效果。
GLfloat radius = 10.0f; GLfloat camX = sin(glfwGetTime()) * radius; GLfloat camZ = cos(glfwGetTime()) * radius; glm::mat4 view; view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));得到的效果應(yīng)該是這個樣子的(顯示不了動畫,應(yīng)該看上起是圍繞中心旋轉(zhuǎn)的樣子):
可以通過設(shè)置不同圓的半徑值來得到不同的效果。
漫游 Walk around
轉(zhuǎn)動攝像機來得到變化角度的場景是有趣的,但是我們還可以讓它更加有趣!那就是我們自己來移動攝像機(想什么時候移動就什么時候移動,想移動到哪兒就移動到哪兒)。不過我們還是像上面一樣,應(yīng)該建立攝像機系統(tǒng)。為了靈活性,先來定義一些必要的變量吧:
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f); glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);現(xiàn)在我們上面說到的Look At矩陣編程了這個樣子:
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);還是和上面一樣。我們首先向glm::lookAt函數(shù)傳遞進事先定義的攝像機位置向量,方向是當(dāng)前的位置與事先定義的方向向量的和,這保證了無論我們怎樣移動,這個攝像機都會指向目標(biāo)。下面讓我們通過將攝像機的位置變量cameraPos和我們的鍵盤輸入相關(guān)聯(lián),以達到按鍵改變相機位置的目的。
在很早的教程中我們就已經(jīng)知道了怎樣使用鍵盤響應(yīng)函數(shù)。不過之前只是實現(xiàn)了我們的程序應(yīng)該如何相應(yīng)”ESC”鍵,現(xiàn)在讓我們?yōu)檫@個鍵盤響應(yīng)函數(shù)添加更多的鍵值處理:
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {...GLfloat cameraSpeed = 0.05f;if(key == GLFW_KEY_W)cameraPos += cameraSpeed * cameraFront;if(key == GLFW_KEY_S)cameraPos -= cameraSpeed * cameraFront;if(key == GLFW_KEY_A)cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if(key == GLFW_KEY_D)cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; }每當(dāng)我們按下WASD鍵的時候,cameraPos就會被相應(yīng)改變。當(dāng)我們想要前后移動的時候,我們將攝像機的位置在指向目標(biāo)的連線上進行加減。當(dāng)我們想要左右移動的時候,我們通過叉乘的方式獲得要移動方向的方向向量,然后也是做相應(yīng)的加減操作。
需要注意的是,我們在代碼中對叉乘的結(jié)果進行了歸一化處理,如果不這樣做的話,因為我們叉乘得到的向量的模可能不一致,可能造成按一下鍵移動的距離不一樣的效果,這不是我們想要的。
如果我們完成上面講的相關(guān)代碼,我們應(yīng)該得到的效果應(yīng)該是下面這個樣子(額,沒法呈現(xiàn)出來。下圖是我按了幾下w鍵的效果):
就是鍵盤上的WASD分別代表向前向左向后向右,如果按下這些鍵的話,可以看到可見場景內(nèi)的所有對象向前向左向后向右。
在初步試驗了攝像機系統(tǒng)你可能已經(jīng)注意到了:我們不能同時在兩個方向上移動(也就是在對角線上移動)攝像機!而且,當(dāng)我們按下某個方向上的按鍵的時候,畫面會“遲疑”一下,然后才會按照應(yīng)該移動的方向進行移動,好奇怪有沒有。原因是:大多數(shù)的事件輸入系統(tǒng)每次只能處理一個按鍵,并且它們的函數(shù)也只能在我們激活一個按鍵的時候才會被調(diào)用。我們的系統(tǒng)雖然也是這樣,但是我們可以通過一些小技巧來克服這個缺點。
這個技巧的原理是:我們在按鍵響應(yīng)函數(shù)中只對按鍵的按下和釋放做處理。在game loop中我們檢查哪一個按鍵被按下/釋放了,并且完成相應(yīng)的動作。所以,我們需要存儲哪一個按鍵被按下或者釋放的狀態(tài)信息并且在游戲循環(huán)中對這些狀態(tài)進行反應(yīng)。首先,讓我們來創(chuàng)建一個bool類型的數(shù)組來保存所有可能按鍵的狀態(tài)信息:
bool keys[1024];然后,我們需要在按鍵響應(yīng)函數(shù)key_callback中根據(jù)按鍵的情況來動態(tài)改變這些值:
if(action == GLFW_PRESS)keys[key] = true; else if(action == GLFW_RELEASE)keys[key] = false;還有就是要創(chuàng)建響應(yīng)函數(shù),當(dāng)我們檢查到鍵的狀態(tài)改變的時候做出相應(yīng)的動作:
void do_movement() {// Camera controlsGLfloat cameraSpeed = 0.01f;if(keys[GLFW_KEY_W])cameraPos += cameraSpeed * cameraFront;if(keys[GLFW_KEY_S])cameraPos -= cameraSpeed * cameraFront;if(keys[GLFW_KEY_A])cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if(keys[GLFW_KEY_D])cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; }最后,我們在game loop中調(diào)用我們上面實現(xiàn)的兩個函數(shù)來完成之前只用一個函數(shù)完成的功能:
while(!glfwWindowShouldClose(window)) {// Check and call eventsglfwPollEvents();do_movement(); // Render stuff... }現(xiàn)在,試試吧,我們應(yīng)該能夠同時在兩個方向上移動了,并且也應(yīng)該沒有”遲疑“了吧。
移動速度 Movement speed
目前看上去我們設(shè)置了一個恒定的值來作為我們移動的速度,在理論上來看,這是正確的,但是在實際上,人們的處理器是不相同的,這就造成了了,有些人能夠在一秒內(nèi)繪制很多幀而有些人卻只能繪制比較少的幀,每秒內(nèi)繪制的幀的數(shù)量叫做幀率。當(dāng)幀率比較大的時候,那么對do_movement的調(diào)用次數(shù)也就會變多,最終造成的是性能較好的機器能夠產(chǎn)生的動作要多于或者快于性能較差的機器。這顯然不是我們希望的,我們希望我們的程序能夠在任何配置的機器上都能夠?qū)ο嗤膭幼鳟a(chǎn)生相同的響應(yīng)。
我們通過下面的方法來保證我們的程序在不同性能機器上都能產(chǎn)生相同的體驗效果:?
主要的原理是對每幀中產(chǎn)生的動作的速度和幀渲染時間進行同步關(guān)聯(lián)。那么移動速度是以幀處理時間為參照的,多以并不會受到機器性能的影響了。
我們記錄下面這兩個量(作為程序的全局變量):
GLfloat deltaTime = 0.0f; // 上一幀開始和當(dāng)前幀開始的時間間隔 GLfloat lastFrame = 0.0f; // 上一幀開始時刻在每幀內(nèi),我們利用上面這兩個量來計算新的deltaTime,供當(dāng)前幀使用。
GLfloat currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame;現(xiàn)在我們可以利用deltatime來計算渲染速度了:
void Do_Movement() {GLfloat cameraSpeed = 5.0f * deltaTime;... }當(dāng)我們上面關(guān)于移動速度的代碼加上后,得到的應(yīng)該就是能夠明顯感覺比較平滑的效果了。我修改的代碼main.cpp。
環(huán)視 Look around
只用鍵盤進行四個方向上的移動并不是那么有趣,尤其是我們不能夠讓我們環(huán)顧(類似于我們是地球環(huán)繞太陽一周那樣)場景而只能夠在某個方向上平移!是時候加入鼠標(biāo)來達到我們想要的這個效果了!
為了能夠環(huán)顧我們的場景,我們需要根據(jù)鼠標(biāo)的輸入來改變攝像機的cameraFront向量。但是這并不是十分簡單的,因為要達到較好的效果,其中需要三角函數(shù)相關(guān)的計算。
歐拉角度 Euler angles
歐拉角的基本思想是將角位移分解為繞三個互相垂直軸的三個旋轉(zhuǎn)組成的序列。這聽起來復(fù)雜,其實它是非常直觀的。之所以有“角位移”的說法正是因為歐拉角能用來描述任意旋轉(zhuǎn),但最有意義的是使用笛卡爾坐標(biāo)系并按照一定順序所組成的旋轉(zhuǎn)序列。最常用的約定,即所謂“heading-pitch-bank”約定。在這個系統(tǒng)中,一個方位被定義為一個heading角,一個pitch角,和一個bank角。它的基本思想就是讓物體開始于“標(biāo)準”方位——就是物體坐標(biāo)軸和慣性坐標(biāo)軸對齊。在標(biāo)準方位上,讓物體作heading,pitch,bank旋轉(zhuǎn),最后物體到達我們想要描述的方位。
歐拉角由三個可以表示三維中任意旋轉(zhuǎn)角度的部分組成,是由Leonhard Euler在1700s左右定義的。這三個歐拉角度分別是pitch,yaw和roll。下圖分別直觀展示了這三個量:
第一個圖中顯示的是pitch,它代表了我們向上或者向下看的角度。第二個圖顯示的是yaw值,它代表了我們向左或者向右看的大小。第三個量是roll代表了我們的轉(zhuǎn)動量,通常在航天飛機攝像機中使用。每一個歐拉角都是一個數(shù)量值,它們?nèi)齻€進行組合就能夠表示三維空間中的任何一個轉(zhuǎn)動角度。
在我們的攝像機系統(tǒng)中,我們只關(guān)心yaw和pitch值,所以我們不會討論roll值的改變。基于任何給定的pitch和yaw值,我們可以將它們轉(zhuǎn)換到三維空間中得到一個方向向量。這個轉(zhuǎn)換過程需要相應(yīng)的三角函數(shù)的的知識:
?
如果我們定義直角三角形的斜邊為1,三角函數(shù)變得簡單,cos = x/h=cos x/1=cos x;sin y/h=sin y/1=sin y。利用這兩個公式,我們可以根據(jù)已知的角度值得到x和y方向的長度。我們下面就是要用這種方法來計算我們方向向量的分量。
?
我們的目標(biāo)是把歐拉角轉(zhuǎn)換成我們的三維坐標(biāo)。實際上看懂了下面這張圖就可以了:
上圖中斜邊是1,pitch和yaw已經(jīng)給出,那么pitch和yaw以及斜邊決定的點的坐標(biāo)就是如圖所示的樣子,這樣,我們也就得到了我們的方向向量。它的計算方式可以通過glm的函數(shù)來完成:
direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw)); direction.y = sin(glm::radians(pitch)); direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));以上就是我們將給定的歐拉角轉(zhuǎn)換到三維坐標(biāo)的方式。但是我們要怎樣獲得歐拉角的值呢?
鼠標(biāo)輸入 Mouse input
Pitch和yaw值的獲取就是通過鼠標(biāo)啊~鼠標(biāo)的水平移動改變yaw值,垂直移動改變pitch值,就是這么簡單。我們的想法是保存上一幀鼠標(biāo)的位置并且在當(dāng)前幀中,我們計算鼠標(biāo)值的該表。根據(jù)其在水平和豎直方向上改變大小來更新我們的pitch值和yaw值。然后也就決定了我們的攝像機的移動。
那么試一下吧~?
首先我們需要設(shè)置GLFW設(shè)置我們的程序能夠在鼠標(biāo)進入我們的程序窗口的時候捕獲它,并且使它隱藏起來。我們可以通過以下簡單的方式來設(shè)置:
在這個函數(shù)調(diào)用之后,我們在程序窗口中移動鼠標(biāo)的時候,它不可見并且不會超出我們的程序窗口。
為了按照上述方式計算鼠標(biāo)的pitch和yaw值,我們需要設(shè)置GLFW使其監(jiān)聽鼠標(biāo)移動事件,這和前面我們已經(jīng)使用的鍵盤監(jiān)聽函數(shù)的實現(xiàn)實際上是差不多的。我們只要先實現(xiàn)一個函數(shù),然后把這個函數(shù)通過注冊函數(shù)注冊到程序中就可以了。1
void mouse_callback(GLFWwindow* window, double xpos, double ypos);其中 xpos和ypos表示鼠標(biāo)的當(dāng)前位置。一旦我們注冊了這個函數(shù),這個函數(shù)就會在鼠標(biāo)移動的時候被調(diào)用。
glfwSetCursorPosCallback(window, mouse_callback);上面的過程完成了鼠標(biāo)數(shù)據(jù)的輸入。接下來還有一些步驟要做:
為了完成步驟1,我們設(shè)置了兩個坐標(biāo)值用于表示坐標(biāo)在程序窗口的位置,實際上是一個二維坐標(biāo),初始化為窗口中心的位置。
GLfloat lastX = 400, lastY = 300;然后,在鼠標(biāo)的回調(diào)函數(shù)中我們來計算鼠標(biāo)兩幀之間的位移:
GLfloat xoffset = xpos - lastX; GLfloat yoffset = lastY - ypos; // Reversed since y-coordinates range from bottom to top lastX = xpos; lastY = ypos;GLfloat sensitivity = 0.05f; xoffset *= sensitivity; yoffset *= sensitivity;需要注意的是我們對位移量做了一個靈敏度加權(quán)的操作,如果我們不使用這個權(quán)值的話,鼠標(biāo)的移動可能會太大,這不是我們想要的效果。我們可以根據(jù)程序的實際運行結(jié)果對這個靈敏度權(quán)值進行調(diào)整。
接下來,我們將偏移量更新到歐拉角pitch和yaw上:
yaw += xoffset; pitch += yoffset;下面,我們想要對這個攝像機系統(tǒng)加上必要的限制,以防止用戶得到奇怪的坐標(biāo)值和奇怪的效果。下面設(shè)置的限定是:向上看不能看到大于89度或者小于-89度的情況(就是不能把脖子仰斷或者頭向下看到屁股):
if(pitch > 89.0f)pitch = 89.0f; if(pitch < -89.0f)pitch = -89.0f;我們并沒有對yaw值進行限制,如果加上,也是十分方便的。原理同上。
最后一步是根據(jù)yaw和pitch計算作用后的方向向量:
glm::vec3 front; front.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw)); front.y = sin(glm::radians(pitch)); front.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw)); cameraFront = glm::normalize(front);最后得到的結(jié)果存放在cameraFront變量中,這是一個向量,我們在前面已經(jīng)在函數(shù)中進行調(diào)用傳參。
如果直接運行上面的代碼,我們得到的結(jié)果是:在每次鼠標(biāo)最開始進入到我們程序的窗口的時候,我們窗口中的場景會有一個比較大的跳躍。產(chǎn)生這種現(xiàn)象的原因是我們將鼠標(biāo)的位置初始化為我們窗口的中心點了(還記得前面的初始化吧),但是我們鼠標(biāo)進入到窗口的時候一般不會恰好是窗口的中心店,這樣就會造成在第一次計算偏移量的時候的一大步跳躍。改進的方法就是將鼠標(biāo)最開始進入到我們程序窗口的坐標(biāo)作為鼠標(biāo)的初始坐標(biāo):
if(firstMouse) // this bool variable is initially set to true {lastX = xpos;lastY = ypos;firstMouse = false; }最終的鼠標(biāo)響應(yīng)函數(shù)為:
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {if(firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}GLfloat xoffset = xpos - lastX;GLfloat yoffset = lastY - ypos; lastX = xpos;lastY = ypos;GLfloat sensitivity = 0.05;xoffset *= sensitivity;yoffset *= sensitivity;yaw += xoffset;pitch += yoffset;if(pitch > 89.0f)pitch = 89.0f;if(pitch < -89.0f)pitch = -89.0f;glm::vec3 front;front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));front.y = sin(glm::radians(pitch));front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));cameraFront = glm::normalize(front); }Ok!這樣就好了!用我們的鼠標(biāo)就可以讓我們的場景不斷地旋轉(zhuǎn)了!代碼.
縮放 Zoom
我們還想要為我們的攝像機系統(tǒng)再加入一點縮放功能的接口。前面的教程中我們講到可以利用fov來定義我們可見程序窗口的尺寸。當(dāng)視口變小,那么投影出來的對象也就變小了,看上去就是縮小的效果。我們想要利用鼠標(biāo)轉(zhuǎn)動滾輪來達到縮放的功能。像上面的鍵盤函數(shù)和鼠標(biāo)移動函數(shù),我們首先定義一個鼠標(biāo)滾輪滾動的響應(yīng)函數(shù):
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {if(fov >= 1.0f && fov <= 45.0f)fov -= yoffset;if(fov <= 1.0f)fov = 1.0f;if(fov >= 45.0f)fov = 45.0f; }當(dāng)滾輪滾動的時候,yoffset代表了我們垂直滾動量。我們借用這個值來修改我們?nèi)衷O(shè)定的fov值,因為45.0ffov的默認值。我們設(shè)定縮放范圍是1.0f到45.0f。
我們將fov作為參數(shù)傳遞進我們的透視投影矩陣生成函數(shù)中:
projection = glm::perspective(fov, (GLfloat)WIDTH/(GLfloat)HEIGHT, 0.1f, 100.0f);最后別忘了將鼠標(biāo)滾輪函數(shù)注冊上。
glfwSetScrollCallback(window, scroll_callback);這樣應(yīng)該就能夠?qū)崿F(xiàn)鼠標(biāo)滾輪滾動來縮放場景的效果了。
原作者指出利用歐拉角實現(xiàn)攝像機系統(tǒng)還是比較low的,他還有更好的方法,好吧,我原來用的方法還沒有歐拉角好。只能先膜拜了!好消息是后面他會講到更好的方法。
攝像機類 Camera class
在后面的教程中,我們會經(jīng)常使用改變攝像機的參數(shù)來達到我們想要的效果。但是正像這次教程講的這整個過程,攝像機系統(tǒng)的建立還是比較復(fù)雜的。所以原作者封裝了一個攝像機類以方便后面的使用。這是代碼。
像前面的shader類一樣,這個攝像機類也是只有一個頭文件,我們想要使用的時候,直接包含這個頭文件就好了,其中的代碼相信完整看過上面步驟的人都能夠很輕松看懂。
利用這個攝像機類(頭文件)完成的上面相同效果的整個代碼在這兒。
總結(jié)
以上是生活随笔為你收集整理的【Modern OpenGL】摄像机系统 Camera的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 宽基指数和策略指数的区别,从这两个角度了
- 下一篇: 股神巴菲特炒股的技巧,三招至关重要