轉(zhuǎn)自?http://www.cocoachina.com/game/20150811/12969.html
譯序
早前學(xué)OpenGL的時候還是1.x版本蔗衡,用的都是glVertex袍镀,glNormal等固定管線API廓奕。后來工作需要接觸DirectX9瞭亮,shader也只是可選項而已睛榄,跟固定管線一起混用著⌒偌現(xiàn)在工作內(nèi)容是手機(jī)游戲飘蚯,又轉(zhuǎn)到OpenGL ES熙参,發(fā)現(xiàn)OpenGL的世界已經(jīng)完全不同了艳吠,OpenGL ES 2.0版本開始就不再支持固定管線,只支持可編程管線孽椰。
國內(nèi)很多資料教程參差不齊昭娩,舊式接口滿天飛。在知乎看到這一系列教程弄屡,覺著挺好题禀,就想著一邊學(xué)順便翻譯下。畢竟手游市場的機(jī)遇和競爭壓力都在同比猛漲膀捷,多了解OpenGL ES肯定沒有壞處迈嘹。浮躁功利的環(huán)境下更需要懷著一顆寧靜致遠(yuǎn)的心去提高自身功底,長路漫漫全庸,與君共勉秀仲。
歡迎大家,這是現(xiàn)代OpenGL教程系列的第一篇壶笼。所有代碼都是開源的神僵,你可以在GitHub上下載:https://github.com/tomdalling/opengl-series
通過這篇教程,你將會學(xué)到如何在Windows下用Visual Studio 2013或Mac下用Xcode搭建OpenGL 3.2工程覆劈。該應(yīng)用包含一個頂點(diǎn)著色器(vertex shader)保礼,一個片段著色器(fragment shader)和使用VAO和VBO來繪制的三角形。該工程使用GLEW來訪問OpenGL API责语,用GLFW來處理窗口創(chuàng)建和輸入炮障,還有使用GLM進(jìn)行矩陣/矢量相關(guān)的數(shù)學(xué)運(yùn)算。
這聽上去有點(diǎn)無聊坤候,但搭建這樣的工程確實(shí)挺麻煩的胁赢,尤其對于初學(xué)者。只要解決完這問題白筹,我們就可以開始玩些有趣的東西了智末。
[TOC]
獲取代碼
所有例子代碼的zip打包可以從這里獲取:https://github.com/tomdalling/opengl-series/archive/master.zip徒河。
這一系列文章中所使用的代碼都存放在:https://github.com/tomdalling/opengl-series系馆。你可以在頁面中下載zip,加入你會git的話虚青,也可以復(fù)制該倉庫它呀。
本文代碼你可以在source/01_project_skeleton目錄里找到。使用OS X系統(tǒng)的棒厘,可以打開根目錄里的opengl-series.xcodeproj纵穿,選擇本文工程。使用Windows系統(tǒng)的奢人,可以在Visual Studio 2013里打開opengl-series.sln谓媒,選擇相應(yīng)工程。
工程里已包含所有依賴何乎,所以你不需要再安裝或者配置額外的東西句惯。如果有任何編譯或運(yùn)行上的問題,請聯(lián)系我支救。
關(guān)于兼容性的提醒
本文使用OpenGL 3.2抢野,但我會嘗試保持如下兼容:
向后兼容OpenGL 2.1
向前兼容OpenGL 3.X和4.X
兼容Android和iOS的OpenGL ES 2.0
因為OpenGL和GLSL存在許多不同版本,本文代碼不一定能做到100%上述兼容各墨。我希望能兼容99%指孤,并且不同版本之間只要輕微修改即可。
想要了解OpenGL和GLSL不同版本間的區(qū)別贬堵,這里很好得羅列了兼容列表恃轩。
Visual Studio下安裝
代碼在Windows 7 32位系統(tǒng),Visual Studio Express 2013(免費(fèi))下創(chuàng)建和測試黎做。你應(yīng)該可以打開解決方案并成功編譯所有工程叉跛。如果有問題請聯(lián)系我,或者將補(bǔ)丁發(fā)我蒸殿,我會更新工程筷厘。
Xcode下安裝
Xcode工程實(shí)在OSX 10.10系統(tǒng),Xcode 6.1下創(chuàng)建并測試的宏所。打開Xcode工程應(yīng)該可以成功編譯所有目標(biāo)酥艳。加入你無法成功編譯請聯(lián)系我。
Linux下安裝
Linux是基于SpartanJ楣铁。我在Ubuntu 12.04下簡單測試通過玖雁。
安裝GLM,GLFW和GLEW: sudo aptitude install libglm-dev libglew-dev libglfw-dev
進(jìn)入工程目錄:cd platforms/linux/01_project_skeleto
運(yùn)行makefile:make
運(yùn)行可執(zhí)行文件:bin/01_project_skeleton-debug
GLEW, GLFW和GLM介紹
現(xiàn)在你有了工程盖腕,就讓我們開始介紹下工程所用到的開源庫和為啥需要這些赫冬。
The OpenGL Extension Wrangler (GLEW)是用來訪問OpenGL 3.2 API函數(shù)的。不幸的是你不能簡單的使用#include來訪問OpenGL接口溃列,除非你想用舊版本的OpenGL劲厌。在現(xiàn)代OpenGL中,API函數(shù)是在運(yùn)行時(run time)確定的听隐,而非編譯期(compile time)补鼻。GLEW可以在運(yùn)行時加載OpenGL API。
GLFW允許我們跨平臺創(chuàng)建窗口,接受鼠標(biāo)鍵盤消息风范。OpenGL不處理這些窗口創(chuàng)建和輸入咨跌,所以就需要我們自己動手。我選擇GLFW是因為它很小硼婿,并且容易理解锌半。
OpenGL Mathematics (GLM)是一個數(shù)學(xué)庫,用來處理矢量和矩陣等幾乎其它所有東西寇漫。舊版本OpenGL提供了類似glRotate, glTranslate和glScale等函數(shù)刊殉,在現(xiàn)代OpenGL中,這些函數(shù)已經(jīng)不存在了州胳,我們需要自己處理所有的數(shù)學(xué)運(yùn)算记焊。GLM能在后續(xù)教程里提供很多矢量和矩陣運(yùn)算上幫助。
在這系列的所有教程中栓撞,我們還編寫了一個小型庫tdogl用來重用C++代碼遍膜。這篇教程會包含tdogl::Shader和tdogl::Program用來加載,編譯和鏈接shaders腐缤。
什么是Shaders捌归?
Shaders在現(xiàn)代OpenGL中是個很重要的概念。應(yīng)用程序離不開它岭粤,除非你理解了惜索,否則這些代碼也沒有任何意義。
Shaders是一段GLSL小程序剃浇,運(yùn)行在GPU上而非CPU巾兆。它們使用OpenGL Shading Language (GLSL)語言編寫,看上去像C或C++虎囚,但卻是另外一種不同的語言角塑。使用shader就像你寫個普通程序一樣:寫代碼,編譯淘讥,最后鏈接在一起才生成最終的程序。
Shaders并不是個很好的名字蒲列,因為它不僅僅只做著色。只要記得它們是個用不同的語言寫的蝗岖,運(yùn)行在顯卡上的小程序就行。
在舊版本的OpenGL中抵赢,shaders是可選的唧取。在現(xiàn)代OpenGL中划提,為了能在屏幕上顯示出物體,shaders是必須的腔剂。
為可能近距離了解shaders和圖形渲染管線驼仪,我推薦Durian Software的相關(guān)文章The Graphics Pipeline chapter掸犬。
那shaders實(shí)際上干了啥?這取決于是哪種shader绪爸。
Vertex Shaders
Vertex shader主要用來將點(diǎn)(x湾碎,y,z坐標(biāo))變換成不同的點(diǎn)奠货。頂點(diǎn)只是幾何形狀中的一個點(diǎn)介褥,一個點(diǎn)叫vectex,多個點(diǎn)叫vertices(發(fā)音為ver-tuh-seez)递惋。在本教程中柔滔,我們的三角形需要三個頂點(diǎn)(vertices)組成铣口。
Vertex Shader的GLSL代碼如下:
#version?150
invec3?vert;
void?main()?{
????//?does?not?alter?the?vertices?at?all
????gl_Position?=?vec4(vert,?1);
}
第一行#version 150告訴OpenGL這個shader使用GLSL版本1.50虎敦。
第二行in vec3 vert;告訴shader需要那一個頂點(diǎn)作為輸入庆亡,放入變量vert绿贞。
第三行定義函數(shù)main痊末,這是shader運(yùn)行入口秦效。這看上去像C恬涧,但GLSL中main不需要帶任何參數(shù)穷绵,并且不用返回void邓馒。
第四行g(shù)l_Position = vec4(vert, 1);將輸入的頂點(diǎn)直接輸出嘶朱,變量gl_Position是OpenGL定義的全局變量,用來存儲vertex shader的輸出光酣。所有vertex shaders都需要對gl_Position進(jìn)行賦值疏遏。
gl_Position是4D坐標(biāo)(vec4),但vert是3D坐標(biāo)(vec3)救军,所以我們需要將vert轉(zhuǎn)換為4D坐標(biāo)vec4(vert, 1)财异。第二個的參數(shù)1是賦值給第四維坐標(biāo)。我們會在后續(xù)教程中學(xué)到更多關(guān)于4D坐標(biāo)的東西缤言。但現(xiàn)在宝当,我們只要知道第四維坐標(biāo)是1即可,i可以忽略它就把它當(dāng)做3D坐標(biāo)來對待胆萧。
Vertex Shader在本文中沒有做任何事庆揩,后續(xù)我們會修改它來處理動畫俐东,攝像機(jī)和其它東西虏辫。
Fragment Shaders
Fragment shader的主要功能是計算每個需要繪制的像素點(diǎn)的顏色砌庄。
一個"fragment"基本上就是一個像素娄昆,所以你可以認(rèn)為片段著色器(fragment shader)就是像素著色器(pixel shader)萌焰。在本文中每個片段都是一像素谷浅,但這并不總是這樣的一疯。你可以更改某個OpenGL設(shè)置墩邀,以便得到比像素更小的片段磕蒲,之后的文章我們會講到這個辣往。
本文所使用的fragment shader代碼如下:
#version?150
out?vec4?finalColor;
void?main()?{
????//set?every?drawn?pixel?to?white
????finalColor?=?vec4(1.0,?1.0,?1.0,?1.0);
}
再次站削,第一行#version 150告訴OpenGL這個shader使用的是GLSL 1.50许起。
第二行finalColor = vec4(1.0, 1.0, 1.0, 1.0);將輸出變量設(shè)為白色。vec4(1.0, 1.0, 1.0, 1.0)是創(chuàng)建一個RGBA顏色惦积,并且紅綠藍(lán)和alpha都設(shè)為最大值狮崩,即白色。
現(xiàn)在诽凌,就能用shader在OpenGL中繪制出了純白色侣诵。在之后的文章中狱窘,我們還會加入不同顏色和貼圖训柴。貼圖就是你3D模型上的圖像幻馁。
編譯和鏈接Shaders
在C++中仗嗦,你需要對你的.cpp文件進(jìn)行編譯稀拐,然后鏈接到一起組成最終的程序德撬。OpenGL的shaders也是這么回事躲胳。
在這篇文章中用到了兩個可復(fù)用的類坯苹,是用來處理shaders的編譯和鏈接:tdogl::Shader和tdogl::Program粹湃。這兩個類代碼不多为鳄,并且有詳細(xì)的注釋,我建議你閱讀源碼并且去鏈接OpenGL是如何工作的歧斟。
什么是VBO和VAO构捡?
當(dāng)shaders運(yùn)行在GPU,其它代碼運(yùn)行在CPU時滑凉,你需要有種方式將數(shù)據(jù)從CPU傳給GPU畅姊。在本文中若未,我們傳送了一個三角的三個頂點(diǎn)數(shù)據(jù)粗合,但在更大的工程中3D模型會有成千上萬個頂點(diǎn)隙疚,顏色供屉,貼圖坐標(biāo)和其它東西伶丐。
這就是我們?yōu)槭裁葱枰猇ertex Buffer Objects (VBOs)和Vertex Array Objects (VAOs)哗魂。VBO和VAO用來將C++程序的數(shù)據(jù)傳給shaders來渲染辙芍。
在舊版本的OpenGL中,是通過glVertex庶灿,glTexCoord和glNormal函數(shù)把每幀數(shù)據(jù)發(fā)送給GPU的往踢。在現(xiàn)代OpenGL中峻呕,所有數(shù)據(jù)必須通過VBO在渲染之前發(fā)送給顯卡瘦癌。當(dāng)你需要渲染某些數(shù)據(jù)時,通過設(shè)置VAO來描述該獲取哪些VBO數(shù)據(jù)推送給shader變量热押。
Vertex Buffer Objects (VBOs)
第一步我們需要從內(nèi)存里上傳三角形的三個頂點(diǎn)到顯存中。這就是VBO該干的事娘锁。VBO其實(shí)就是顯存的“緩沖區(qū)(buffers)” - 一串包含各種二進(jìn)制數(shù)據(jù)的字節(jié)區(qū)域莫秆。你能上傳3D坐標(biāo)雷蹂,顏色杯道,甚至是你喜歡的音樂和詩歌党巾。VBO不關(guān)心這些數(shù)據(jù)是啥,因為它只是對內(nèi)存進(jìn)行復(fù)制署海。
Vertex Array Objects (VAOs)
第二步我們要用VBO的數(shù)據(jù)在shaders中渲染三角形医男。請記住VBO只是一塊數(shù)據(jù)镀梭,它不清楚這些數(shù)據(jù)的類型研底。而告訴OpenGL這緩沖區(qū)里是啥類型數(shù)據(jù),這事就歸VAO管榜晦。
VAO對VBO和shader變量進(jìn)行了連接抖剿。它描述了VBO所包含的數(shù)據(jù)類型牙躺,還有該傳遞數(shù)據(jù)給哪個shader變量。在OpenGL所有不準(zhǔn)確的技術(shù)名詞中脓恕,“Vertex Array Object”是最爛的一個炼幔,因為它根本沒有解釋VAO該干的事。
你回頭看下本文的vertex shader(在文章的前面)跺讯,你就能發(fā)現(xiàn)我們只有一個輸入變量vert刀脏。在本文中愈污,我們用VAO來說明“hi,OpenGL金麸,這里的VBO有3D頂點(diǎn)揍魂,我想要你在vertex shader時喜最,發(fā)三個頂點(diǎn)數(shù)據(jù)給vert變量瞬内〕娴”
在后續(xù)的文章中能真,我們會用VAO來說“hi,OpenGL卤档,這里的VBO有3D頂點(diǎn)蝙泼,顏色,貼圖坐標(biāo)劝枣,我想要你在shader時汤踏,發(fā)頂點(diǎn)數(shù)據(jù)給vert變量,發(fā)顏色數(shù)據(jù)給vertColor變量舔腾,發(fā)貼圖坐標(biāo)給vertTexCoord變量茎活。”
給使用上個OpenGL版本的用戶的提醒:
假如你在舊版本的OpenGL中使用了VBO但沒有用到VAO琢唾,你可能會不認(rèn)同VAO的描述。你會爭論說“頂點(diǎn)屬性”可以用glVertexAttribPointer將VBO和shaders連接起來盾饮,而不是用VAO采桃。這取決于你是否認(rèn)為頂點(diǎn)屬性應(yīng)該是VAO“內(nèi)置(inside)”的(我是這么認(rèn)為的)普办,或者說它們是否是VAO外置的一個全局狀態(tài)。3.2內(nèi)核和我用的AIT驅(qū)動中,VAO不是可選項 - 沒有VAO的封裝glEnableVertexAttribArray, glVertexAttribPointer和glDrawArrays都會導(dǎo)致GL_INVALID_OPERATION錯誤。這就是為啥我認(rèn)為頂點(diǎn)屬性應(yīng)該內(nèi)置于VAO,而非全局狀態(tài)的原因。3.2內(nèi)核手冊也說VAO是必須的,但我只聽說ATI驅(qū)動會拋錯誤。下面描述引用自OpenGL 3.2內(nèi)核手冊
所有與頂點(diǎn)處理有關(guān)的數(shù)據(jù)定義都應(yīng)該封裝在VAO里。 一般VAO邊界包含所有更改vertex array狀態(tài)的命令监署,比如VertexAttribPointer和EnableVertexAttribArray;所有使用vertex array進(jìn)行繪制的命令俏拱,比如DrawArrays和DrawElements;所有對vertex array狀態(tài)進(jìn)行查詢的命令(見第6章)么鹤。
不管怎樣,我也知道為啥會有人認(rèn)為頂點(diǎn)屬性應(yīng)該放在VAO外部。glVertexAttribPointer出現(xiàn)早于VAO,在這段時間里頂點(diǎn)屬性一直被認(rèn)為是全局狀態(tài)。你應(yīng)該能看得出VAO是一種改變?nèi)譅顟B(tài)的有效方法。我更傾向于認(rèn)為是這樣:假如你沒有創(chuàng)建VAO,那OpenGL通過了一個默認(rèn)的全局VAO挺份。所以當(dāng)你使用glVertexAttribPointer時探赫,你仍然是在VAO內(nèi)修改頂點(diǎn)屬性搁嗓,只不過現(xiàn)在從默認(rèn)的VAO變成你自己創(chuàng)建的VAO。
這里有更多的討論:http://www.opengl.org/discussion_boards/showthread.php/174577-Questions-on-VAOs
代碼解釋
終于!理論已經(jīng)說完了潘拨,我們開始編碼。OpenGL對于初學(xué)者而言不是特別友好濒生,但如果你理解了之前所介紹的概念(shaders,VBO,VAO)那你就沒啥問題。
打開main.cpp不脯,我們從main()函數(shù)開始。
首先肖揣,我們初始化GLFW:
glfwSetErrorCallback(OnError);
if(!glfwInit())
????throwstd::runtime_error("glfwInit?failed");
glfwSetErrorCallback(OnError)這一行告訴GLFW當(dāng)錯誤發(fā)生時調(diào)用OnError函數(shù)。OnError函數(shù)會拋一個包含錯誤信息的異常,我們能從中發(fā)現(xiàn)哪里出錯了蔚出。
然后我們用GLFW創(chuàng)建一個窗口衅胀。
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT,?GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE,?GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,?3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,?2);
glfwWindowHint(GLFW_RESIZABLE,?GL_FALSE);
gWindow?=?glfwCreateWindow((int)SCREEN_SIZE.x,?(int)SCREEN_SIZE.y,?"OpenGL?Tutorial",?NULL,?NULL);
if(!gWindow)
????throwstd::runtime_error("glfwCreateWindow?failed.?Can?your?hardware?handle?OpenGL?3.2?");
該窗口包含一個向前兼容的OpenGL 3.2內(nèi)核上下文。假如glfwCreateWindow失敗了宙帝,你應(yīng)該降低OpenGL版本仍侥。
創(chuàng)建窗口最后一步,我們應(yīng)該設(shè)置一個“當(dāng)前”O(jiān)penGL上下文給剛創(chuàng)建的窗口:
1glfwMakeContextCurrent(gWindow);
無論我們調(diào)用哪個OpenGL函數(shù)否淤,都會影響到“當(dāng)前上下文”鞍帝。我們只會用到一個上下文,所以設(shè)置完后煞茫,就別管它了帕涌。理論上來說,我們可以有多個窗口续徽,且每個窗口都可以有自己的上下文蚓曼。
現(xiàn)在我們窗口有了OpenGL上下文變量,我們需要初始化GLEW以便訪問OpenGL接口钦扭。
glewExperimental?=?GL_TRUE;?//stops?glew?crashing?on?OSX?:-/
if(glewInit()?!=?GLEW_OK)
????throwstd::runtime_error("glewInit?failed");
這里的GLEW與OpenGL內(nèi)核有點(diǎn)小問題纫版,設(shè)置glewExperimental就可以修復(fù),但希望再未來永遠(yuǎn)不要發(fā)生客情。
我們也可以用GLEW再次確認(rèn)3.2版本是否存在:
if(!GLEW_VERSION_3_2)
????throwstd::runtime_error("OpenGL?3.2?API?is?not?available.");
在LoadShaders函數(shù)中其弊,我們使用本教程提供的tdogl::Shader和tdogl::Program兩個類編譯和鏈接了vertex shader和fragment shader。
std::vector?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?=?newtdogl::Program(shaders);
在LoadTriangle函數(shù)中裹匙,我們創(chuàng)建了一個VAO和VBO瑞凑。這是第一步,創(chuàng)建和綁定新的VAO:
glGenVertexArrays(1,?&gVAO);
glBindVertexArray(gVAO);
然后我們創(chuàng)建和綁定新的VBO:
glGenBuffers(1,?&gVBO);
glBindBuffer(GL_ARRAY_BUFFER,?gVBO);
接著概页,我們上傳一些數(shù)據(jù)到VBO中籽御。這些數(shù)據(jù)就是三個頂點(diǎn),每個頂點(diǎn)包含三個GLfloat惰匙。
GLfloat?vertexData[]?=?{
????//??X?????Y?????Z
?????0.0f,?0.8f,?0.0f,
????-0.8f,-0.8f,?0.0f,
?????0.8f,-0.8f,?0.0f,
};
glBufferData(GL_ARRAY_BUFFER,?sizeof(vertexData),?vertexData,?GL_STATIC_DRAW);
現(xiàn)在緩沖區(qū)包含了三角形的三個頂點(diǎn)技掏,是時候開始設(shè)置VAO了。首先项鬼,我們應(yīng)該啟用shader程序中的vert變量哑梳。這些變量能被開啟或關(guān)閉,默認(rèn)情況下是關(guān)閉的绘盟,所以我們需要開啟它鸠真。vert變量是一個“屬性變量(attribute variable)”,這也是為何OpenGL函數(shù)名稱中有帶“Attrib”龄毡。我們可以在后續(xù)的文章中看到更多類型吠卷。
1glEnableVertexAttribArray(gProgram->attrib("vert"));
VAO設(shè)置最復(fù)雜的部分就是下個函數(shù):glVertexAttribPointer。讓我們先調(diào)用該函數(shù)沦零,等會解釋祭隔。
1glVertexAttribPointer(gProgram->attrib("vert"),?3,?GL_FLOAT,?GL_FALSE,?0,?NULL);
第一個參數(shù),gProgram->attrib("vert")路操,這就是那個需要上傳數(shù)據(jù)的shder變量疾渴。在這個例子中千贯,我們需要發(fā)數(shù)據(jù)給vertshader變量。
第二個參數(shù)搞坝,3表明每個頂點(diǎn)需要三個數(shù)字搔谴。
第三個參數(shù),GL_FLOAT說明三個數(shù)字是GLfloat類型桩撮。這非常重要己沛,因為GLdouble類型的數(shù)據(jù)大小跟它是不同的。
第四個參數(shù)距境,GL_FALSE說明我們不需要對浮點(diǎn)數(shù)進(jìn)行“歸一化”,假如我們使用了歸一化垮卓,那這個值會被限定為最小0垫桂,最大1。我們不需要對我們的頂點(diǎn)進(jìn)行限制粟按,所以這個參數(shù)為false诬滩。
第五個參數(shù),0灭将,該參數(shù)可以在頂點(diǎn)之間有間隔時使用疼鸟,設(shè)置參數(shù)為0,表示數(shù)據(jù)之間沒有間隔庙曙。
第六個參數(shù)空镜,NULL,假如我們的數(shù)據(jù)不是從緩沖區(qū)頭部開始的話捌朴,可以設(shè)置這個參數(shù)來指定吴攒。設(shè)置該參數(shù)為NULL,表示我們的數(shù)據(jù)從VBO的第一個字節(jié)開始砂蔽。
現(xiàn)在VBO和VAO都設(shè)置完成洼怔,我們需要對它們進(jìn)行解綁定,防止一不小心被哪里給更改了左驾。
glBindBuffer(GL_ARRAY_BUFFER,?0);
glBindVertexArray(0);
到此镣隶,shader,VBO和VAO都準(zhǔn)備好了诡右。我們可以開始在Render函數(shù)里繪制了安岂。
首先,我們先清空下屏幕稻爬,讓它變成純黑色:
glClearColor(0,?0,?0,?1);?//?black
glClear(GL_COLOR_BUFFER_BIT?|?GL_DEPTH_BUFFER_BIT);
然后告訴OpenGL我們要開始使用VAO和shader了:
glUseProgram(gProgram->object());
glBindVertexArray(gVAO);
最后嗜闻,我們繪制出三角形:
1glDrawArrays(GL_TRIANGLES,?0,?3);
調(diào)用glDrawArrays函數(shù)說明我們需要繪制三角形,從第0個頂點(diǎn)開始桅锄,有3個頂點(diǎn)被發(fā)送到shader琉雳。OpenGL會在當(dāng)前VAO范圍內(nèi)確定該從哪里獲取頂點(diǎn)样眠。
頂點(diǎn)將會從VBO中取出并發(fā)送到vertex shader。然后三角形內(nèi)的每個像素會發(fā)送給fragment shader翠肘。接著fragment shader將每個像素變成白色檐束。歡呼!
現(xiàn)在繪制結(jié)束了束倍,為了安全起見被丧,我們需要將shader和VAO進(jìn)行解綁定:
glBindVertexArray(0);
glUseProgram(0);
最后一件事,在我們看到三角形之前需要切換幀緩沖:
1glfwSwapBuffers(gWindow);
在幀緩沖被交換前绪妹,我們會繪制到一個不可見的離屏(off-screen)幀緩沖區(qū)甥桂。當(dāng)我們調(diào)用glfwSwapBuffers時,離屏緩沖會變成屏幕緩沖邮旷,所以我們就能在窗口上看見內(nèi)容了黄选。