青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

WisKeyのLullaby

huangwei.pro 『我失去了一只臂膀』「就睜開了一只眼睛」

  C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
  12 Posts :: 0 Stories :: 23 Comments :: 0 Trackbacks

公告

“我該走哪條路?”
“這取決于你要去哪里。”
“我只想能到某個地方。”
“只要你走的夠遠(yuǎn),你始終能到達(dá)那個地方。”

Home: huangwei.pro
E-Mail: sir.huangwei [at] gmail.com
09.6 畢業(yè)于杭州電子科技大學(xué)
進(jìn)入網(wǎng)易杭州研究院工作至今

常用鏈接

留言簿(1)

我參與的團(tuán)隊

搜索

  •  

積分與排名

  • 積分 - 51837
  • 排名 - 449

最新評論

閱讀排行榜

評論排行榜

http://huangwei.pro/2015-09/modern-opengl4/

 

本篇教程中,我們會鞏固上一篇所提到的矩陣和相機(jī)知識,并使用tdogl::Camera類來實現(xiàn)第一人稱射擊類型的相機(jī)。然后,我們會將相機(jī)與鍵盤和鼠標(biāo)掛鉤,使得我們可以移動和瀏覽3D場景。這里會學(xué)一些向量數(shù)學(xué),還有上一篇沒提到的逆矩陣。

獲取代碼

所有例子代碼的zip打包可以從這里獲取:https://github.com/tomdalling/opengl-series/archive/master.zip

這一系列文章中所使用的代碼都存放在:https://github.com/tomdalling/opengl-series。你可以在頁面中下載zip,加入你會git的話,也可以復(fù)制該倉庫。

本文代碼你可以在source/04_camera目錄里找到。使用OS X系統(tǒng)的,可以打開根目錄里的opengl-series.xcodeproj,選擇本文工程。使用Windows系統(tǒng)的,可以在Visual Studio 2013里打開opengl-series.sln,選擇相應(yīng)工程。

工程里已包含所有依賴,所以你不需要再安裝或者配置額外的東西。如果有任何編譯或運(yùn)行上的問題,請聯(lián)系我。

向量理論

在上一篇學(xué)了矩陣?yán)碚摵螅阋詾閿?shù)學(xué)理論課就結(jié)束了?想得太美了,現(xiàn)在下一部分就來了:向量。正統(tǒng)的理解認(rèn)為向量是3D編程的基礎(chǔ)。后面我會展示些代碼,是用鍵盤來進(jìn)行向量運(yùn)算,讓相機(jī)可以在不同方向上移動。

在3D中(2D中也一樣),向量經(jīng)常用來表示一些不同的東西,比如:

  1. 位置(即,坐標(biāo))
  2. 位移(比如,移動)
  3. 方向(比如,南北,上下)
  4. 速度(比如,車的速度和方向)
  5. 加速(比如,重力)

你可能注意到了上面所提的一些概念都是通常是用來實現(xiàn)物理引擎的。我們在本文中不會實現(xiàn)所有的物理,但為了更好的理解向量,第一步讓我們來一些物理教學(xué)。

什么是向量?一種偽數(shù)學(xué)的定義上來說,一個向量(vector)就是幅度(magnitude)加上方向它能向上,向下,往左,往右,朝北,朝西南等等。你能用3D向量來表示任何一個你指向的方向。向量的另一部分,幅度,表示向量的長度或者大小。

向量最簡單的可視化方式就是繪制它,一般向量都會被繪制為箭頭。箭頭所指的方向就是向量的方向,箭頭的長度就是幅度。下面的圖是一個2D向量,但2D的理論同樣能應(yīng)用到3D上。

 


下面用例子來說明向量代表的不同含義。

 方向幅度含義
往北5千米5千米位置
頭上5厘米5厘米位置
以50千米每小時開往西湖西湖方向50千米/每小時速度
地球引力為9.8m/s2往地球質(zhì)心9.8m/s2加速


當(dāng)編碼時,向量只是一組數(shù)字。每個數(shù)字都是向量的“一維”。比如,一個三維3D向量就是有3個數(shù)字的數(shù)組,2D向量是有2個數(shù)字。因為我們是在3D中進(jìn)行工作,所以大部分情況只要處理3D向量,但我們也需要用到4D。無論何時我說“向量”,那意味著是3D向量。我們使用GLM的向量數(shù)學(xué)庫,2D,3D,4D的類型分別為glm::vec2 ,glm::vec3,glm::vec4

3D向量表示頂點(diǎn),坐標(biāo)或者位置相當(dāng)簡單。3D向量的3個維度分別是X,Y,Z的值。當(dāng)向量表示位置,方向和幅度時,都是從原點(diǎn)(0,0,0)開始計算的。比如,假設(shè)一個物體的XYZ坐標(biāo)為(0,2,0),則它的幅度是2,方向為“沿Y軸向上”。

負(fù)向量

當(dāng)你要將向量取負(fù)時,就是保持相同的幅度,但將方向變成方向。

比如:

 

A=向北5千米
-A=向南5千米

如果相機(jī)的方向是往右的,我們可以使用負(fù)向量來算出相機(jī)往左的方向。就像這樣:

glm::vec3 rightDirection = gCamera.right(); glm::vec3 leftDirection = -rightDirection; //vector negation 

標(biāo)量乘法

當(dāng)你將向量乘上一個數(shù)值時,新向量的結(jié)果表示相同的方向,但幅度被擴(kuò)大了相應(yīng)倍數(shù)。這個數(shù)值被稱為“標(biāo)量”,這就是為何該乘法被稱為“標(biāo)量乘法”。

比如:

 

A=向北5千米
0.5 × A=向北2.5千米
2 × A=向北10千米

我們可以使用標(biāo)量乘法來計算基于“移動速度”的相機(jī)位置,像這樣:

const float moveSpeed = 2.0; //units per second float distanceMoved = moveSpeed * secondsElapsed; glm::vec3 forwardDirection = gCamera.forward(); glm::vec3 displacement = distanceMoved * forwardDirection; //scalar multiplication 

向量加法

向量加法在2D圖形表現(xiàn)下最容易理解。對兩個向量進(jìn)行加法,就是將它們的頭部(箭頭一段)連接尾部(非箭頭一段)。加法順序不重要。它的結(jié)果就是,從第一個向量尾部走向另外一個向量的頭部。

 

注意,即使這些向量看上去是在不同的位置上,但結(jié)果向量的幅度(長度)和方向不會改變。請記住,向量只有一個方向和一個幅度。它們沒有起始點(diǎn),所以它們可以在任意不同位置上,但還是相等的。

比如:

A = 往北1千米

B = 往西1千米

A + B = 往西北1.41千米

向量減法相當(dāng)于是加上一個負(fù)向量,比如:

A = 往北1千米

B = 往西1千米

A - B = 往西北1.41千米

A + (-B) = 往西北1.41千米

我們使用向量加法來計算出相機(jī)位移后的的新位置,像這樣:

glm::vec3 displacement = gCamera.forward() * moveSpeed * secondsElapsed; glm::vec3 oldPosition = gCamera.position(); glm::vec3 newPosition = oldPosition + displacement; //vector addition gCamera.setPosition(newPosition); 

單位向量

單位向量是幅度為1的向量。它們經(jīng)常被用來表示方向。

當(dāng)一個向量是用來表示方向時,它的幅度就沒啥用處。即使這樣,我們還是將它的幅度設(shè)為1,是為了計算時更方便一些。

當(dāng)你在單位向量上使用標(biāo)量乘法時,它的方向仍然不變,但幅度會被設(shè)為標(biāo)量的值。因此,你將一個單位向量乘上5后,新的向量的幅度就是5。假如你乘上123,那幅度也就是123。基本上這允許我們設(shè)置任意一個向量的幅度,而不會更改它的方向。

讓我們對相機(jī)進(jìn)行往左移動12單位的操作。我們先設(shè)置一個方向為左的單位向量,然后使用標(biāo)量乘法將它的幅度設(shè)為12,最后使用它來計算出新位置。代碼看上去應(yīng)該是這樣的:

// `gCamera.right()` returns a unit vector, therefore `leftDirection` will also be a unit vector. // Negation only affects the direction, not the magnitude. glm::vec3 leftDirection = -gCamera.right(); //`displacement` will have a magnitude of 12 glm::vec3 displacement = leftDirection * 12; //`newPosition` will be 12 units to the left of `oldPosition` glm::vec3 newPosition = oldPosition + displacement; 

任何一個向量都能變?yōu)閱挝幌蛄俊_@個操作叫做單位化。我們可以用GLM來單位化一個向量:

glm::vec3 someRandomVector = glm::vec3(123,456,789); glm::vec3 unitVector = glm::normalize(someRandomVector); 

tdogl::Camera類

恭喜你看到這兒了!現(xiàn)在你已經(jīng)有足夠的向量知識了,來,讓我們開始編碼。

tdogl::Camera類的接口這里,實現(xiàn)代碼在這里

在前面文章中我們在OpenGL中用矩陣來實現(xiàn)相機(jī)。tdogl::Camera類可以基于各種屬性來創(chuàng)建矩陣,比如:

  • 相機(jī)位置
  • 相機(jī)朝向(方向)
  • 縮放(視野)
  • 最大和最小可視距離(遠(yuǎn)近平面)
  • 視口/窗口縱橫比

上面的每個屬性都有各自的設(shè)置和獲取接口。前文已經(jīng)介紹過了。

現(xiàn)在讓我們用matrixorientation方法來實現(xiàn)如何讓這所有屬性組合成一個矩陣。

glm::mat4 Camera::matrix() const { glm::mat4 camera = glm::perspective(_fieldOfView, _viewportAspectRatio, _nearPlane, _farPlane); camera *= orientation(); camera = glm::translate(camera, -_position); return camera; }  glm::mat4 Camera::orientation() const { glm::mat4 orientation; orientation = glm::rotate(orientation, _verticalAngle, glm::vec3(1,0,0)); orientation = glm::rotate(orientation, _horizontalAngle, glm::vec3(0,1,0)); return orientation; } 

我們可以看到,最終的相機(jī)矩陣是由四個不同的變換組成。按順序是:

  • 移動,基于相機(jī)位置
  • 旋轉(zhuǎn),基于相機(jī)水平(左/右)轉(zhuǎn)角
  • 旋轉(zhuǎn),基于相機(jī)垂直(上/下)轉(zhuǎn)角
  • 透視,基于視野,近平面,遠(yuǎn)平面和縱橫比

假如你覺得這順序是反的,那請記住矩陣乘法是從右往左,代碼上順序是從底往上。

注意,移動用了相機(jī)的負(fù)位置。這里再次用前文提到的方式,我們可以讓3D場景往后來實現(xiàn)相機(jī)往前走。向量為負(fù)時會反轉(zhuǎn)其方向,所以“往前”就變成“往后”。

tdogl::Camera類還有其它方法來返回單位向量:,。我們需要從鍵盤獲取消息來實現(xiàn)相機(jī)移動。

相機(jī)方位矩陣求逆

讓我來看下tdogl::Camera::up方法的實現(xiàn),這里有兩個東西我們還沒有提及。

glm::vec3 Camera::up() const { glm::vec4 up = glm::inverse(orientation()) * glm::vec4(0,1,0,1); return glm::vec3(up); } 

我們看到它使用了glm::inverse方法。從上一篇文章中,我們知道矩陣能對坐標(biāo)進(jìn)行變換。在這里,我們還需要對坐標(biāo)進(jìn)行“反變換”,使得我們能獲得矩陣乘法變換前的坐標(biāo)。為了實現(xiàn)這個目的,我們需要計算矩陣。逆矩陣是一個矩陣,完全相反于另外一個矩陣,這意味著它能撤銷另外一個矩陣的變換。比如,矩陣A是繞著Y軸旋轉(zhuǎn)90°,那矩陣A的逆矩陣就是繞著Y軸旋轉(zhuǎn)-90°。

當(dāng)相機(jī)的方向改變時,“向上”的方向也隨之改變。比如,想象下有個箭頭指向你的頭頂,假如你旋轉(zhuǎn)你的頭往地上看,那箭頭就是向前傾斜,假如你往天上看,那箭頭是向后傾斜的。如果你往前看,就是你的頭“不旋轉(zhuǎn)”,那箭頭就是筆直向上。我們用“筆直向上”的單位向量(0,1,0)來表示相機(jī)的向上方向,“不旋轉(zhuǎn)”使用相機(jī)方位矩陣的逆矩陣。另外一種解釋,在相機(jī)旋轉(zhuǎn)后,向上方向總是為(0,1,0),所以我們要將逆旋轉(zhuǎn)乘上(0,1,0),這就能得到相機(jī)旋轉(zhuǎn)前的向上方向。

(0,1,0)是單位向量,當(dāng)你旋轉(zhuǎn)一個單位向量結(jié)果還是一個單位向量。假如結(jié)果不是單位向量,你應(yīng)該使用glm::normalize來單位化。

計算相機(jī)的方向是同樣的方式。

你可能注意到了這里用了一個4D向量glm::vec4。前文解釋過,4x4 矩陣(glm::mat4)需要一個4D向量來進(jìn)行矩陣乘法,使用glm::vec3會導(dǎo)致編譯錯誤。只要把3D向量(0,1,0)變成4D向量(0,1,0,1)就可以進(jìn)行矩陣乘法了,計算完成后我們再將4D向量變回3D向量。

整合tdogl::Camera類

現(xiàn)在我們開始使用tdogl:Camera類。

在之前的文章中,我們分別設(shè)置了投影矩陣和相機(jī)矩陣兩個著色器變量。在本文中,tdogl::Camera合并了這兩個矩陣,所以讓我們移除projection著色器變量,只用camera變量就足夠了。下面是頂點(diǎn)著色器的更新:

#version 150  uniform mat4 camera; uniform mat4 model;  in vec3 vert; in vec2 vertTexCoord;  out vec2 fragTexCoord;  void main() { // Pass the tex coord straight through to the fragment shader fragTexCoord = vertTexCoord; // Apply all matrix transformations to vert gl_Position = camera * model * vec4(vert, 1); } 

現(xiàn)在我們將tdogl::Camera整合到main.cpp中。首先包含頭文件:

#include "tdogl/Camera.h" 

然后聲明全局變量:

tdogl::Camera gCamera; 

在前一篇文章中,相機(jī)和投影矩陣是不會改變的,所以在LoadShaders函數(shù)中設(shè)置一次就好了。但在本文中,因為我們需要用鼠標(biāo)和鍵盤來控制,所以設(shè)置相機(jī)矩陣要放在Render函數(shù)中并每幀都要設(shè)置一下。首先讓我們移除舊代碼:

static void LoadShaders() { std::vector<tdogl::Shader> shaders; shaders.push_back(tdogl::Shader::shaderFromFile(ResourcePath("vertex-shader.txt"), GL_VERTEX_SHADER)); shaders.push_back(tdogl::Shader::shaderFromFile(ResourcePath("fragment-shader.txt"), GL_FRAGMENT_SHADER)); gProgram = new tdogl::Program(shaders); // the commented-out code below was removed /*      gProgram->use();      //set the "projection" uniform in the vertex shader, because it's not going to change     glm::mat4 projection = glm::perspective<float>(50.0, SCREEN_SIZE.x/SCREEN_SIZE.y, 0.1, 10.0);     //glm::mat4 projection = glm::ortho<float>(-2, 2, -2, 2, 0.1, 10);     gProgram->setUniform("projection", projection);      //set the "camera" uniform in the vertex shader, because it's also not going to change     glm::mat4 camera = glm::lookAt(glm::vec3(3,3,3), glm::vec3(0,0,0), glm::vec3(0,1,0));     gProgram->setUniform("camera", camera);      gProgram->stopUsing();     */ } 

然后,在Render函數(shù)中設(shè)置camera著色器變量:

// draws a single frame static void Render() { // clear everything glClearColor(0, 0, 0, 1); // black glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // bind the program (the shaders) gProgram->use(); // set the "camera" uniform gProgram->setUniform("camera", gCamera.matrix()); 

gCamera.matrix()函數(shù)返回的是一個glm::mat4, 并且setUniform函數(shù)使用了glUniformMatrix4fv來設(shè)置頂點(diǎn)著色器中的相機(jī)矩陣uniform變量。

AppMain函數(shù)中設(shè)置相機(jī)的初始化位置和視窗縱橫比。

gCamera.setPosition(glm::vec3(0,0,4)); gCamera.setViewportAspectRatio(SCREEN_SIZE.x / SCREEN_SIZE.y); 

其余相機(jī)屬性都留成默認(rèn)值。

你現(xiàn)在運(yùn)行程序,會看到上次實現(xiàn)的旋轉(zhuǎn)立方體。下一步就讓我們用鼠標(biāo)和鍵盤來控制相機(jī)。

鍵盤輸入

我們先來實現(xiàn)鍵盤控制。每次我們更新屏幕時,我們先檢查'W','A','S'或'D'按鍵是否被按下,如果有觸發(fā)那就稍微移動下相機(jī)。函數(shù)glfwGetKey返回一個布爾值來表示這個按鍵是否按下。新的Update函數(shù)看上去是這樣的:

// update the scene based on the time elapsed since last update void Update(float secondsElapsed) { //rotate the cube const GLfloat degreesPerSecond = 180.0f; gDegreesRotated += secondsElapsed * degreesPerSecond; while(gDegreesRotated > 360.0f) gDegreesRotated -= 360.0f; //move position of camera based on WASD keys const float moveSpeed = 2.0; //units per second if(glfwGetKey(gWindow, 'S')){ gCamera.offsetPosition(secondsElapsed * moveSpeed * -gCamera.forward()); } else if(glfwGetKey(gWindow, 'W')){ gCamera.offsetPosition(secondsElapsed * moveSpeed * gCamera.forward()); } if(glfwGetKey(gWindow, 'A')){ gCamera.offsetPosition(secondsElapsed * moveSpeed * -gCamera.right()); } else if(glfwGetKey(gWindow, 'D')){ gCamera.offsetPosition(secondsElapsed * moveSpeed * gCamera.right()); } } 

我們先忽略立方體的旋轉(zhuǎn)。

當(dāng)S鍵被按下時,我們可以看得更近些:

gCamera.offsetPosition(secondsElapsed * moveSpeed * -gCamera.forward()); 

這一行代碼做了好多事,讓我們用更容易懂的方式重寫一遍,新的函數(shù)叫MoveCameraBackwards

void MoveCameraBackwards(float secondsElapsed) { //TODO: finish writing this function } 

向后是一個方向,所以應(yīng)該是個單位向量。在相機(jī)類中沒有backward函數(shù),但它有個forward函數(shù)。向后就是向前的反方向,所以我們只要對向前的單位向量取負(fù)數(shù)即可。

void MoveCameraBackwards(float secondsElapsed) { //`direction` is a unit vector, set to the "backwards" direction glm::vec3 direction = -gCamera.forward(); //TODO: finish writing this function } 

然后,我們應(yīng)該知道將相機(jī)移多遠(yuǎn)。我們有相機(jī)的移動速度moveSpeed,我們還知道從上一幀到現(xiàn)在過去了多少時間secondsElapsed。對這兩個值進(jìn)行乘法,就能得到相機(jī)移動的距離。

void MoveCameraBackwards(float secondsElapsed) { //`direction` is a unit vector, set to the "backwards" direction glm::vec3 direction = -gCamera.forwards(); //`distance` is the total distance to move the camera float distance = moveSpeed * secondsElapsed; //TODO: finish writing this function } 

現(xiàn)在,我們知道了移動的距離和方向,我們就能構(gòu)造一個位移向量。它的幅度就是distance,它的方向就是direction。因為direction是個單位向量,我們可以用標(biāo)量乘法來設(shè)置幅度。

void MoveCameraBackwards(float secondsElapsed) { //`direction` is a unit vector, set to the "backwards" direction glm::vec3 direction = -gCamera.forwards(); //vector negation //`distance` is the total distance to move the camera float distance = moveSpeed * secondsElapsed; //`displacement` is a combination of `distance` and `direction` glm::vec3 displacement = distance * direction; //scalar multiplication //TODO: finish writing this function } 

最后,我們移動(或者說是置換)相機(jī)當(dāng)前位置。用向量加法即可。最基礎(chǔ)的公式newPosition = oldPosition + displacement

void MoveCameraBackwards(float secondsElapsed) { //`direction` is a unit vector, set to the "backwards" direction glm::vec3 direction = -gCamera.forwards(); //vector negation //`distance` is the total distance to move the camera float distance = moveSpeed * secondsElapsed; //`displacement` is a combination of `distance` and `direction` glm::vec3 displacement = distance * direction; //scalar multiplication //change the position of the camera glm::vec3 oldPosition = gCamera.position(); glm::vec3 newPosition = oldPosition + displacement; //vector addition gCamera.setPosition(newPosition); } 

完成了!MoveCameraBackwards函數(shù)這么多行代碼跟這一行代碼是一樣的:

gCamera.offsetPosition(secondsElapsed * moveSpeed * -gCamera.forward()); 

offsetPosition函數(shù)做的就是向量加法,它將位移向量作為參數(shù)傳入。讓我們使用那一行代碼來替換MoveCameraBackwards函數(shù),因為簡潔就是美。

其余按鍵的工作方式都是相同的,無非是方向不同而已。讓我們再添加ZX鍵來實現(xiàn)相機(jī)上和下。

if(glfwGetKey(gWindow, 'Z')){ gCamera.offsetPosition(secondsElapsed * moveSpeed * -glm::vec3(0,1,0)); } else if(glfwGetKey(gWindow, 'X')){ gCamera.offsetPosition(secondsElapsed * moveSpeed * glm::vec3(0,1,0)); } 

注意,為什么這里用向量(0,1,0)而不是gCamera.up()。記住,“向上”方向會隨著相機(jī)方向而改變。假如相機(jī)看地上,“向上”指的是向前,假設(shè)相機(jī)看天上,“向上”指的是向后。這并不是我想實現(xiàn)的行為,我希望的是“筆直向上”的方向(0,1,0),不依賴于相機(jī)的方向。

現(xiàn)在當(dāng)你運(yùn)行程序,你能使用WASDX,和Z鍵來向前移動,向左移動,向后移動,向右移動,向上移動和向下移動。觀察時不會因為相機(jī)移動而改變方向,這個將留個鼠標(biāo)來控制。

鼠標(biāo)輸入

此時,我們的窗口還無法捕捉鼠標(biāo)消息。你能看到鼠標(biāo)在窗口上移來移去。我希望它消失,并且不希望它移出窗口。為了實現(xiàn)這個,我們要改下GLFW的設(shè)置。

在我們捕獲鼠標(biāo)之前,讓我們先實現(xiàn)用取消鍵(Esc)退出程序。我不想再點(diǎn)擊關(guān)閉按鈕了,因為鼠標(biāo)隱藏,并且無法離開窗口。讓我們在AppMain主循環(huán)下放加上些代碼:

// run while the window is open double lastTime = glfwGetTime(); while(!glfwWindowShouldClose(gWindow)){ // process pending events glfwPollEvents(); // update the scene based on the time elapsed since last update double thisTime = glfwGetTime(); Update((float)(thisTime - lastTime)); lastTime = thisTime; // draw one frame Render(); // check for errors GLenum error = glGetError(); if(error != GL_NO_ERROR) std::cerr << "OpenGL Error " << error << std::endl; //exit program if escape key is pressed if(glfwGetKey(gWindow, GLFW_KEY_ESCAPE)) glfwSetWindowShouldClose(gWindow, GL_TRUE); } 

當(dāng)我們用glfwCreateWindow打開窗口這樣設(shè)置時,就可以捕獲鼠標(biāo)了:

// GLFW settings glfwSetInputMode(gWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwSetCursorPos(gWindow, 0, 0); 

這段代碼讓鼠標(biāo)消失了,并且將它移動到了像素坐標(biāo)(0,0)。在Update中,我們會獲取鼠標(biāo)位置來更新相機(jī),更新完后將鼠標(biāo)坐標(biāo)再次設(shè)為(0,0)。這種方式可以很方便的看出每幀鼠標(biāo)移動了多少,還要在當(dāng)鼠標(biāo)要移出窗口時停住它。在Update函數(shù)下面添加以下代碼:

//rotate camera based on mouse movement const float mouseSensitivity = 0.1f; double mouseX, mouseY; glfwGetCursorPos(gWindow, &mouseX, &mouseY); gCamera.offsetOrientation(mouseSensitivity * (float)mouseY, mouseSensitivity * (float)mouseX); glfwSetCursorPos(gWindow, 0, 0); //reset the mouse, so it doesn't go out of the window 

鼠標(biāo)的坐標(biāo)單位是像素,但相機(jī)方向是基于兩個角度。這就是為何我們使用mouseSensitivity變量來將像素轉(zhuǎn)為角度。越大的鼠標(biāo)靈敏度,相機(jī)轉(zhuǎn)向的越快,越小的靈敏度,轉(zhuǎn)向的越慢。靈敏度設(shè)為0.1f的含義就是每10像素就旋轉(zhuǎn)1°。

offsetOrientation函數(shù)類似于offsetPosition函數(shù),它會使用水平和垂直角度來更新相機(jī)方向。

好了!基本到這就完成了。你現(xiàn)在運(yùn)行程序的話,你能繞著飛行并且幾乎能觀察任意方向。立方體的旋轉(zhuǎn)動畫可能會讓你在環(huán)繞時失去方向感,我們可以關(guān)閉它。

用鼠標(biāo)滾輪控制視野

就像蛋糕上的糖衣一樣,我們可以滾動鼠標(biāo)或者在觸摸板上滑動來實現(xiàn)相機(jī)鏡頭的視野縮放。上篇文章我們已經(jīng)解釋過視野的概念了。

我們使用同樣的方式來使用鼠標(biāo)位置,并且每幀重置滾動值。首先我們創(chuàng)建一個全局變量來保存滾動值:

double gScrollY = 0.0; 

使用GLFW來接受滾輪消息,首先我們得創(chuàng)建個回調(diào):

// records how far the y axis has been scrolled void OnScroll(GLFWwindow* window, double deltaX, double deltaY) { gScrollY += deltaY; } 

然后我們用GLFW在AppMain中注冊下回調(diào):

glfwSetScrollCallback(gWindow, OnScroll); 

當(dāng)每幀我們渲染的時候,我們使用gScrollY值來更改視野。代碼放在Update函數(shù)的下放:

const float zoomSensitivity = -0.2f; float fieldOfView = gCamera.fieldOfView() + zoomSensitivity * (float)gScrollY; if(fieldOfView < 5.0f) fieldOfView = 5.0f; if(fieldOfView > 130.0f) fieldOfView = 130.0f; gCamera.setFieldOfView(fieldOfView); gScrollY = 0; 

zoomSensitivity常量類似mouseSensitivity常量。視野取值范圍是0°到180°,但假如你設(shè)置的值離上下限很近的話,3D場景看上去會很奇怪,所以我們限制這個值范圍在5°到130°。類似鼠標(biāo)位置的方法,我們在每幀之后設(shè)置gScrollY = 0

下篇預(yù)告

下一篇文章,我們會重構(gòu)代碼來實現(xiàn)最最基本的“引擎”。我們會將代碼分為資產(chǎn)(資源)和實例,類似典型的3D引擎,可以生成有多個略微不同的木箱子的3D場景。

更多資源

posted on 2015-09-01 17:38 威士忌 閱讀(1978) 評論(1)  編輯 收藏 引用

Feedback

# re: 現(xiàn)代OpenGL教程 04 - 相機(jī),向量,輸入 2015-09-02 14:41 Luna
支持一下,更新了。  回復(fù)  更多評論
  


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久精品亚洲一区二区三区浴池| 亚洲国产一区二区三区在线播 | 午夜精品一区二区三区电影天堂| 欧美日韩在线免费观看| 另类国产ts人妖高潮视频| 久久精品国产视频| 久久综合五月| 欧美日韩一区二区三区四区在线观看| 亚洲天堂激情| 亚洲欧美在线一区二区| 久久久国产一区二区三区| 欧美1区免费| 欧美日韩小视频| 国产一区自拍视频| 亚洲高清激情| 午夜视频一区在线观看| 亚洲欧美一区二区三区极速播放| 99国产精品自拍| 国产精品一区二区在线| 亚洲丰满在线| 美女精品自拍一二三四| 欧美激情一区二区久久久| 国产日韩成人精品| 国产一区免费视频| 一级成人国产| 亚洲国产综合在线看不卡| 亚洲综合精品一区二区| 免费亚洲电影| 国产日韩欧美高清免费| 欧美一区观看| 久久久久高清| 亚洲美女毛片| 免费不卡在线视频| 亚洲一区精品视频| 99精品国产一区二区青青牛奶| 午夜精品影院在线观看| 久久久91精品国产| 欧美大片免费观看在线观看网站推荐| 日韩午夜电影| 国产精品久久久久高潮| 欧美日韩中文| 99热精品在线| 黄色一区二区在线| 欧美专区日韩专区| 久久免费黄色| 性做久久久久久免费观看欧美| 欧美激情第一页xxx| 欧美日韩第一区| 欧美不卡激情三级在线观看| 国产精品最新自拍| 日韩写真在线| 在线日韩av永久免费观看| 亚洲高清免费在线| 欧美日韩一区三区| 欧美顶级艳妇交换群宴| 亚洲尤物视频网| 亚洲在线观看免费| 最新国产成人av网站网址麻豆| 久久久精品国产一区二区三区| 久久久久网站| 欧美性猛交视频| 欧美日韩精品二区第二页| 久久精品日韩| 国产精品v日韩精品| 亚洲精品黄网在线观看| 亚洲高清在线精品| 亚洲免费综合| 欧美精品七区| 91久久久精品| 欧美成人亚洲成人| 久久精品视频在线观看| 欧美三级午夜理伦三级中视频| 中国成人在线视频| 亚洲在线免费观看| 欧美—级高清免费播放| 久热精品视频在线观看| 老色鬼精品视频在线观看播放| 老牛影视一区二区三区| 亚洲三级视频| 日韩亚洲成人av在线| 国产精品乱码一区二区三区| 翔田千里一区二区| 久久精品国产一区二区三 | 亚洲人成在线影院| 亚洲国产精品va在线看黑人 | 亚洲精品久久久久久久久久久| 亚洲影视中文字幕| 国产午夜精品理论片a级探花| 亚洲人成网站在线观看播放| 欧美好骚综合网| 欧美午夜精品久久久久免费视 | 欧美激情视频在线播放| 日韩午夜av| 亚洲欧美色婷婷| 亚洲国产美女| 亚洲综合第一页| 91久久在线视频| 性亚洲最疯狂xxxx高清| 亚洲精一区二区三区| 午夜一区二区三区不卡视频| 亚洲人午夜精品| 亚洲午夜在线观看| 韩日视频一区| 久久黄金**| 欧美激情综合五月色丁香| 在线亚洲精品| 夜夜嗨av一区二区三区免费区| 久久久久青草大香线综合精品| 国产精品美女午夜av| 中文网丁香综合网| 性欧美激情精品| 亚洲一区欧美二区| 免播放器亚洲| 玖玖精品视频| 国产欧美一区二区三区在线老狼 | 国产精品视频网址| 欧美激情久久久| 欧美成人精品福利| 亚洲国产精品悠悠久久琪琪| 久久国产精品72免费观看| 老司机凹凸av亚洲导航| 欧美一区二区三区在线播放| 欧美成人免费va影院高清| 久久久精品国产99久久精品芒果| 欧美在线视频免费| 国产精品免费福利| 亚洲精品中文在线| 亚洲欧洲日产国产网站| 久久久久综合网| 国语自产在线不卡| 激情六月婷婷久久| 亚洲一区日韩在线| 在线视频欧美日韩| 亚洲精品国产精品乱码不99 | 亚洲男人的天堂在线| 日韩午夜在线电影| 美女诱惑黄网站一区| 久久免费视频在线观看| 欧美一区二区久久久| 亚洲一区欧美一区| 欧美日韩免费高清一区色橹橹| 午夜精品久久久久久久99水蜜桃| 亚洲人成77777在线观看网| 一区二区三区我不卡| 欧美一区二区女人| 午夜精品www| 国产精品99一区| 另类人畜视频在线| 激情综合色丁香一区二区| 欧美激情国产日韩精品一区18| 欧美成年人视频网站| 亚洲性图久久| 欧美一区二区视频观看视频| 欧美在线黄色| 欧美日本国产精品| 一个色综合av| 欧美一区视频在线| 在线观看视频一区| 欧美高清视频在线播放| 亚洲免费在线观看视频| 国产日韩欧美在线观看| 久久蜜臀精品av| 亚洲伊人网站| 国产女主播一区二区| 国产精品欧美风情| 亚洲欧美综合精品久久成人| 亚洲午夜一区二区三区| 欧美日韩午夜视频在线观看| 亚洲永久精品大片| 久久婷婷国产综合国色天香| 欧美日韩国产一区二区| 亚洲一区久久| 欧美风情在线观看| 午夜国产精品视频免费体验区| 欧美精品一区在线播放| 亚洲尤物在线视频观看| 99在线精品免费视频九九视| 欧美日韩精品免费观看| 欧美91大片| 激情五月婷婷综合| 亚洲欧美日本视频在线观看| 免费成人av| 午夜精品区一区二区三| 亚洲人成高清| 国产真实久久| 欧美色另类天堂2015| 久久久亚洲国产天美传媒修理工| 久久国产精彩视频| 国产欧美一区在线| 欧美成人三级在线| 久久久久久色| 西瓜成人精品人成网站| 欧美一区二区国产| 亚洲美女色禁图| 欧美日韩亚洲激情| 亚洲日韩视频| 免费美女久久99| 久久精品综合一区| 欧美在线播放高清精品| 亚洲欧美日本在线|