|
電腦遊戲製作開發設計論壇 任何可以在PC上跑的遊戲都可以討論,主要以遊戲之製作開發為主軸,希望讓台灣的遊戲人有個討論、交流、教學、經驗傳承的園地
|
上一篇主題 :: 下一篇主題 |
發表人 |
內容 |
Director 偶而上來逛逛的過客
註冊時間: 2013-11-04 文章: 13
381.66 果凍幣
|
發表於: 2013-12-16, PM 2:37 星期一 文章主題: GLSL&GLUT 從環境設定開始的基礎教學(06) - Frame Buffer Object(FBO) |
|
|
引言回覆: |
※2015/7/5補充:
其實已經過很久了,GLee似乎已經沒再繼續更新,我在寫了這篇教學不久後也改用了GLEW取代Glee,它們的功能及函式名稱其實是幾乎一樣的,安裝方式同其他函式庫
所以只需要將glee.h及glee.lib改為glew.h及glew.lib就好,這是下載的地方,抓Binaries那個http://glew.sourceforge.net/。
lib用 lib\Release\Win32 裡面的。
|
前陣子忙著做繪圖引擎的光影和FXAA的優化,這篇稍晚了些
我們這次要做的東西是這個!
這樣可能看不出是什麼東西,實際上這個正方形裡面畫的是一個周圍場景的動畫(程式中是一直旋轉),你可以想像是一個攝影機那樣,而正方形裡就是攝影機傳來的畫面。
這個運用可以拿來做出遊戲中監視器的效果,它叫做Frame Buffer Object,下稱FBO。
當然它可以做的事情絕不只是這個,像是影片剪輯軟體中的濾鏡功能、遊戲裡的反鋸齒、全景泛光等等,大都可以透過這個搭配特定Shader來實現,是個很實用的好東西。
關於Frame Buffer我不會提太多,畢竟我這系列是想以實作的觀點來跟大家研究GLSL,請多見諒
引言回覆: |
那我們來簡單談談FBO,我們都知道畫面中的每一禎都是資訊經過計算後,送到Frame Buffer Processor,再送到畫面上來的。
而FBO最經常被拿來用的做法就是,把Frame Buffer Processor裡的資料截下來,然後存成材質再來做演算。
什麼意思呢?
大家可以想像成,在vertex的資料處理完之後,畫出來的圖,在把他們搬到螢幕上之前,我們還有機會針對整個畫面調整。
像是把畫面弄模糊、變清晰等等,各式各樣的濾鏡功能都是在這樣情形下運作的,當然大家熟悉的抗鋸齒AA(anti-aliasing)也是這樣實作的。
又或者結合我上一篇說的cubemap,他可以從反射物體的位置,取到動態的cubemap材質,從而做出可以反射移動中物體畫面的反射物。
它的用途非常非常多,像我自己就做過3D遊戲中的控制面板、監視器、全景泛光、抗鋸齒等等運用,有機會的話我今後會多紀錄一些,但還是推薦大家上原文網站查找,資料量真的不可比擬。
那今天我們就來做個簡易攝影機和屏幕吧!
|
那麼簡單說明下步驟:
Step1: 創建兩張全新的材質,一個深度一個色彩(用來儲存從Frame Buffer Processor拿到的資料)
Step2: 創建一個新的FBO,然後將剛剛創建的材質綁上去
Step3: 布置環境,把想要的畫面渲染到FBO的材質上
Step4: 把每一禎渲染出的材質貼在一個方形上,done!
開始看程式碼吧
引言回覆: | Step1
首先要做的是創建新材質,如何創建呢? 我們寫個函式來製作吧!
代碼: | unsigned int createTexture( int w, int h, bool isDepth )
{
unsigned int textureID;
glGenTextures( 1, &textureID ); // 向OpenGL登錄一張材質
glBindTexture( GL_TEXTURE_2D, textureID ); // 下面開始做材質設定
glTexImage2D( GL_TEXTURE_2D, 0, // 首參數是你要什麼樣的材質(GL_TEXTURE_2D), 第二參數level可以不管
isDepth ? GL_DEPTH_COMPONENT : GL_RGBA8, // 再來是根據引數進來的isDepth來判斷我們要深度還是色彩材質
w, h, 0, // 材質長寬, border可以不管
isDepth ? GL_DEPTH_COMPONENT : GL_RGBA, // 同上, 但有區別, 可參考, http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml
GL_FLOAT, NULL ); // 色彩資料存在float格式中, 最後我們不是導入image檔, 沒有圖像資料所以田NULL
// 材質設定算是OpenGL初學的知識我就不提了
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
// 簡易的錯誤回報
int error;
error = glGetError();
if( glGetError() != 0 ){
std::cout << "Error happened while loading the texture: " << gluErrorString(error) << std::endl;
}
glBindTexture( GL_TEXTURE_2D, 0 );
// 最後回傳生成好的材質ID
return textureID;
} |
大多都如註解
再來就是宣告兩張新材質囉。
代碼: | GLuint renderTex, depthTex;
void Initialize()
{
...
...
renderTex = createTexture( WINDOW_SIZE_W, WINDOW_SIZE_H, false );
depthTex = createTexture( WINDOW_SIZE_W, WINDOW_SIZE_H, true );
...
...
} |
第一步就到這邊。
|
引言回覆: | Step2
OK! 那我們再來看要如何建立一個新的FBO。
代碼: | GLuint FBO;
void Initialize()
{
...
...
glGenFramebuffers( 1, &FBO ); // 登錄一個Frame Buffer Object
glBindFramebuffer( GL_FRAMEBUFFER, FBO ); // 然後使用它, 下面選擇要存資料的材質
// GL_COLOR_ATTACHMENT0, 表示用來存色彩資料
// GL_TEXTURE_2D, 就是材質的性質(剛剛創建的是2D材質)
// level可以不管
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTex, 0 );
// GL_DEPTH_ATTACHMENT, 表是用來存深度資料
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0 );
glBindFramebuffer( GL_FRAMEBUFFER, 0 ); // 一般來說, FBO不使用的時候就會使用"0"來關閉
...
...
} |
FBO的建立沒什麼困難的,也沒有太多參數可以提
|
引言回覆: | Step3
很快的我們就跳到的渲染的階段了,這邊要有的概念很簡單,被包在:
代碼: | glBindFramebuffer( GL_FRAMEBUFFER, FBO ); |
以及
代碼: | glBindFramebuffer( GL_FRAMEBUFFER, 0 ); |
之間的所有畫面,都會渲染到剛剛綁的那兩張材質上面!
你想要讓攝影機顯現出什麼畫面,你就先把你的視野移動到那個畫面,記下那時候的攝影機狀態(view matrix),然後到FBO下面完整的畫一遍那個畫面,這樣畫面就會存到renderTex了!
以我的例子,我設定了一個新的camera,並且讓它每一禎旋轉1度,最後把這個camera看的到畫面畫到正方形上面,我再從別的camera來看這塊正方形,這就是攝影機了!
於是首先要做的就是佈置場景,調整你想要在螢幕上看到的視野(view matrix)
代碼: | float angle = 0.0f;
void Display()
{
angle += 1.0f;
if( angle >= 360 )
angle -= 360;
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glBindFramebuffer( GL_FRAMEBUFFER, FBO ); // 使用FBO
// 綁定色彩資料材質
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTex, 0 );
// 綁定深度資料材質
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0 );
// 我不想讓keyboard影響到攝影機的視野, 所以傳了一個假的keyboard狀態給newCamera
bool tmpKB[256] = {false};
newCamera.lookAt( newCamera.getPitch(), angle );
newCamera.Control( tmpKB, CAM_NO_MOUSE_INPUT );
// 畫點東西
drawSkybox();
glClear( GL_DEPTH_BUFFER_BIT );
newCamera.UpdateCamera();
// 然後結束FBO
glBindFramebuffer( GL_FRAMEBUFFER, 0 ); |
到這裡為止,我就完成了我想要看到的畫面,就是一個一直旋轉的攝影機看到的東西。
|
引言回覆: | Step4
然後最後一步,你只要向平常一樣把renderTex當作一張材質貼上正方形就好!
代碼: | // 要來畫新的東西, 記得重置視野的matrix
glLoadIdentity();
MainCamera.Control( stateKeyboard, CAM_NO_MOUSE_INPUT );
drawSkybox();
glClear( GL_DEPTH_BUFFER_BIT );
MainCamera.UpdateCamera();
glm::mat4 modelViewMatrix =
glm::rotate( -MainCamera.getPitch(), 1.0f, 0.0f, 0.0f ) *
glm::rotate( -MainCamera.getYaw(), 0.0f, 1.0f, 0.0f ) *
glm::translate( MainCamera.getLocation().x, MainCamera.getLocation().y, MainCamera.getLocation().z );
glm::vec4 worldLightPosition = modelViewMatrix * glm::vec4( lightPosition.x, lightPosition.y, lightPosition.z, 1.0f );
mainShader->useShader();
glActiveTexture( GL_TEXTURE0 );
// 全部跟上一章一樣, 有改的只有這邊, 貼上剛剛渲染的renderTex
glBindTexture( GL_TEXTURE_2D, renderTex );
glUniform1i( glGetUniformLocation( mainShader->getProgramID(), "tex" ), 0 );
glUniform3f( glGetUniformLocation( mainShader->getProgramID(), "lightPosition" ), worldLightPosition.x, worldLightPosition.y, worldLightPosition.z );
glUniform4f( glGetUniformLocation( mainShader->getProgramID(), "lambient" ), 0.4, 0.4, 0.4, 1.0 );
glUniform4f( glGetUniformLocation( mainShader->getProgramID(), "ldiffuse" ), 0.7, 0.7, 0.7, 1.0 );
glUniform4f( glGetUniformLocation( mainShader->getProgramID(), "lspecular" ), 1.0, 1.0, 1.0, 1.0 );
glBegin(GL_QUADS);
glNormal3f( 0, 0, 1 );
glTexCoord2f( 1, 1 );
glVertex3f(1, 1, -5);
glTexCoord2f( 0, 1 );
glVertex3f(-1, 1, -5);
glTexCoord2f( 0, 0 );
glVertex3f(-1, -1, -5);
glTexCoord2f( 1, 0 );
glVertex3f(1, -1, -5);
glEnd();
mainShader->delShader();
glutSwapBuffers();
} |
只要把myTex改成renderTex你就可以把剛剛FBO截下的材質貼到正方形上!
之後若有機會提到濾鏡的實作,概念就是把這張renderTex經過Shader處理過後再貼上,很簡單的道理,之後有機會再談囉
|
最後留下本章範例程式碼。
描述: |
|
下載 |
檔名: |
ch6.rar |
附件大小: |
106.71 KB |
下載次數: |
共 1236 次 |
|
|
回頂端 |
|
|
JeffreyMt 稍嫌羞澀的路人
註冊時間: 2015-08-13 文章: 1 來自: France 7.08 果凍幣
|
發表於: 2015-9-10, PM 11:37 星期四 文章主題: GLSLLUT 06 Frame Buffer ObjectFBO |
|
|
I am trying to modify the animation of Skeleton.Nif, but when I try to export, I get this error.
Error in Anim Buffer: Frame Out of Range 31 not in 1,20
anu clue of its meaning and how to workaround it?
|
|
回頂端 |
|
|
Takenaka 稍嫌羞澀的路人
註冊時間: 2016-01-04 文章: 1
7.58 果凍幣
|
發表於: 2016-1-4, PM 9:34 星期一 文章主題: |
|
|
感謝樓主,本人是今年開始才學習OPENGL的新人....
我只有基本的java知識,還有真的是微不足道C++知識(我同時只學習了C++大約3星期...)
因為自己不能明白學校的教學內容,所以來此學習C++,請多指教!
|
|
回頂端 |
|
|
|
|
您 無法 在這個版面發表文章 您 無法 在這個版面回覆文章 您 無法 在這個版面編輯文章 您 無法 在這個版面刪除文章 您 無法 在這個版面進行投票 您 可以 在這個版面附加檔案 您 可以 在這個版面下載檔案
|
|