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

WisKeyのLullaby

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

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

公告

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

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

常用鏈接

留言簿(1)

我參與的團隊

搜索

  •  

積分與排名

  • 積分 - 51755
  • 排名 - 447

最新評論

閱讀排行榜

評論排行榜

http://huangwei.pro/2015-08/modern-opengl3/

 

本文中,我會將不會動的2D三角形替換為旋轉(zhuǎn)的3D立方體。你會看到這樣的效果:

 

現(xiàn)在我們終于能在屏幕上搞點有趣的東西了,我放了更多的動圖在這里:http://imgur.com/a/x8q7R

為了生成旋轉(zhuǎn)立方體,我們需要學(xué)些關(guān)于矩陣的數(shù)學(xué),用于創(chuàng)建透視投影,旋轉(zhuǎn),平移和“相機”概念。我們還有必要學(xué)習(xí)些深度緩沖,和典型的隨時間改變的3D應(yīng)用,比如動畫。

獲取代碼

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

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

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

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

矩陣原理

本文講的最多的就是關(guān)于3D中的矩陣,所以讓我們在寫代碼前先了解下矩陣原理。我不會過多關(guān)注數(shù)學(xué),網(wǎng)上有很多好的這類資源。我們只需要使用GLM來實現(xiàn)相關(guān)運算。我會注重于那些應(yīng)用在我們3D程序里的矩陣。

矩陣是用來進行3D變換。可能的變換包括(點擊可以看動畫):

一個矩陣是一個數(shù)字表格,像這樣:

 

矩陣英文matrix的復(fù)數(shù)形式是matrices。

不同的數(shù)值的能產(chǎn)生不同類型的變換。上面的那個矩陣會繞著Z軸旋轉(zhuǎn)90°。我們會使用GLM來創(chuàng)建矩陣,所以我們不用理解如何計算出這些數(shù)值。

矩陣可以有任意行和列,但3D變換使用4×4矩陣,就像上面看到的那樣。無論我在那說到“矩陣”,指的就是4×4矩陣。

當(dāng)用代碼實現(xiàn)矩陣時,一般會用一個浮點數(shù)組來表示。我們使用glm::mat4類來表示4×4矩陣。

兩個最重要的矩陣操作是:

  • matrix × matrix = combined matrix
  • matrix × coordinate = transformed coordinate

矩陣 × 矩陣

當(dāng)你要對兩個矩陣進行相乘時,它們的乘積是一個包含兩者變換的新矩陣。

比如,你將一個旋轉(zhuǎn)矩陣乘以一個平移矩陣,得到的結(jié)果就是“組合”矩陣,即先旋轉(zhuǎn)然后平移。下面的例子展示這類矩陣相乘。

 

不像普通的乘法,矩陣乘法中順序很重要。 比如,AB是矩陣,A*B不一定等于B*A。下面我們會使用相同的矩陣,但改變下乘法順序:

 

注意不同的順序,結(jié)果也不同。下面動畫說明順序有多重要。相同的矩陣,不同的順序。兩個變換分別是沿Y軸上移,和旋轉(zhuǎn)45°。

 

 

當(dāng)你編碼的時候,假如看到變換出錯,請回頭檢查下你的矩陣運算是否是正確的順序。

矩陣 × 坐標(biāo)

當(dāng)你用矩陣乘以一個坐標(biāo)時,它們的乘積就是一個變換后的新坐標(biāo)。

比如,你有上面提到的旋轉(zhuǎn)矩陣,乘上坐標(biāo)(1,1,0),它的結(jié)果就是(-1,1,0)。變換后的坐標(biāo)就是原始坐標(biāo)繞著Z周旋轉(zhuǎn)90°。下面是該乘法的圖例:

 

為何我們會使用4D坐標(biāo)

你可能注意到了上面的坐標(biāo)是4D的,而非3D。它的格式是這樣的:

 

為何我們會使用4D坐標(biāo)?因為我們需要用4x4的矩陣完成所有我們需要的3D變換。不管怎樣,矩陣乘法需要左邊的列數(shù)等于右邊的行數(shù)。這就意味著4x4矩陣無法與3D坐標(biāo)相乘,因為矩陣有4列,但坐標(biāo)只有3行。我們需要使用4D坐標(biāo),因為4x4的矩陣需要用它們來完成矩陣運算。

一些變換,比如旋轉(zhuǎn),縮放,只需要3x3矩陣。對于這些變換,我們不需要4D坐標(biāo),因為3D坐標(biāo)就能運算。但無論如何,變換需要至少是4x3的矩陣,而透視投影矩陣需要4x4矩陣,而我們兩者都會用到,所以我們強制使用4D。

這些被稱為齊次坐標(biāo)。在后續(xù)的教程里,我們會講到有向光照,那里我們會學(xué)到有關(guān)“W”維度的表示。在這里,我們只需要將3D轉(zhuǎn)換為4D。3D轉(zhuǎn)換為4D只要將第四維坐標(biāo)“W”設(shè)為1即可。比如,坐標(biāo)(22,33,44)轉(zhuǎn)換為:

 

當(dāng)需要將4D坐標(biāo)變?yōu)?D時,假如“W”維度是1,你可以直接忽略它,使用X,Y,Z的值即可。如果你發(fā)現(xiàn)“W”的值不為1,好吧,你就需要做些額外處理,或者這里出了個bug。

構(gòu)造一個立方體

代碼上第一個變動就是用立方體替換之前的三角形。

我們用三角形來構(gòu)造立方體,用兩個三角形表示6個面的每個面。在舊版本的OpengGL中,我們可以使用1個正方形(GL_QUADS)來替代2個三角表示每個面,但GL_QUADS已經(jīng)被現(xiàn)代版本的OpenGL給移除了。X,Y,Z坐標(biāo)值域為-1到1,這意味著立方體是兩個單位寬,立方體中心點在原點(原點坐標(biāo)(0,0,0))。我們將使用256×256的貼圖給立方體每個面貼上。后序文章中都會使用這個數(shù)據(jù),我們不需要改變太多。這里有立方體數(shù)據(jù):

GLfloat vertexData[] = { //  X     Y     Z       U     V // bottom -1.0f,-1.0f,-1.0f, 0.0f, 0.0f, 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, -1.0f,-1.0f, 1.0f, 0.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, -1.0f,-1.0f, 1.0f, 0.0f, 1.0f, // top -1.0f, 1.0f,-1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 0.0f, 1.0f, 1.0f,-1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, // front -1.0f,-1.0f, 1.0f, 1.0f, 0.0f, 1.0f,-1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, // back -1.0f,-1.0f,-1.0f, 0.0f, 0.0f, -1.0f, 1.0f,-1.0f, 0.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, -1.0f, 1.0f,-1.0f, 0.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, // left -1.0f,-1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f,-1.0f, 1.0f, 0.0f, -1.0f,-1.0f,-1.0f, 0.0f, 0.0f, -1.0f,-1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, 1.0f, 0.0f, // right 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, 1.0f, 1.0f,-1.0f, 0.0f, 0.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f }; 

我們需要更改下Render函數(shù)中glDrawArrays調(diào)用,之前是用來繪制三角形的。立方體6個面,每個面有2個三角形,每個三角形有3個頂點,所以需要繪制的頂點數(shù)是:6 × 2 × 3 = 36。新的glDrawArrays調(diào)用像這樣:

glDrawArrays(GL_TRIANGLES, 0, 6*2*3); 

最后,我們使用新的貼圖“wooden-crate.jpg”,我們更改LoadTexture中的文件名,如下:

tdogl::Bitmap bmp = tdogl::Bitmap::bitmapFromFile(ResourcePath("wooden-crate.jpg")); 

就是這樣!我們已經(jīng)提供了所有繪制帶貼圖立方體的需要用到的數(shù)據(jù)。假如你運行程序,你可以看到這樣的:

 

此時此刻,我們有兩個問題。第一,這個立方體看上去非常2D,因為我們只看到了一個面。我們需要“移動相機”,以不同角度觀察這個立方體。第二,上面有些問題,因為立方體寬和高應(yīng)該相等,但從截圖看上去寬度明顯比高度大。為了修復(fù)這兩個問題,我們需要學(xué)習(xí)更多的矩陣知識,和如何應(yīng)用到3D程序中。

裁剪體 - 默認相機

為了理解3D中的“相機”,我們首先得理解裁剪體。

裁剪體是一個立方體。無論什么東西在裁剪體中的都會顯示在屏幕上,任何在裁剪體之外的都不會顯示。裁剪體跟我們上面的立方體是相同大小,它的X,Y,Z坐標(biāo)值域也是從-1到+1。-X表示左邊,+X表示右邊,-Y是底部,+Y是頂部,+Z是遠離相機,-Z是朝著相機。

因為我們的立方體和裁剪體一樣大,所以我們只能看到立方體的正面。

這也解釋了為何我們的立方體看起來比較寬。窗口顯示了裁剪體里的所有東西。窗口的左右邊緣是X軸的-1和+1,窗口的底部和頂部邊緣是Y軸的-1和+1。裁剪體被拉伸了,用來跟窗口的可視大小相適應(yīng),所以我們的立方體看上去不是正方形的。

固定住相機,讓世界移動起來

我們需要移動相機,使得可以從不同角度進行觀察,或放大縮小。但不管怎樣,裁剪體不會更改。它永遠是一樣的大小和位置。所以我們換種方式來替代移動相機,我們可以移動3D場景讓它正確得出現(xiàn)在裁剪體中。比如,我們想要讓相機往右旋轉(zhuǎn),我們可以把整個世界往左旋轉(zhuǎn)。假如我們想要讓相機離玩家近些,我們可以把玩家挪到相機前。這就是“相機”在3D中的工作方式,變換整個世界使得它出現(xiàn)在裁剪體中并且看上去是正確的。

無論你走到哪里,都會覺得是世界沒動,是你在移動。但你也能想象出當(dāng)你不動,而世界在你腳下滾動,就像你在跑步機上一樣。這就是“移動相機”和“移動世界”的區(qū)別,這兩種方式,對于觀察者而言,看上去都是一樣的。

我們?nèi)绾螌?D場景進行變換來適應(yīng)裁剪體呢?這里我們需要用到矩陣。

實現(xiàn)相機矩陣

讓我們先來實現(xiàn)相機矩陣。3D中“相機”的解釋可認為是對3D場景的一系列變換。因為相機就是一個變換,所以我們可以用矩陣來表示。

首先,我們需要包含GLM頭文件,用來創(chuàng)建不同類型的矩陣。

#include <glm/gtc/matrix_transform.hpp> 

接著,我們需要更新頂點著色器。我們創(chuàng)建一個相機矩陣變量叫做camera,并且每個頂點都會乘上這個相機矩陣。這樣我們就將整個3D場景進行了變換。每個頂點都會被相機矩陣所變換。新的頂點著色器看上去應(yīng)該是這樣的:

#version 150  uniform mat4 camera; //this is the new variable  in vec3 vert; in vec2 vertTexCoord;  out vec2 fragTexCoord;  void main() { // Pass the tex coord straight through to the fragment shader fragTexCoord = vertTexCoord; // Transform the input vertex with the camera matrix gl_Position = camera * vec4(vert, 1); } 

現(xiàn)在我們需要在C++代碼中設(shè)置camera著色器變量。在LoadShaders函數(shù)的地步,我們添加這樣的代碼:

gProgram->use();  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(); 

這個相機矩陣在本文中不會再被改變,當(dāng)所有著色器被創(chuàng)建后,我們只需這樣設(shè)置一次。

你無法在設(shè)置著色器變量,除非著色器在使用中,這就是為何我們用到了gProgram->use()gProgram->stopUsing()

我們使用glm::lookAt函數(shù)為我們創(chuàng)建相機矩陣。假如你使用的是舊版本的OpenGL,那你應(yīng)該使用gluLookAt函數(shù)來達到相同目的,但gluLookAt已經(jīng)在最近的OpenGL版本中被移除了。第一個參數(shù)glm::vec3(3,3,3)是相機的位置。第二個參數(shù)glm::vec3(0,0,0)是相機觀察的點。立方體中心是(0,0,0),相機就朝著這個點觀察。最后一個參數(shù)glm::vec3(0,1,0)是“向上”的方向。我們需要垂直擺放相機,所以我們設(shè)置“向上”是沿著Y軸的正方向。假如相機是顛倒或者傾斜的,這里就是其它值了。

在我們生成了相機矩陣后,我們用gProgram->setUniform("camera", camera);來設(shè)置camera著色器變量,setUniform方法屬于tdogl::Program類,它會調(diào)用glUniformMatrix4fv來設(shè)置變量。

就是這樣!我們現(xiàn)在有了一個可運行的相機。

不幸的是,假如你現(xiàn)在運行程序,你會看到整個都是黑屏。因為我們的立方體頂點經(jīng)過相機矩陣變換后,飛出了裁剪體。這就是上面我提到的,在裁剪體之外的它是不會被顯示。為了能再次看到它,我們需要設(shè)置投影矩陣

實現(xiàn)投影矩陣

記住裁剪體只有2個單元寬、高和深。假設(shè)1個單元等于我們3D場景中的1米。這就意味著我們在相機中能看到正前方2米,這樣不是很方便。

我們需要擴大裁剪體使得能看到3D場景中的更多東西,可憐我們又不能改變裁剪體的大小,但,我們能縮小整個場景。縮小是一個變換,所以我們用矩陣來表示,基本上說,投影矩陣就是用來干這個的。

讓我們在頂點著色器中加入投影矩陣變量。更新后的代碼看上去是這樣的:

#version 150  uniform mat4 projection; //this is the new variable uniform mat4 camera;  in vec3 vert; in vec2 vertTexCoord;  out vec2 fragTexCoord;  void main() { // Pass the tex coord straight through to the fragment shader fragTexCoord = vertTexCoord; // Apply camera and projection transformations to the vertex gl_Position = projection * camera * vec4(vert, 1); } 

注意矩陣相乘的順序:projection * camera * vert。相機變換是放在首位的,投影矩陣是第二位。矩陣乘法中,變換從右往左,從頂點角度說是從最近的變換到更早前的變換。

現(xiàn)在讓我們在C++代碼中設(shè)置projection著色器變量,方式和我們設(shè)置camera變量相同。在LoadShaders函數(shù)中,添加如下代碼:

glm::mat4 projection = glm::perspective(glm::radians(50.0f), SCREEN_SIZE.x/SCREEN_SIZE.y, 0.1f, 10.0f); gProgram->setUniform("projection", projection); 

假如你使用的是舊版本OpenGL,你可以使用gluPerspective來設(shè)置投影矩陣,同樣gluPerspective函數(shù)在最近版本的OpenGL中也被移除了。幸運的是你可以使用glm::perspective來替代。

glm::perspective第一個參數(shù)是“可視區(qū)域”參數(shù)。這個參數(shù)是個弧度,用來說明相機視野有多寬。弧度換算我們可以用glm::radians函數(shù)來將50度轉(zhuǎn)換為弧度。大的可視區(qū)域意味著我們的相機可以看到更多場景,看上去就像是縮小了。小的可視區(qū)域意味著相機只能看到場景的一小部分,看上去像是放大了。第二個參數(shù)是“縱橫比”,該參數(shù)表示可視區(qū)域的縱橫比率。一般該參數(shù)設(shè)置為窗口的width/height,倒數(shù)第二個參數(shù)是“近平面”,近平面是裁剪體的前面,0.1表示近平面離相機是0.1單位遠。任何離相機小于0.1單位的物體均不可見。近平面的值必須大于0。最后一個參數(shù)是“遠平面”,遠平面是裁剪體的后面。10.0表示相機所顯示的物體均離相機10個單位之內(nèi)。任何大于10單位的物體均不可見。我們的立方體是3單位遠,所以它能被看見。

glm::perspective對將可視錐體對應(yīng)到裁剪體中非常有用。一個錐體像是一個金字塔被砍掉了頂端。金字塔的底部就是遠平面,頂部就是近平面。可視區(qū)域就是該錐體胖瘦。任何在錐體里的物體都會被顯示,而不再內(nèi)的就隱藏。

 

有了相機矩陣和投影矩陣的組合,我們就可以看到立方體了。運行程序你會看到:

 

這看上去。。。幾乎是對的。

這個立方體看上去已經(jīng)是正方形了,不再是矩形。這是因為glm::perspective中的“縱橫比”參數(shù),能夠基于窗口的寬和高進行正確的調(diào)整比例。

不幸的是,截圖看上去立方體的背面渲染并覆蓋到前面來了。我們當(dāng)然不希望發(fā)生這樣的事,我們需要開啟深度緩沖來解決。

深度緩沖

OpenGL默認會將最新的繪制覆蓋到之前的繪制上。假如一個物體的背面在前面之后繪制,就會發(fā)生背面擋住前面。深度緩沖就是為了防止背景層覆蓋到前景層的東西。

假如深度緩沖被開啟,每個被繪制的像素到相機的距離都是可知的。這個距離會以一個數(shù)值保存在深度緩沖里。當(dāng)你繪制一個像素在另外一個已存在的像素上時,OpenGL會查找深度緩沖來決定哪個像素應(yīng)該離相機更近。假如新的像素離相機更近,那該像素點就會被重寫。假如之前的像素離相機更近,那新像素就會被拋棄。所以,一個之前已存在的像素只會當(dāng)新像素離相機更近時才會被重寫。這就叫做“深度測試”。

實現(xiàn)深度緩沖

AppMain函數(shù)中,調(diào)用了glewInit之后,我們添加如下代碼:

glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); 

這告訴OpenGL開啟深度測試。調(diào)用glDepthFunc是表明假如像素離相機的距離小于之前的像素距離時應(yīng)該被重寫。

最后一步我們需要在渲染每幀之后清理深度緩沖。假如我們不清理,舊的像素距離會保存在緩沖中,這樣會影響到繪制新的一幀。在Render函數(shù)里,我們改變glClear來實現(xiàn)它:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

 

旋轉(zhuǎn)立方體

假如你完成了上述例子,祝賀你走了這么遠!最后我們來實現(xiàn)會旋轉(zhuǎn)的立方體動畫。

如何實現(xiàn)旋轉(zhuǎn)?你會猜到:另外一個矩陣。這與之前的矩陣不同的是,這個矩陣是每幀都在改變,之前的矩陣都是常量。

我需要新建一個“模型”矩陣。在常見的3D引擎中,每個物體都有一個模型矩陣。相機和投影矩陣對整個場景來說是一樣的,但模型矩陣是每個物體都不同。模型矩陣用來擺放每個物體在正確的位置上(平移),設(shè)置正確的面向(旋轉(zhuǎn)),或者改變物體大小(縮放)。我們只有一個物體在當(dāng)前3D場景上,所以,我們只需要一個模型矩陣。

讓我們添加一個model矩陣變量到頂點著色器,就像我們添加相機和投影一樣。最終版本的頂點著色器應(yīng)該是這樣的:

#version 150  uniform mat4 projection; uniform mat4 camera; uniform mat4 model; //this is the new variable  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 = projection * camera * model * vec4(vert, 1); } 

還是要注意矩陣相乘的順序。模型矩陣是vert變量最近的一次變換,意味著模型矩陣應(yīng)該第一個被使用,其次是相機,最后是投影。

現(xiàn)在我們需要設(shè)置新的model著色器變量。不像相機和投影變量,模型變量需要每幀都被設(shè)置,所以我們把它放在Render函數(shù)里。在gProgram->use()之后添加這樣的代碼:

gProgram->setUniform("model", glm::rotate(glm::mat4(), glm::radians(45.0f), glm::vec3(0,1,0))); 

我們使用glm::rotate函數(shù)創(chuàng)建一個旋轉(zhuǎn)矩陣。第一個參數(shù)是一個已存在的需要進行旋轉(zhuǎn)的矩陣。在這我們不需要對已存在的矩陣進行旋轉(zhuǎn),所以我們傳個新的glm::mat4對象就可以了。下一個參數(shù)是旋轉(zhuǎn)的角度,或者說是要旋轉(zhuǎn)多少度。現(xiàn)在讓我給它設(shè)置個45°。最后一個參數(shù)是旋轉(zhuǎn)的軸。想象下旋轉(zhuǎn)像是將物體插在叉子上,然后轉(zhuǎn)動叉子。叉子就是軸,角度就是你的轉(zhuǎn)動。在我們的例子中,我們使用垂直的叉子,所以立方體像在一個平臺上旋轉(zhuǎn)。

運行程序,你們看到立方體被旋轉(zhuǎn):

 

它還沒有轉(zhuǎn)動,因為矩陣沒有被更改-它永遠是旋轉(zhuǎn)了45°。最后一步就是讓它每幀都旋轉(zhuǎn)一下。

動畫

首先,添加一個新的全局變量叫gDegreesRotated

GLfloat gDegreesRotated = 0.0f; 

每幀,我們會輕微的增加gDegreesRotated,并且我們用它來計算新的旋轉(zhuǎn)矩陣。這樣就能達到動畫效果。我們需要做的就是更新,繪制,更新,繪制,更新,繪制,這樣一個模式。

讓我們創(chuàng)建一個Update函數(shù),用來每次增加gDegreesRotated

void Update() { //rotate by 1 degree gDegreesRotated += 1.0f; //don't go over 360 degrees while(gDegreesRotated > 360.0f) gDegreesRotated -= 360.0f; } 

我們需要每幀都調(diào)用一次Update函數(shù)。讓我們把它加入到AppMain的循環(huán)中,在調(diào)用Render之前。

while(glfwGetWindowParam(GLFW_OPENED)){ // process pending events glfwPollEvents(); // update the rotation animation Update(); // draw one frame Render(); } 

現(xiàn)在我們需要基于gDegreesRotated變量來重新計算模型矩陣。在Render函數(shù)中我們修改相關(guān)代碼來設(shè)置模型矩陣:

gProgram->setUniform("model", glm::rotate(glm::mat4(), glm::radians(gDegreesRotated), glm::vec3(0,1,0))); 

與之前唯一不同的是我們使用了gDegreesRotated來替換45°常量。

你現(xiàn)在運行程序能看到一個漂亮,平滑轉(zhuǎn)動的立方體動畫。唯一的問題就是轉(zhuǎn)動的速度很你的FPS幀率有關(guān)。假如FPS高,你的立方體旋轉(zhuǎn)的就快。假如FPS降低,那立方體旋轉(zhuǎn)的就慢些。這不夠理想。一個程序應(yīng)該能正確更新,而不在乎于運行的幀率。

基于時間的動畫

為了使程序跑起來更正確,不依賴于FPS,動畫應(yīng)該每秒更新,而非每幀更新。最簡單得方式就是對時間進行計數(shù),并相對上次更新時間來正確更新。讓我們改下Update函數(shù),增加個變量secondsElapsed

void Update(float secondsElapsed) { const GLfloat degreesPerSecond = 180.0f; gDegreesRotated += secondsElapsed * degreesPerSecond; while(gDegreesRotated > 360.0f) gDegreesRotated -= 360.0f; } 

這段代碼使得立方體每秒旋轉(zhuǎn)180°,而無關(guān)多少幀率。

AppMain循環(huán)中,我們需要計算離上次更新過去了多少秒。新的循環(huán)應(yīng)該是這樣:

double lastTime = glfwGetTime(); while(glfwGetWindowParam(GLFW_OPENED)){ // 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(); } 

glfwGetTime返回從程序啟動開始到現(xiàn)在所逝去的時間。

我們使用lastTime變量來記錄上次更新時間。每次迭代,我們獲取最新的時間存入變量thisTime。從上次更新到現(xiàn)在的差值就是thisTime - lastTime。當(dāng)更新結(jié)束,我們設(shè)置lastTime = thisTime以便下次循環(huán)迭代的時候很正常工作。

這是基于時間更新的最簡單方法。這里還有更好的更新方法,但我們還不需要搞得這么復(fù)雜。

下篇預(yù)告

下一篇,我們會使用tdogl::Camera類來實現(xiàn)用鍵盤操作第一人稱射擊類型的相機移動,可以用鼠標(biāo)觀察不同方向,或者用鼠標(biāo)滾輪來放大縮小。

更多資源

posted on 2015-08-14 17:03 威士忌 閱讀(1803) 評論(0)  編輯 收藏 引用

只有注冊用戶登錄后才能發(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>
            久久久99爱| 最新日韩中文字幕| 午夜在线成人av| 女主播福利一区| 久久精品国产综合| 国产一区二区三区四区三区四 | 国产一区二区无遮挡| 亚洲激情婷婷| 欧美激情亚洲自拍| 久久国产精品一区二区| 国产一区二区三区av电影| 亚洲欧美中日韩| 亚洲欧美激情视频| 国产一区二区三区四区在线观看 | 国产精品综合| 午夜欧美大尺度福利影院在线看| 亚洲精品日韩精品| 国产精品美女| 欧美在线国产| 久久精品99国产精品| 国产日韩精品一区二区三区 | 亚洲国产mv| 亚洲人成网站精品片在线观看| 久久综合九色综合久99| 亚洲国产精品黑人久久久| 国产精品久久久久久久久久三级| 在线一区欧美| 久久国产精品高清| 一区二区三区色| 久久精品国产第一区二区三区| 亚洲精品一级| 久久国产日韩| 亚洲女女女同性video| 欧美在线观看一区二区| 日韩午夜中文字幕| 午夜欧美大片免费观看| 亚洲精品国产精品乱码不99按摩| 亚洲淫片在线视频| 亚洲激情成人网| 欧美影院午夜播放| 欧美一区二区三区另类| 欧美日韩精品欧美日韩精品一| 久久嫩草精品久久久久| 国产精品成人在线| 亚洲最新中文字幕| 亚洲精品综合久久中文字幕| 欧美国产日本韩| 久久免费视频这里只有精品| 国产日韩三区| 欧美一区二区三区在线看 | 日韩香蕉视频| 最新日韩精品| 欧美日韩一区二区三区免费看| 亚洲福利在线看| 一本大道久久a久久精二百| 欧美精品久久久久久久免费观看 | 久久久不卡网国产精品一区| 亚洲欧美激情在线视频| 国产精品一区二区三区四区五区| 艳妇臀荡乳欲伦亚洲一区| 亚洲图片在线观看| 国产精品久久久久久久久久尿 | 亚洲自拍啪啪| av成人黄色| 欧美手机在线| 欧美福利电影在线观看| 亚洲一区二区三区免费观看| 狂野欧美性猛交xxxx巴西| 亚洲人成毛片在线播放女女| 国产精品影视天天线| 欧美激情第六页| 中文欧美字幕免费| 日韩一区二区精品| 午夜精彩视频在线观看不卡| 亚洲图片你懂的| 在线视频日韩精品| 亚洲一区精品电影| 久久免费精品日本久久中文字幕| 久久久久国产精品www| 亚洲午夜激情在线| 久久九九国产精品| 久久综合狠狠综合久久综合88| 亚洲调教视频在线观看| 久久国产日韩| 欧美日韩高清不卡| 国产精品黄色| 亚洲大胆人体在线| 一本久久知道综合久久| 国产一区二区三区在线播放免费观看| 欧美 日韩 国产一区二区在线视频| 亚洲欧美日韩国产成人精品影院| 在线亚洲欧美视频| 国产综合欧美在线看| 欧美日韩国产综合视频在线| 欧美一区二区啪啪| 久久国产一区二区| 久久中文字幕一区二区三区| 久久精品国产欧美亚洲人人爽| 亚洲欧美国产一区二区三区| 宅男噜噜噜66国产日韩在线观看| 亚洲国产精彩中文乱码av在线播放| 欧美国产成人精品| 久久久久久久一区| 欧美国产亚洲另类动漫| 亚洲精品免费在线播放| 亚洲天堂黄色| 久久9热精品视频| 欧美日韩成人精品| 国产性色一区二区| av成人动漫| 久久精品91久久久久久再现| 美女日韩欧美| 亚洲精品国产精品国自产观看| 一本大道av伊人久久综合| 久久久噜噜噜久久中文字免| 欧美激情精品久久久久久| 国产精品免费观看视频| 一区久久精品| 一区二区三区国产精华| 久久精品国产一区二区电影| 国产精品美女www爽爽爽视频| 国产精品成人一区二区三区夜夜夜| 国产视频综合在线| 亚洲天堂av在线免费| 久久综合久久久| 欧美一区二区视频在线| 欧美日韩一区二区精品| 亚洲国产福利在线| 久久亚洲一区二区| 久久午夜色播影院免费高清| 国产伪娘ts一区| 久久精品夜色噜噜亚洲aⅴ| 亚洲国产美女精品久久久久∴| 欧美一区永久视频免费观看| 91久久综合| 老牛嫩草一区二区三区日本 | 欧美精品aa| 亚洲电影在线观看| 久久久久久久精| 欧美一级一区| 狠狠色香婷婷久久亚洲精品| 亚洲午夜高清视频| 亚洲精品视频一区| 国产精品影音先锋| 夜夜爽99久久国产综合精品女不卡 | 久久精品二区| 久久人人超碰| 99精品欧美一区| 国产精品99久久久久久白浆小说| 久久夜色精品国产欧美乱| 亚洲精品社区| 久久成人精品无人区| 红桃av永久久久| 91久久在线播放| 狠狠色狠色综合曰曰| 亚洲欧洲视频| 国产午夜精品在线观看| 亚洲第一网站免费视频| 欧美视频中文字幕在线| 91久久精品国产91性色tv| 国产老肥熟一区二区三区| 亚洲人成亚洲人成在线观看| 国产一区二区三区成人欧美日韩在线观看| 一区二区三区精品久久久| 欧美一区二区三区日韩| 亚洲美女精品久久| 亚洲欧美欧美一区二区三区| 小嫩嫩精品导航| 国际精品欧美精品| 免费在线视频一区| 亚洲人成毛片在线播放| 午夜精品久久久久99热蜜桃导演| 国产曰批免费观看久久久| 欧美日韩成人综合| 久久精品欧美日韩精品| 一区二区免费看| 欧美xxxx在线观看| 欧美色欧美亚洲另类二区| 亚洲欧美日韩成人| 在线一区二区三区四区五区| 欧美国产专区| 午夜久久电影网| 亚洲国产三级网| 狠狠色丁香久久婷婷综合_中| 欧美激情综合五月色丁香小说| 宅男66日本亚洲欧美视频| 亚洲黄色一区| 免费一区视频| 亚洲美女黄网| 亚洲综合欧美| 国产精品欧美久久久久无广告| 久久综合色播五月| 欧美一级在线播放| 亚洲国产综合91精品麻豆| 欧美老女人xx| 欧美日韩国产综合视频在线观看中文| 欧美一区二区三区视频免费播放 | 欧美一区二区三区视频在线观看| 狠狠色狠狠色综合日日小说| 国产精品裸体一区二区三区|