@[TOC](OpenGL (二)OpenGL入門(mén)理論知識(shí))
OpenGL 學(xué)習(xí)網(wǎng)址
感覺(jué)難度適中
(有一些skybox,shadow volume 等實(shí)現(xiàn)技巧的教程)
(看起來(lái)更傾向于計(jì)算機(jī)圖形學(xué),而不僅僅是一個(gè) OpenGL 的教程)
(這個(gè)網(wǎng)站也很基礎(chǔ)坦报,內(nèi)容非常好端蛆,水平和第一個(gè)類似图云,并且還有中文泥技。至少看到紋理映射這篇的教程华坦,講得比第一個(gè)鏈接更翔實(shí)间驮〖菪祝可以和第一個(gè)互相參考尘奏,強(qiáng)推)
這個(gè)是我覺(jué)得全世界最知名的OpenGL教程滩褥,而且有網(wǎng)友將其中48個(gè)教程翻譯成了中文http://www.owlei.com/DancingWind/Nehe 此教程最大的特點(diǎn)是提供了針對(duì)不同平臺(tái)、不同編譯器炫加、不同語(yǔ)言的各種版本瑰煎。你不用考慮自己用的是Linux/Windows铺然、VC/BC、C++/Java/C#/VB酒甸,甚至D語(yǔ)言魄健,你都能找到對(duì)應(yīng)的版本。除了這些教程插勤,在Nehe Productions你還能下載到各種很cool的Demo沽瘦,相當(dāng)多的一部分都提供源代碼。http://nehe.gamedev.net/data/downloads/download.asp?letter=0-9
有100個(gè)免費(fèi)的OpenGL教程农尖,內(nèi)容涉及很多八叉樹(shù)析恋、BSP、Cg盛卡、GLSL助隧、各種紋理映射技術(shù)等,還有OpenAL窟扑、Ray Tracing的教程喇颁。該網(wǎng)站還推出了一本教你制作游戲引擎的書(shū)《Ultimate Game Programming with DirectX》,暫時(shí)還沒(méi)有中文翻譯版嚎货。
有關(guān)于OpenGL橘霎、DirectX的很多教程、而且還定時(shí)更新一些東西殖属,不過(guò)最近該鏈接我打開(kāi)不了姐叁,不知道暫時(shí)出了什么問(wèn)題。
有針對(duì)View Frustum洗显、GLSL外潜、Math、Billboarding挠唆、Picking处窥、Terrain、Display Lists玄组、GLUT各專題的教程滔驾。其中關(guān)于GLUT、Terrain部分講解都非常詳細(xì)俄讹。一般的教程網(wǎng)站都是通過(guò)一段代碼展示OpenGL的某種渲染效果哆致,如果沒(méi)有圖形學(xué)背景很難理解其原理,該網(wǎng)站對(duì)所列每項(xiàng)技術(shù)的來(lái)龍去脈患膛,相關(guān)算法都有比較詳細(xì)的說(shuō)明摊阀,會(huì)讓你理解更加深刻。
我接觸最早的一個(gè)OpenGL教程網(wǎng)站,有很多有特色的教程胞此,比如一個(gè)小的2D RPG教程臣咖,可惜現(xiàn)在大部分代碼都不能免費(fèi)下載了。
有關(guān)于OpenGL豌鹤、GLSL亡哄、Direct3D的教程,還有很多其它有用的資料布疙、工具下載蚊惯,個(gè)人覺(jué)得很棒的一個(gè)網(wǎng)站。
OpenGL灵临、GLSL截型、DirectX、Physics儒溉、Math等相關(guān)教程宦焦,看著很不錯(cuò)。
教程的內(nèi)容包含OpenGL基本變換顿涣、紋理映射等波闹,也有一些有趣的Demo,可惜沒(méi)有提供源碼下載涛碑。
挺有特色的一個(gè)網(wǎng)站精堕。
我知道的第一個(gè)OpenGL視頻教程,可惜是全英文的蒲障,看視頻教程還有一個(gè)好處就是不經(jīng)意間能學(xué)到作者編程時(shí)的一些細(xì)小技巧歹篓。
我學(xué)習(xí)opengl得到的啟示最多的一篇文章,我強(qiáng)烈地建議大家去讀一下這位大神的相關(guān)系列的文章.這里面的代碼包含全面揉阎,真正想學(xué)習(xí)opengl的可以去看看
OpenGL 簡(jiǎn)介
- 什么是OpenGL
1庄撮、OpenGL是一種應(yīng)用程序編程接口,它是一種可以對(duì)圖形硬件設(shè)備特性進(jìn)行訪問(wèn)的軟件庫(kù)毙籽。
2洞斯、OpenGL被設(shè)計(jì)為一個(gè)現(xiàn)代化的、硬件無(wú)關(guān)的接口坑赡,因此我們可以在不考慮計(jì)算機(jī)操作系統(tǒng)或窗口系統(tǒng)的前提下巡扇,在多種不同的圖形硬件系統(tǒng)上,或者完全通過(guò)軟件的方式實(shí)現(xiàn)OpenGL接口垮衷。
3、OpenGL自身并不包含任何執(zhí)行窗口任務(wù)乖坠,或者處理用戶輸入的函數(shù)搀突。
4、OpenGL也沒(méi)有提供任何用于表達(dá)三維物體模型熊泵,或者讀取圖像文件的操作仰迁。我們需要通過(guò)一系列的幾何圖元(點(diǎn)甸昏,線段,三角形徐许,以及patch)來(lái)創(chuàng)建三維空間物體施蜜!
5、OpenGL API是過(guò)程性的雌隅,不是描述性的翻默,即OpenGL不是面向?qū)ο蟮模設(shè)penGL無(wú)法利用面向?qū)ο蟮奶匦郧∑稹J褂玫臅r(shí)候只需要:程序與OpenGL的實(shí)現(xiàn)鏈接就可以了修械!
6、OpenGL的實(shí)現(xiàn)可以是軟件實(shí)現(xiàn)检盼,也可以是硬件實(shí)現(xiàn)肯污。
軟件實(shí)現(xiàn):是對(duì)OpengGL函數(shù)調(diào)用時(shí)作出的響應(yīng)并創(chuàng)建二維或三維圖像的函數(shù)庫(kù)。
硬件實(shí)現(xiàn):則是通過(guò)設(shè)置能夠繪制圖形或圖像的圖形卡驅(qū)動(dòng)程序
硬件實(shí)現(xiàn)要比軟件實(shí)現(xiàn)快得多6滞鳌蹦渣!
- OpenGL可以用來(lái)干什么?
1貌亭、OpenGL已經(jīng)誕生很長(zhǎng)時(shí)間了柬唯,1992年7月,SGI公司發(fā)布了OpenGL的1.0版本属提。
2权逗、應(yīng)用領(lǐng)域:視頻、圖形冤议、圖片處理斟薇,2D/3D游戲引擎開(kāi)發(fā),科學(xué)可視化恕酸,醫(yī)學(xué)軟件的開(kāi)發(fā) 堪滨,CAD(計(jì)算機(jī)輔助技術(shù)),虛擬實(shí)境(AR VR)蕊温,AI人工智能
- OpenGL ES和OpenGL有什么關(guān)系?
1袱箱、OpenGL ES是OpenGL的子集,針對(duì)手機(jī)义矛、PDA和游戲主機(jī)嵌入式設(shè)備而設(shè)計(jì)
2发笔、OpenGL ES 是從 OpenGL 裁剪定制而來(lái)的,去除了 glBegin/glEnd凉翻,四邊形(GL_QUADS)了讨、多邊形(GL_POLYGONS)等復(fù)雜圖元等許多非絕對(duì)必要的特性,剩下最核心有用的部分。
可以理解成:OpenGL ES是一個(gè)在移動(dòng)平臺(tái)上能夠支持 OpenGL 最基本功能的精簡(jiǎn)規(guī)范前计。
- OpenGL程序需要執(zhí)行的主要操作步驟
1胞谭、從OpenGL的幾何圖元中設(shè)置數(shù)據(jù),用于構(gòu)建形狀
2男杈、使用不同的著色器對(duì)輸入的圖元數(shù)據(jù)執(zhí)行計(jì)算操作丈屹,判斷位置,顏色以及其他渲染屬性伶棒。
3旺垒、將輸入圖元的數(shù)學(xué)描述 轉(zhuǎn)換為與屏幕位置對(duì)應(yīng)的像素片元,也稱(光柵化)苞冯。
4袖牙、針對(duì)光柵化過(guò)程產(chǎn)生的每個(gè)片元,執(zhí)行 片元著色器舅锄,從而決定這個(gè)片元的最終顏色和位置
5鞭达、如果有必要 可以對(duì)片元執(zhí)行一些額外操作。
例如:判斷片元對(duì)應(yīng)的對(duì)象是否可見(jiàn)皇忿,或者將片元的顏色與當(dāng)前屏幕位置的顏色進(jìn)行融合畴蹭。
- 開(kāi)發(fā)語(yǔ)言與編程約定
我們以后會(huì)見(jiàn)到OpenGL的函數(shù)多是以gl開(kāi)頭,因?yàn)镺penGL的函數(shù)遵循一定的命名約定鳍烁,它可以告訴我們這個(gè)函數(shù)來(lái)自哪個(gè)函數(shù)庫(kù)叨襟,并且還可以告訴我們這個(gè)函數(shù)的參數(shù)個(gè)數(shù)和類型。
OpenGL的函數(shù)是采用以下的書(shū)寫(xiě)格式:
<函數(shù)庫(kù)前綴> <根命令> <可選的參數(shù)數(shù)量> <可選的參數(shù)類型>
- OpenGL版本比較
早前學(xué)OpenGL的時(shí)候還是1.x版本幔荒,用的都是glVertex糊闽,glNormal等固定管線API。后來(lái)工作需要接觸DirectX9爹梁,shader也只是可選項(xiàng)而已右犹,跟固定管線一起混用著。OpenGL ES 2.0版本開(kāi)始就不再支持固定管線姚垃,只支持可編程管線念链。
OpenGL 基礎(chǔ)理論知識(shí)
1. 坐標(biāo)系與變換
1、在開(kāi)發(fā)OpenGL程序時(shí)积糯,需要用到兩個(gè)坐標(biāo)系掂墓。
一個(gè)稱為對(duì)象坐標(biāo)系 :(物體坐標(biāo)系)第一個(gè)坐標(biāo)系是我們?cè)陂_(kāi)發(fā)中使用的坐標(biāo)系。
另一個(gè)稱為世界坐標(biāo)系:(世界坐標(biāo)系)第二個(gè)坐標(biāo)系又稱為窗口坐標(biāo)系或屏幕坐標(biāo)系看成,在這個(gè)坐標(biāo)系中的單位是像素君编。
2、 在繪制的過(guò)程中川慌,OpenGL會(huì)自動(dòng)實(shí)現(xiàn)從對(duì)象到窗口坐標(biāo)系的轉(zhuǎn)換啦粹,所需要的信息是屏幕中顯示窗口的尺寸和用戶希望顯示對(duì)象空間的大小偿荷。
3、OpenGL中所需要的坐標(biāo)系變換由兩個(gè)矩陣決定唠椭,
即:模型視圖矩陣和投影矩陣,這些矩陣是OpenGL的狀態(tài)的一部分忍饰。
設(shè)置這兩種矩陣的典型步驟包括以下三個(gè)步驟:
(1) 指定我們希望修改的矩陣贪嫂。
(2) 將矩陣設(shè)為單位矩陣。
(3) 修改當(dāng)前矩陣為用戶期望的矩陣艾蓝。
2. OpenGL 顯示圖形流程
OpenGL是使用客戶端——服務(wù)端的形式實(shí)現(xiàn)的力崇。
(客戶端):我們編寫(xiě)的程序。
(服務(wù)端):計(jì)算機(jī)圖形硬件廠商所提供的OpenGL實(shí)現(xiàn)赢织。
在沒(méi)有OpenGL的時(shí)候亮靴,CPU與GPU之間傳遞數(shù)據(jù)是通過(guò)控制器內(nèi)存之間傳遞,傳遞速度非常的慢于置,內(nèi)存在復(fù)制數(shù)據(jù)時(shí)茧吊,CPU和GPU都不能操作這個(gè)數(shù)據(jù)。OpenGL很好的解決這個(gè)問(wèn)題八毯,OpenGL創(chuàng)建了緩存區(qū)域搓侄,能夠連續(xù)的管理RAM,保證數(shù)據(jù)傳輸不影響GPU處理數(shù)據(jù)效率话速。
3. OpenGL 基本概念
- GLEW, GLFW和GLM介紹
The OpenGL Extension Wrangler (GLEW)是用來(lái)訪問(wèn)OpenGL 3.2 API函數(shù)的讶踪。不幸的是你不能簡(jiǎn)單的使用#include <GL/gl.h>來(lái)訪問(wèn)OpenGL接口,除非你想用舊版本的OpenGL泊交。在現(xiàn)代OpenGL中乳讥,API函數(shù)是在運(yùn)行時(shí)(run time)確定的,而非編譯期(compile time)廓俭。GLEW可以在運(yùn)行時(shí)加載OpenGL API云石。
GLFW允許我們跨平臺(tái)創(chuàng)建窗口,接受鼠標(biāo)鍵盤(pán)消息白指。OpenGL不處理這些窗口創(chuàng)建和輸入留晚,所以就需要我們自己動(dòng)手。我選擇GLFW是因?yàn)樗苄「娉埃⑶胰菀桌斫狻?/p>
OpenGL Mathematics (GLM)是一個(gè)數(shù)學(xué)庫(kù)错维,用來(lái)處理矢量和矩陣等幾乎其它所有東西。舊版本OpenGL提供了類似glRotate, glTranslate和glScale等函數(shù)橄唬,在現(xiàn)代OpenGL中赋焕,這些函數(shù)已經(jīng)不存在了,我們需要自己處理所有的數(shù)學(xué)運(yùn)算仰楚。GLM能在后續(xù)教程里提供很多矢量和矩陣運(yùn)算上幫助隆判。
- Shaders
Shaders在現(xiàn)代OpenGL中是個(gè)很重要的概念犬庇。應(yīng)用程序離不開(kāi)它,除非你理解了侨嘀,否則這些代碼也沒(méi)有任何意義臭挽。
Shaders是一段GLSL小程序,運(yùn)行在GPU上而非CPU咬腕。它們使用OpenGL Shading Language (GLSL)語(yǔ)言編寫(xiě)欢峰,看上去像C或C++,但卻是另外一種不同的語(yǔ)言涨共。使用shader就像你寫(xiě)個(gè)普通程序一樣:寫(xiě)代碼纽帖,編譯,最后鏈接在一起才生成最終的程序举反。
Shaders并不是個(gè)很好的名字懊直,因?yàn)樗粌H僅只做著色。只要記得它們是個(gè)用不同的語(yǔ)言寫(xiě)的火鼻,運(yùn)行在顯卡上的小程序就行室囊。
在舊版本的OpenGL中,shaders是可選的凝危。在現(xiàn)代OpenGL中波俄,為了能在屏幕上顯示出物體,shaders是必須的蛾默。
為可能近距離了解shaders和圖形渲染管線懦铺,我推薦Durian Software的相關(guān)文章The Graphics Pipeline chapter。
- Vertex Shaders
Vertex shader主要用來(lái)將點(diǎn)(x支鸡,y冬念,z坐標(biāo))變換成不同的點(diǎn)。頂點(diǎn)只是幾何形狀中的一個(gè)點(diǎn)牧挣,一個(gè)點(diǎn)叫vectex急前,多個(gè)點(diǎn)叫vertices(發(fā)音為ver-tuh-seez)。在本教程中瀑构,我們的三角形需要三個(gè)頂點(diǎn)(vertices)組成裆针。
Vertex Shader的GLSL代碼如下:
#version 150
in vec3 vert;
void main() {
// does not alter the vertices at all
gl_Position = vec4(vert, 1);
}
第一行#version 150告訴OpenGL這個(gè)shader使用GLSL版本1.50.
第二行in vec3 vert;告訴shader需要那一個(gè)頂點(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定義的全局變量沐祷,用來(lái)存儲(chǔ)vertex shader的輸出嚷闭。所有vertex shaders都需要對(duì)gl_Position進(jìn)行賦值。
gl_Position是4D坐標(biāo)(vec4)赖临,但vert是3D坐標(biāo)(vec3)胞锰,所以我們需要將vert轉(zhuǎn)換為4D坐標(biāo)vec4(vert, 1)。第二個(gè)的參數(shù)1是賦值給第四維坐標(biāo)兢榨。我們會(huì)在后續(xù)教程中學(xué)到更多關(guān)于4D坐標(biāo)的東西胜蛉。但現(xiàn)在,我們只要知道第四維坐標(biāo)是1即可色乾,i可以忽略它就把它當(dāng)做3D坐標(biāo)來(lái)對(duì)待。
Vertex Shader在本文中沒(méi)有做任何事领突,后續(xù)我們會(huì)修改它來(lái)處理動(dòng)畫(huà)暖璧,攝像機(jī)和其它東西
- Fragment Shaders
Fragment shader的主要功能是計(jì)算每個(gè)需要繪制的像素點(diǎn)的顏色。
一個(gè)”fragment”基本上就是一個(gè)像素君旦,所以你可以認(rèn)為片段著色器(fragment shader)就是像素著色器(pixel shader)澎办。在本文中每個(gè)片段都是一像素,但這并不總是這樣的金砍。你可以更改某個(gè)OpenGL設(shè)置局蚀,以便得到比像素更小的片段,之后的文章我們會(huì)講到這個(gè)恕稠。
本文所使用的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這個(gè)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)建一個(gè)RGBA顏色千扶,并且紅綠藍(lán)和alpha都設(shè)為最大值,即白色骆捧。
現(xiàn)在澎羞,就能用shader在OpenGL中繪制出了純白色。在之后的文章中敛苇,我們還會(huì)加入不同顏色和貼圖妆绞。貼圖就是你3D模型上的圖像。
- 編譯和鏈接Shaders
在C++中枫攀,你需要對(duì)你的.cpp文件進(jìn)行編譯括饶,然后鏈接到一起組成最終的程序。OpenGL的shaders也是這么回事脓豪。
在這篇文章中用到了兩個(gè)可復(fù)用的類巷帝,是用來(lái)處理shaders的編譯和鏈接:tdogl::Shader和tdogl::Program。這兩個(gè)類代碼不多扫夜,并且有詳細(xì)的注釋楞泼,我建議你閱讀源碼并且去鏈接OpenGL是如何工作的驰徊。
- 什么是VBO和VAO?
當(dāng)shaders運(yùn)行在GPU堕阔,其它代碼運(yùn)行在CPU時(shí)棍厂,你需要有種方式將數(shù)據(jù)從CPU傳給GPU。在本文中超陆,我們傳送了一個(gè)三角的三個(gè)頂點(diǎn)數(shù)據(jù)牺弹,但在更大的工程中3D模型會(huì)有成千上萬(wàn)個(gè)頂點(diǎn),顏色时呀,貼圖坐標(biāo)和其它東西张漂。
這就是我們?yōu)槭裁葱枰猇ertex Buffer Objects (VBOs)和Vertex Array Objects (VAOs)。VBO和VAO用來(lái)將C++程序的數(shù)據(jù)傳給shaders來(lái)渲染谨娜。
在舊版本的OpenGL中航攒,是通過(guò)glVertex,glTexCoord和glNormal函數(shù)把每幀數(shù)據(jù)發(fā)送給GPU的趴梢。在現(xiàn)代OpenGL中漠畜,所有數(shù)據(jù)必須通過(guò)VBO在渲染之前發(fā)送給顯卡。當(dāng)你需要渲染某些數(shù)據(jù)時(shí)坞靶,通過(guò)設(shè)置VAO來(lái)描述該獲取哪些VBO數(shù)據(jù)推送給shader變量憔狞。
- Vertex Buffer Objects (VBOs)
第一步我們需要從內(nèi)存里上傳三角形的三個(gè)頂點(diǎn)到顯存中。這就是VBO該干的事彰阴。VBO其實(shí)就是顯存的“緩沖區(qū)(buffers)” - 一串包含各種二進(jìn)制數(shù)據(jù)的字節(jié)區(qū)域瘾敢。你能上傳3D坐標(biāo),顏色硝枉,甚至是你喜歡的音樂(lè)和詩(shī)歌廉丽。VBO不關(guān)心這些數(shù)據(jù)是啥,因?yàn)樗皇菍?duì)內(nèi)存進(jìn)行復(fù)制妻味。
- Vertex Array Objects (VAOs)
第二步我們要用VBO的數(shù)據(jù)在shaders中渲染三角形正压。請(qǐng)記住VBO只是一塊數(shù)據(jù),它不清楚這些數(shù)據(jù)的類型责球。而告訴OpenGL這緩沖區(qū)里是啥類型數(shù)據(jù)焦履,這事就歸VAO管。
VAO對(duì)VBO和shader變量進(jìn)行了連接雏逾。它描述了VBO所包含的數(shù)據(jù)類型嘉裤,還有該傳遞數(shù)據(jù)給哪個(gè)shader變量。在OpenGL所有不準(zhǔn)確的技術(shù)名詞中栖博,“Vertex Array Object”是最爛的一個(gè)屑宠,因?yàn)樗緵](méi)有解釋VAO該干的事。
你回頭看下本文的vertex shader(在文章的前面)仇让,你就能發(fā)現(xiàn)我們只有一個(gè)輸入變量vert典奉。在本文中躺翻,我們用VAO來(lái)說(shuō)明“hi,OpenGL卫玖,這里的VBO有3D頂點(diǎn)公你,我想要你在vertex shader時(shí),發(fā)三個(gè)頂點(diǎn)數(shù)據(jù)給vert變量假瞬∩驴浚”
在后續(xù)的文章中,我們會(huì)用VAO來(lái)說(shuō)“hi脱茉,OpenGL剪芥,這里的VBO有3D頂點(diǎn),顏色琴许,貼圖坐標(biāo)粗俱,我想要你在shader時(shí),發(fā)頂點(diǎn)數(shù)據(jù)給vert變量虚吟,發(fā)顏色數(shù)據(jù)給vertColor變量,發(fā)貼圖坐標(biāo)給vertTexCoord變量签财〈浚”
給使用上個(gè)OpenGL版本的用戶的提醒
假如你在舊版本的OpenGL中使用了VBO但沒(méi)有用到VAO,你可能會(huì)不認(rèn)同VAO的描述唱蒸。你會(huì)爭(zhēng)論說(shuō)“頂點(diǎn)屬性”可以用glVertexAttribPointer將VBO和shaders連接起來(lái)邦鲫,而不是用VAO。這取決于你是否認(rèn)為頂點(diǎn)屬性應(yīng)該是VAO“內(nèi)置(inside)”的(我是這么認(rèn)為的)神汹,或者說(shuō)它們是否是VAO外置的一個(gè)全局狀態(tài)庆捺。3.2內(nèi)核和我用的AIT驅(qū)動(dòng)中,VAO不是可選項(xiàng) - 沒(méi)有VAO的封裝glEnableVertexAttribArray, glVertexAttribPointer和glDrawArrays都會(huì)導(dǎo)致GL_INVALID_OPERATION錯(cuò)誤屁魏。這就是為啥我認(rèn)為頂點(diǎn)屬性應(yīng)該內(nèi)置于VAO滔以,而非全局狀態(tài)的原因。
3.2內(nèi)核手冊(cè)也說(shuō)VAO是必須的氓拼,但我只聽(tīng)說(shuō)ATI驅(qū)動(dòng)會(huì)拋錯(cuò)誤你画。下面描述引用自OpenGL 3.2內(nèi)核手冊(cè)
所有與頂點(diǎn)處理有關(guān)的數(shù)據(jù)定義都應(yīng)該封裝在VAO里。
一般VAO邊界包含所有更改vertex array狀態(tài)的命令桃漾,比如VertexAttribPointer和EnableVertexAttribArray坏匪;所有使用vertex array進(jìn)行繪制的命令,比如DrawArrays和DrawElements撬统;所有對(duì)vertex array狀態(tài)進(jìn)行查詢的命令(見(jiàn)第6章)适滓。
不管怎樣,我也知道為啥會(huì)有人認(rèn)為頂點(diǎn)屬性應(yīng)該放在VAO外部恋追。glVertexAttribPointer出現(xiàn)早于VAO凭迹,在這段時(shí)間里頂點(diǎn)屬性一直被認(rèn)為是全局狀態(tài)罚屋。你應(yīng)該能看得出VAO是一種改變?nèi)譅顟B(tài)的有效方法。我更傾向于認(rèn)為是這樣:假如你沒(méi)有創(chuàng)建VAO蕊苗,那OpenGL通過(guò)了一個(gè)默認(rèn)的全局VAO沿后。所以當(dāng)你使用glVertexAttribPointer時(shí),你仍然是在VAO內(nèi)修改頂點(diǎn)屬性朽砰,只不過(guò)現(xiàn)在從默認(rèn)的VAO變成你自己創(chuàng)建的VAO尖滚。
這里有更多的討論:http://www.opengl.org/discussion_boards/showthread.php/174577-Questions-on-VAOs
OpenGL 入門(mén)實(shí)踐
- 先來(lái)介紹一個(gè)大神的開(kāi)源opengl項(xiàng)目代碼:
這是現(xiàn)代OpenGL教程系列的第一篇。所有代碼都是開(kāi)源的瞧柔。通過(guò)這篇教程漆弄,你將會(huì)學(xué)到如何在Windows下用Visual Studio 2013或Mac下用Xcode搭建OpenGL 3.2工程。該應(yīng)用包含一個(gè)頂點(diǎn)著色器(vertex shader)造锅,一個(gè)片段著色器(fragment shader)和使用VAO和VBO來(lái)繪制的三角形撼唾。該工程使用GLEW來(lái)訪問(wèn)OpenGL API,用GLFW來(lái)處理窗口創(chuàng)建和輸入哥蔚,還有使用GLM進(jìn)行矩陣/矢量相關(guān)的數(shù)學(xué)運(yùn)算倒谷。所有例子代碼的zip打包可以從這里獲取:https://github.com/tomdalling/opengl-series/archive/master.zip糙箍。
這一系列文章中所使用的代碼都存放在:https://github.com/tomdalling/opengl-series渤愁。
你可以在頁(yè)面中下載zip,加入你會(huì)git的話深夯,也可以復(fù)制該倉(cāng)庫(kù)抖格。
- 關(guān)于兼容性
本文使用OpenGL 3.2,但我會(huì)嘗試保持如下兼容:
向后兼容OpenGL 2.1
向前兼容OpenGL 3.X和4.X
兼容Android和iOS的OpenGL ES 2.0
因?yàn)镺penGL和GLSL存在許多不同版本咕晋,本文代碼不一定能做到100%上述兼容雹拄。我希望能兼容99%,并且不同版本之間只要輕微修改即可掌呜。
想要了解OpenGL和GLSL不同版本間的區(qū)別 滓玖,可以參考這個(gè)網(wǎng)站:OpenGL, OpenGL ES, WebGL, GLSL, GLSL ES APIs Table
- 環(huán)境安裝配置
- Visual Studio下安裝
代碼在Windows 7 32位系統(tǒng),Visual Studio Express 2013(免費(fèi))下創(chuàng)建和測(cè)試质蕉。你應(yīng)該可以打開(kāi)解決方案并成功編譯所有工程呢撞。
- Xcode下安裝
Xcode工程實(shí)在OSX 10.10系統(tǒng),Xcode 6.1下創(chuàng)建并測(cè)試的饰剥。打開(kāi)Xcode工程應(yīng)該可以成功編譯所有目標(biāo)殊霞。
- Linux下安裝
Linux是基于SpartanJ。我在Ubuntu 12.04下簡(jiǎn)單測(cè)試通過(guò)汰蓉。
安裝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
- 開(kāi)始編碼
打開(kāi)main.cpp,我們從main()函數(shù)開(kāi)始。
首先祝钢,我們初始化GLFW:
glfwSetErrorCallback(OnError);
if(!glfwInit())
throw std::runtime_error("glfwInit failed");
glfwSetErrorCallback(OnError)這一行告訴GLFW當(dāng)錯(cuò)誤發(fā)生時(shí)調(diào)用OnError函數(shù)比规。OnError函數(shù)會(huì)拋一個(gè)包含錯(cuò)誤信息的異常,我們能從中發(fā)現(xiàn)哪里出錯(cuò)了拦英。
然后我們用GLFW創(chuàng)建一個(gè)窗口蜒什。
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)
throw std::runtime_error("glfwCreateWindow failed. Can your hardware handle OpenGL 3.2?");
該窗口包含一個(gè)向前兼容的OpenGL 3.2內(nèi)核上下文。假如glfwCreateWindow失敗了疤估,你應(yīng)該降低OpenGL版本灾常。
創(chuàng)建窗口最后一步,我們應(yīng)該設(shè)置一個(gè)“當(dāng)前”O(jiān)penGL上下文給剛創(chuàng)建的窗口:
glfwMakeContextCurrent(gWindow);
無(wú)論我們調(diào)用哪個(gè)OpenGL函數(shù)铃拇,都會(huì)影響到“當(dāng)前上下文”钞瀑。我們只會(huì)用到一個(gè)上下文,所以設(shè)置完后慷荔,就別管它了雕什。理論上來(lái)說(shuō),我們可以有多個(gè)窗口显晶,且每個(gè)窗口都可以有自己的上下文贷岸。
現(xiàn)在我們窗口有了OpenGL上下文變量,我們需要初始化GLEW以便訪問(wèn)OpenGL接口磷雇。
glewExperimental = GL_TRUE; //stops glew crashing on OSX :-/
if(glewInit() != GLEW_OK)
throw std::runtime_error("glewInit failed");
這里的GLEW與OpenGL內(nèi)核有點(diǎn)小問(wèn)題凰盔,設(shè)置glewExperimental就可以修復(fù),但希望再未來(lái)永遠(yuǎn)不要發(fā)生倦春。
我們也可以用GLEW再次確認(rèn)3.2版本是否存在:
if(!GLEW_VERSION_3_2)
throw std::runtime_error("OpenGL 3.2 API is not available.");
在LoadShaders函數(shù)中,我們使用本教程提供的tdogl::Shader和tdogl::Program兩個(gè)類編譯和鏈接了vertex shader和fragment shader落剪。
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);
在LoadTriangle函數(shù)中睁本,我們創(chuàng)建了一個(gè)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ù)就是三個(gè)頂點(diǎn)凡泣,每個(gè)頂點(diǎn)包含三個(gè)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ū)包含了三角形的三個(gè)頂點(diǎn),是時(shí)候開(kāi)始設(shè)置VAO了鞋拟。首先骂维,我們應(yīng)該啟用shader程序中的vert變量。這些變量能被開(kāi)啟或關(guān)閉贺纲,默認(rèn)情況下是關(guān)閉的航闺,所以我們需要開(kāi)啟它。vert變量是一個(gè)“屬性變量(attribute variable)”,這也是為何OpenGL函數(shù)名稱中有帶“Attrib”潦刃。我們可以在后續(xù)的文章中看到更多類型侮措。
glEnableVertexAttribArray(gProgram->attrib("vert"));
VAO設(shè)置最復(fù)雜的部分就是下個(gè)函數(shù):glVertexAttribPointer。讓我們先調(diào)用該函數(shù)乖杠,等會(huì)解釋分扎。
glVertexAttribPointer(gProgram->attrib("vert"), 3, GL_FLOAT, GL_FALSE, 0, NULL);
第一個(gè)參數(shù),gProgram->attrib("vert")胧洒,這就是那個(gè)需要上傳數(shù)據(jù)的shder變量畏吓。在這個(gè)例子中,我們需要發(fā)數(shù)據(jù)給vertshader變量略荡。
第二個(gè)參數(shù)庵佣,3表明每個(gè)頂點(diǎn)需要三個(gè)數(shù)字。
第三個(gè)參數(shù)汛兜,GL_FLOAT說(shuō)明三個(gè)數(shù)字是GLfloat類型巴粪。這非常重要,因?yàn)镚Ldouble類型的數(shù)據(jù)大小跟它是不同的粥谬。
第四個(gè)參數(shù)肛根,GL_FALSE說(shuō)明我們不需要對(duì)浮點(diǎn)數(shù)進(jìn)行“歸一化”,假如我們使用了歸一化漏策,那這個(gè)值會(huì)被限定為最小0派哲,最大1。我們不需要對(duì)我們的頂點(diǎn)進(jìn)行限制掺喻,所以這個(gè)參數(shù)為false芭届。
第五個(gè)參數(shù),0感耙,該參數(shù)可以在頂點(diǎn)之間有間隔時(shí)使用褂乍,設(shè)置參數(shù)為0,表示數(shù)據(jù)之間沒(méi)有間隔即硼。
第六個(gè)參數(shù)逃片,NULL,假如我們的數(shù)據(jù)不是從緩沖區(qū)頭部開(kāi)始的話只酥,可以設(shè)置這個(gè)參數(shù)來(lái)指定褥实。設(shè)置該參數(shù)為NULL,表示我們的數(shù)據(jù)從VBO的第一個(gè)字節(jié)開(kāi)始裂允。
現(xiàn)在VBO和VAO都設(shè)置完成损离,我們需要對(duì)它們進(jìn)行解綁定,防止一不小心被哪里給更改了绝编。
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
然后告訴OpenGL我們要開(kāi)始使用VAO和shader了:
glUseProgram(gProgram->object());
glBindVertexArray(gVAO);
最后草冈,我們繪制出三角形:
glDrawArrays(GL_TRIANGLES, 0, 3);
調(diào)用glDrawArrays函數(shù)說(shuō)明我們需要繪制三角形,從第0個(gè)頂點(diǎn)開(kāi)始,有3個(gè)頂點(diǎn)被發(fā)送到shader怎棱。OpenGL會(huì)在當(dāng)前VAO范圍內(nèi)確定該從哪里獲取頂點(diǎn)哩俭。
頂點(diǎn)將會(huì)從VBO中取出并發(fā)送到vertex shader。然后三角形內(nèi)的每個(gè)像素會(huì)發(fā)送給fragment shader拳恋。接著fragment shader將每個(gè)像素變成白色凡资。歡呼!
現(xiàn)在繪制結(jié)束了谬运,為了安全起見(jiàn)隙赁,我們需要將shader和VAO進(jìn)行解綁定:
glBindVertexArray(0);
glUseProgram(0);
最后一件事,在我們看到三角形之前需要切換幀緩沖:
glfwSwapBuffers(gWindow);
參考教程:https://www.e-learn.cn/content/qita/641129
http://huangwei.pro/2015-05/modern-opengl1/