OpenGL快問快答
本文內容主要來自對(http://www.opengl.org/wiki/FAQ)的翻譯,隨機加入了本人的觀點。與原文相比,章節未必完整,含義未必雷同,順序未必一致。僅供參考。
名詞術語
渲染:等於"畫",等於"draw"。
OpenGL是什么?
OpenGL是Open Graphics Library(開源圖形庫)的縮寫。它是一本說明書,是一個PDF文件。它寫的是渲染三維圖形所使用的API(Application Programming Interface)。OpenGL實現是實現了說明書里定義的那些API的函數庫。
簡單理解:OpenGL是一個關於C語言函數聲明的*.h文件。
普通顯卡里都有一個OpenGL實現。犯二顯卡里可能沒有。所以你才能通過include一個"gl.h"之類的頭文件就能編寫運行OpenGL程序。OpenGL說明書是平台無關的,所以你寫的OpenGL程序能夠在多種顯卡上運行,並且可能支持以后出現的新型顯卡。
OpenGL不是什么?
OpenGL的API只處理渲染什么圖形的問題。OpenGL實現處理如何渲染圖形的問題。作為OpenGL使用者,我們不學如何渲染圖形的問題,只學渲染什么圖形的問題。OpenGL里沒有處理動畫、計時、文件讀寫、圖片格式、GUI這些功能的函數。OpenGL只關心渲染。
GLUT不是OpenGL。GLUT也不是OpenGL的一部分。它是一個庫,可以用來創建OpenGL窗口。
OpenGL說明書是誰寫的?
OpenGL說明書是OpenGL Architectural Review Board(簡稱ARB)編寫和維護的。
OpenGL是開源的嗎?
不。因為OpenGL根本就沒有代碼,它只是一本說明書。說明書里講的是程序員可以使用哪些函數,這些函數應該做什么。准確來說,OpenGL是一套理論,一些函數聲明,一本開源的說明書,人人都可以免費下載。ISO的標准和說明書就不是免費下載的。
有一個開源的OpenGL實現(估計這是提問者想要的東西),名字是Mesa3D。它自稱實現了 OpenGL 3.0 和 GLSL 1.30。反正我沒研究過。
在哪兒下載OpenGL?
如"開源"之問,OpenGL不是一個軟件產品,它是一本說明書。在蘋果電腦上,自動集成了OpenGL的實現。
在Windows系統上,nVidia和AMD/ATI等公司根據OpenGL說明書實現了各自的OpenGL功能。所以說OpenGL功能包含在他們提供的顯卡驅動里。
如果你的OpenGL版本不夠新,更新顯卡驅動就可以了。
程序員編程時需要獲取OpenGL函數指針(手動加載 或 自動加載),這可以參考openGL入門。
OpenGL有SDK嗎?
沒有。
不過文檔、教程之類的還是有的,比如這里。
NVIDIA 和ATI關於OpenGL有各自的SDK,且富含示例代碼。
哪些平台支持OpenGL?
Windows:95及其以上。
蘋果:所有版本。
Linux:由開源驅動、Mesa庫或專有Nvidia驅動提供。
嵌入式系統中通常支持OpenGL ES。但是OpenGL ES是一套不同於傳統的OpenGL的API。
OpenGL上下文是什么?
(詳情參見 OpenGL context)
OpenGL能畫很大很復雜很絢麗的三維世界,它肯定要用一些變量記錄一些狀態、編號,把這些狀態、編號集合起來,作為一個整體,就是OpenGL上下文。OpenGL看到這些狀態、編號,才知道自己應該畫什么,所以叫做上下文。
你必須先創建一個OpenGL上下文,之后才能調用OpenGL函數。像下面這樣直接調用是不行的:
1 int main(int argc, char **argv) 2 { 3 char *GL_version=(char *)glGetString(GL_VERSION); 4 char *GL_vendor=(char *)glGetString(GL_VENDOR); 5 char *GL_renderer=(char *)glGetString(GL_RENDERER); 6 return 0; 7 }
這段代碼中,程序員只是想獲取OpenGL版本信息,但是失敗了。因為這段代碼還沒有與OpenGL驅動器開始對話。
假如你想問一個人的名字,至少得先找個人在你面前。上面這段代碼就等於直接對空氣問"what's your name?"。
如果是我,我會想讓上帝給我找一個大學生,性別女,愛好男,學習成績好,性格溫和,相貌端庄,無不良嗜好……然后問"你喜歡吃青椒嗎?"。
類似地,我想用OpenGL時,我會想讓OpenGL驅動器給我分配一塊顯卡上的內存,指定像素格式、色彩緩存格式、深度緩存、模版緩存、累積緩存、是否啟用光照、霧等,然后才能使用上面那段代碼。
如何實現離屏渲染?
有人想做離屏渲染:渲染的時候不顯示窗口。唯一的方法是:像平常一樣創建窗口,然后把窗口隱藏起來。你指定像素格式,創建OpenGL上下文,把它置為當前上下文,然后調用OpenGL函數進行渲染。建議你創建一個FBO並渲染到FBO上,否則可能會失敗。這方面的詳情請參考:
http://www.opengl.org/wiki/Common_Mistakes#The_Pixel_Ownership_Problem
http://www.opengl.org/wiki/Common_Mistakes#The_Object_Oriented_Language_Problem
http://www.opengl.org/wiki/Platform_specifics
OpenGL程序在Windows上是如何實現的?
當你編譯一個App時,會鏈接opengl32.dll 。
當你運行此App,opengl32.dll會被加載並檢測是否存在OpenGL驅動器。如果存在,就加載驅動器。例如,ATI的OpenGL驅動器是atioglxx.dll而NVIDIA的是nvoglv32.dll。(這兩個文件名可能隨版本不同而有所差異。)
opengl32.dll僅直接包含OpenGL 1.1的函數。想使用更高版本里的函數的話,你必須使用 wglGetProcAddress 來手動獲取其函數指針。詳情在此。有一些輔助庫幫你做這件事,被稱為 Extension Loading Libraries。
重要的是要知道opengl32.dll屬於Microsoft。所以不要改動它,不要替換它,也不要在發布你的App時把它加安裝包。也不要把nvoglv32.dll(或者其它系統文件)加入安裝包。
安裝OpenGL驅動是用戶的事,不管他們是從哪里(Dell、HP、nVidia、ATI/AMD……)下載。你可以提醒他們,這很正常。
我的OpenGL版本是多少?
使用 glGetString,傳入參數 GL_VERSION。它會返回一個字符串(可能很長)。
在OpenGL 3.0以上,也可以用 glGetIntegerv(GL_MAJOR_VERSION, *) 和 glGetIntegerv(GL_MINOR_VERSION, *) 。
想獲取你的GPU支持的OpenGL最高版本?更新你的顯卡驅動即可,OpenGL就在里面。
假如你當前的OpenGL版本是2.0,而你的GPU卻不支持更高版本,那么顯卡廠商可能就不給你做新的驅動了。
這怎么破?兩個辦法:買個新顯卡,或者使用Mesa3D(純軟件實現的OpenGL,在http://www.mesa3d.org)。不過截止到本文編寫時(2015年5月11日),Mesa3D只支持到OpenGL 3.3,而最新的OpenGL說明書已經出到OpenGL 4.5了。
為什么我的OpenGL版本只有1.4或者更低?
可能原因有三:
1. 在Windows上,如果你創建OpenGL上下文時使用了未加速的(我也不知道什么叫"加速")像素格式,你就只能得到OpenGL1.1版本。
解決辦法是小心選擇像素格式,詳情參考Platform_specifics:_Windows或自行百度。
2. 另一個原因是你的顯卡及其驅動廠商沒有提供新版的OpenGL實現。好多顯卡廠商都已經關門大吉了,哪有什么售后。
不過,在那些尚未死掉的廠商里,Intel集成顯卡最容易出這個問題。對此我們無計可施。NVIDIA 和ATI就很好地支持他們的集成顯卡更新。
3. 最后,檢查一下你有沒有安裝顯卡驅動吧。
用glGetString檢測OpenGL版本正常時,你就萬事俱備了。
glTranslate/glRotate/glScale 有硬件加速嗎?
沒有。
沒有GPU直接支持這三個家伙。他們已經在OpenGL 3.0被標記為棄用了。你應該用你自己的數學庫,自己構造矩陣,把矩陣上傳到shader。這里有幾個庫你可以試試。
現代GPU還支持固定功能管道嗎?
(詳情參見 Legacy OpenGL)
現在GPU不再為管道中的某步計算提供專門的硬件。所有的計算都由shader完成。為了保持兼容性,OpenGL驅動器生成一個特別的shader,這個shader模仿了固定功能管道。
例如渲染一個圖元,固定功能管道的方式是在glBegin() 和glEnd() 之間用glVertex3f( , , ),提交各個頂點的位置屬性。使用shader的方式時,你必須先在內存中用數組保存所有頂點的屬性,然后創建緩存對象,最后用glBufferData、glBufferSubData、glMapBuffer或者glMapBufferRange把數組上傳到顯卡內存中。這樣shader就可以用上傳的數據進行渲染了。
如何在像素空間渲染?(在屏幕固定位置渲染一個模型)
指定這樣一個投影矩陣:
1 glMatrixMode(GL_PROJECTION); 2 glLoadIdentity(); 3 glOrtho(0.0, WindowWidth, 0.0, WindowHeight, -1.0, 1.0); 4 //Setup modelview to identity if you don't need GL to move around objects for you 5 glMatrixMode(GL_MODELVIEW); 6 glLoadIdentity();
注意,在glOrtho中Y軸是從下到上的。當然你可以通過交換bottom和top參數來翻轉Y軸。你得保證你用正確的順序(順時針or逆時針)渲染多邊形,不然OpenGL可能會剔除它。保證不了就禁用剔除: glDisable(GL_CULL_FACE); 。
怎么畫全屏四邊形?
經常有人問,怎么畫一個四邊形,讓它覆蓋全屏?這需要什么樣的投影矩陣?
投影矩陣用單位矩陣就可以了。在老版OpenGL,你可以用
1 glMatrixMode(GL_PROJECTION); 2 glLoadIdentity(); 3 glMatrixMode(GL_MODELVIEW); 4 glLoadIdentity();
在使用shader的OpenGL里,GLSL代碼甚至不需要矩陣。這樣就行:
#version 110 void main() { gl_Position = gl_Vertex; //Just output the incoming vertex }
當然,這要求四邊形的頂點得是:{-1.0, -1.0, 0.0}, {1.0, -1.0, 0.0}, {1.0, 1.0, 0.0}, {-1.0, 1.0, 0.0}。
多索引渲染?
多索引渲染的意思是,每種頂點屬性(位置、法向量等)都有自己的索引數組。OpenGL和Direct3D都不支持多索引渲染。
程序員必須自己調整數據格式,使得各種頂點屬性的數組長度相同。這樣才能使用單一的索引。
使用OBJ文件格式的同學經常問這個問題。
1 v 1.52284 39.3701 1.01523 2 v 36.7365 17.6068 1.01523 3 v 12.4045 17.6068 -32.475 4 and so on ... 5 n 0.137265 0.985501 -0.0997287 6 n 0.894427 0.447214 -8.16501e-08 7 n 0.276393 0.447214 -0.850651 8 and so on ... 9 t 0.6 1 10 t 0.5 0.647584 11 t 0.7 0.647584 12 and so on ... 13 f 102/102/102 84/84/84 158/158/158 14 f 158/158/158 84/84/84 83/83/83 15 f 158/158/158 83/83/83 159/159/159 16 and so on ...
以f開頭的行是面(face)。每個頂點都有三個索引,分別是位置、法向量和貼圖坐標(v、n和t)。上面的例子里,幸運的是每個{位置, 法向量, 貼圖坐標}里的三個分量都是相等的。你得處理它們不相等的情況,比如:
1 f 1/1/1 2/2/2 3/2/2 2 f 5/5/5 6/6/6 3/4/5
這里的3/2/2和3/4/5就是兩個不同的頂點。
所以加載OBJ文件后你得做一些后處理。OBJ文件里的頂點位置數、法向量數、貼圖坐標數……都要相同。例如,你有10各頂點的位置,那么你必須有10個法向量與之對應。你不能有10個頂點的位置卻只有7個法向量。
你有三個選項:
1. 為頂點的每個屬性分別分配一個數組。
2. 創建一個數組,交錯填入各個屬性值。
3. 創建一個數組,依次填入各個屬性。(例如,先把所有位置屬性放到數組開頭,然后放法向量,最后放貼圖坐標)。
詳情可參考Vertex Specification
畫立方體
很多人都需要畫立方體,所以這個問題值得一提。
從上一個問題我們知道,OpenGL只支持單一索引。
由於立方體的面是平的,我們可以用平面着色來渲染各個面。這意味着每個面都只有1個法向量。但是OpenGL只知道頂點的法向量,面的法向量是通過頂點的法向量計算出來的。那么辦法來了:把畫立方體的工作看做畫6個互不相干的正方形的工作。這就是說要把一個頂點復制2次,變為3個。自己體會。
還有個辦法,如下代碼所示:
1 //The following code is for GL 2.0 2 3 //Render face 1 4 glNormal3fv(mynormal1); //Set your face normal 5 glBindBuffer(GL_ARRAY_BUFFER, VertexVBOID); 6 glEnableClientState(GL_VERTEX_ARRAY); 7 glVertexPointer(3, GL_FLOAT, sizeof(MyVertex), BUFFER_OFFSET(0)); //The starting point of the VBO, for the vertices 8 glClientActiveTexture(GL_TEXTURE0); 9 glEnableClientState(GL_TEXTURE_COORD_ARRAY); 10 glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), BUFFER_OFFSET(12)); //The starting point of texcoords, 12 bytes away 11 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexVBOID); //Bind the IBO 12 glDrawElements(GL_QUADS, 4, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0)); 13 14 //Render face 2 15 glNormal3fv(mynormal2); //Set your face normal 16 //We can just call glDrawElements since the rest of the setup is already done above 17 glDrawElements(GL_QUADS, 4, GL_UNSIGNED_SHORT, BUFFER_OFFSET(16)); 18 19 //.... and render face 3 and 4 and 5 and 6
上述代碼中,缺點是渲染1個立方體就需要6次調用 glDrawElements,且使用了被鄙視的 glNormal3fv這個函數。 glNormal3fv不使用VBO里的數據,很low。
但是也有一點好處,繪制1個立方體可以節省3個法向量 * 3個浮點數 * 4字節 * 6 面= 216 字節。
glClear和 glScissor
glScissor 是為數不多的幾個影響glClear的函數之一。如果你只想清除畫面上某一塊區域,就調用 glScissor 和glEnable(GL_SCISSOR_TEST)。
如果你忘記用glDisable(GL_SCISSOR_TEST);關閉剪切測試,glClear就可能會出奇奇怪怪的問題。
屏蔽(Masking)
小心使用 glColorMask , glStencilMask 和 glDepthMask 。如果你用 glDepthMask(FALSE); 禁掉了深度寫入,那么所有對 glClear 的調用都不會清除深度緩存。
glGetError (如何檢查OpenGL錯誤?)
(詳情參見GL Error Codes)
OpenGL保存最近一次的各種類型的錯誤,每調用一次glGetError() 就返回一個類型的錯誤代碼並清除其記錄。如果一個錯誤記錄都沒有,就返回 GL_NO_ERROR 。
這個輔助函數可用於查詢最近所有命中的錯誤:
1 int CheckGLErrors() 2 { 3 int errCount = 0; 4 for(GLenum currError = glGetError(); currError != GL_NO_ERROR; currError = glGetError()) 5 { 6 //Do something with `currError`. 7 ++errCount; 8 } 9 10 return errCount; 11 }
這個函數主動輪詢錯誤信息。這會降低性能。所以最好只在debug版的程序里使用。
有個OpenGL擴展(ARB_debug_output)提供另一種機制來進行錯誤處理,且不需要輪詢。開銷也很明顯,且只在擁有 CONTEXT_DEBUG_BIT_ARB 標記的上下文里才能用。它在OpenGL 4.3里成為了核心特性(KHR_debug),你隨時可以用,但是在非debug上下文里不會有實際有效的log信息。
我應該用什么3D文件格式?
新手往往對使用哪種3D文件格式感到沒底。
OpenGL不加載文件。所以,你可以用任何網格格式。這也意味着你得自己編寫格式解析代碼,OpenGL不管。
現有的文件格式各具特色。下述格式都可以用Open Asset Import 庫解析。
此格式較簡單。每個.obj文件描述一個單獨的網格。Obj文件可以引用外部的材質文件(存儲為.mtl格式,比較少見)。Obj里的網格只能包含位置、法向量和貼圖坐標(可有可無)。
這是二進制的網格格式。包含材質信息,能存儲多個命名的網格。
也是二進制網格格式。不含材質信息,只存儲一個網格。支持關鍵幀動畫,就是說一個單獨的網格文件可能含有所有的關鍵幀及動畫數據。
這是基於XML的網格文件格式。什么玩意都能存。它適合用在不同的3D文件格式之間互相轉換上。
OpenGL有內存泄漏嗎?
沒有。
常常有人認為OpenGL驅動器發生了內存泄漏。他們寫下如下的代碼:
1 glClear(...); 2 SwapBuffers(...);
然后他們觀察到:每次渲染都會使內存占用量增長。
這其實很正常。驅動器可能在另一個線程上進行優化相關的運算,或者在准備緩存數據。具體干什么無法得知,但是內存泄漏是沒有的。
也有人使用 glDeleteTextures 或者 glDeleteLists 或者其它的delete函數,然后他們注意到內存占用量沒有下降。這是你我無能為力的。內存管理是驅動器自己負責的,它可能不在你調用delete函數時就立即執行釋放內存的操作。所以這也不是什么內存泄漏的證據。
誰管理內存?OpenGL如何管理內存?
顯卡的內存也是有限的。如果你想分配太多的緩存對象或者貼圖或者其它OpenGL資源,OpenGL驅動器就會把他們存到系統內存里。當你使用他們的時候,驅動器會視情況做數據交換。這顯然會降低渲染速度。驅動器本身持有的內存量也是有限的,所以你調用 glGetError() 時可能會得到一個 GL_OUT_OF_MEMORY 。就算還有一些內存可用,如果你申請一個超大的緩存對象,OpenGL驅動器也會給你一個 GL_OUT_OF_MEMORY。
內存不足的問題在OpenGL說明書里沒有寫。因為OpenGL是與系統無關的,它才不管系統設計,不在乎系統有沒有顯卡,用的是集成顯卡還是獨立顯卡。
我應該用顯示列表、頂點數組還是頂點緩存對象?
詳情參見(Vertex Specification)
顯示列表和頂點數組從最初的OpenGL開始就有。頂點緩存對象在OpenGL 1.5引入。他們都可以渲染圖元,但是有很大的區別。
顯示列表里的數據提交到顯卡內存后是不能改變的,但渲染速度較快。
多個頂點信息保存在數組里,通過一次API調用就可以完成傳送,但是始終保存在App內存中,每次渲染時都要傳送到顯卡內存。
頂點緩存對象把頂點信息存儲在顯卡內存里。這避免了每次渲染都傳送頂點數據。且數據是可以更新的。最后,需要調用的API次數也很少。
顯示列表和頂點數組都是OpenGL最古老的特性。現代OpenGL一般都用頂點緩存對象和頂點數組對象描述頂點信息。建議你克制使用那些古老的特性。
當且僅當需要維護已有的古老代碼且不可能重寫渲染系統時,再用顯示列表描述靜態的頂點信息;如果頂點信息需要更新,就用頂點數組。如果有立即模式(比如glBegin() ),包含的頂點有幾百幾千,那么用顯示列表或頂點數組都可以提升性能。
Unresolved External Symbol是什么意思?
新手編譯OpenGL程序時可能會遇到這樣的錯誤:
error LNK2001: unresolved external symbol _glBegin
你需要告訴編譯器它應該搜索哪個庫文件。
使用VC++2010時,你可以右擊項目,選擇'屬性'。在屬性對話框里,在左側,打開'配置屬性',打開'鏈接器',點擊'輸入'。在右側,有一項'附加依賴'。輸入庫的名字,用分號隔開。如果是OpenGL,就輸入'opengl32.lib',如果是GLU,就輸入'glu32.lib'。
你也可以在cpp文件里添加下面兩行來達到同一效果。
1 #pragma comment(lib, "opengl32.lib") 2 #pragma comment(lib, "glu32.lib")
不過有的編譯器可能不支持#pragma。
我就不一一列舉各種IDE的做法了,請自行百度。
如果你使用GCC,在這樣編譯時:
1 gcc -lGL -lglut myprogram.c -o myprogram
編譯器告訴你:
/tmp/ccCQkTKm.o:myprogram.c:function display: error: undefined reference to 'gluLookAt'
這是因為你使用了GLU但是沒有鏈接GLU庫,你應該這么寫:
1 gcc -lGL -lGLU -lglut myprogram.c -o myprogram
Not Declared In This Scope是什么意思?
有時候你會得到這樣的編譯錯誤:
GL_TEXTURE_3D was not declared in this scope
這是因為編譯器找不到 GL_TEXTURE_3D 的聲明。
你可能沒有include合適的OpenGL頭文件。gl.h可能不包含所有的東西。你應該用OpenGL Loading Library;;不然就得用the OpenGL Registry里的glext.h,而且還得手動加載OpenGL函數。
OpenGL最多渲染8個光源嗎?
以前,OpenGL固定功能管道有個最大光源數的概念。現在,shader根本沒有光源的概念,你可以用shader渲染任意數量的光源。
經常有人問為什么管道功能管道最多渲染8個光源。其實並非如此。OpenGL要求的是最少支持8個光源。你的驅動器/GPU可以支持超過8個光源,但是他們一般都限制為8個。原因是有3、4個光源時,再增加光源就對一個照射到一個平面上的光沒多少區別了。所以,實際上8是一個足夠大的數了。
例如,你設計了一個城市,路上有路燈,那需要的光源肯定海了去了。怎么辦?辦法是把你的街道划分成小段,使得只有3個光源能影響到這段路里的平面。
例如,你在做一個粒子系統,每個粒子都是一個光源,所以你可能需要1000個光源。對於舊硬件(比如僅支持到OpenGL 1.5的),這太瘋狂了。你可以用一個覆蓋整個粒子系統范圍的光源代替那1000個光源。
如果你實在想要超過8個光源,你可以采用多遍渲染的方式。先開啟8個光源,渲染一遍,然后開啟混合並使用glBlendFunc(GL_ONE, GL_ONE),然后再渲染一遍。你可能需要把深度測試設置為 GL_LEQUAL 。
現在換個角度看問題。那些古老的游戲使用光源了嗎?實際上,沒有。很多古老的游戲對靜態表面使用燈光貼圖。對於運動的物體,程序員自己計算光照或者預計算(即light volume)。
能否預編譯shader?
OpenGL 4.1增加了預編譯shader的功能。你需要為你的顯卡/GPU下載專門的shader編譯器。這玩意換個GPU可能就不行了;顯卡驅動更新后可能也就不行了。這個特性的目的是一次編譯后留作以后運行。這避免了每次運行都需要編譯、鏈接的時間。但是,驅動器更新時可能更新shader版本,這會強制你再次編譯shader。
如果你發布的App里使用了預編譯的shader,你還是得把shader源碼也放在安裝包里。因為一旦預編譯shader無法加載,你只能重新用源碼編譯一次。
注意,實現這一特性的擴展(ARB_get_program_binary)在4.1之前的硬件就廣泛存在了。所以4.x級的硬件不是必須的。
如何組織紋理?
這項技術也稱為紋理地圖集。
有的程序員認為,把多個小型2D紋理放到一個大型2D紋理上(1024 x 1024什么的),可以少調用一次 glBindTexture,也許能提升性能。這是個辦法,但是你必須小心處理你的模型的紋理坐標。你也要仔細處理紋理濾波,因為線性濾波會導致紋素滲透(texel bleed),即一個模型使用了其它模型的紋理(因為紋素相鄰了)
如果所有的2D紋理的大小都相同,你還可以把它們全放到一個3D紋理中。OpenGL 1.3開始支持 GL_TEXTURE_3D。不過在線性濾波時還是會有紋素滲透的問題。在S、T方向上不會有事,有事的是紋理層之間。
另一個辦法是使用2D紋理數組。OpenGL 3.0開始支持 GL_TEXTURE_2D_ARRAY。這個方法沒有紋素滲透的問題,不過也要求所有的2D紋理大小都相同。
字體渲染和文字渲染
OpenGL是一個底層庫,它不處理渲染文字的事。它只渲染點、線、三角形這些。想渲染文字,要么使用第三方庫,要么自己做個庫。
一種最簡單的方法是把字符做成紋理。需要渲染文字的時候就渲染四邊形,並把紋理貼上去。
當然,你也可以做一個整句的紋理。
你也可以讀取操作系統的字體信息,然后生成紋理。使用方法同上。
Windows提供了一些渲染文字的方法,不過很老了,不建議使用。詳情參見(wglUseFontBitmaps)和(wglUseFontOutlines)。
更多內容可參考 http://www.opengl.org/archives/resources/faq/technical/#indx0170
GLU是什么?
GLU是OpenGL utility library 的縮寫。它基於老版OpenGL特性,提供一些實用的功能,最著名的例子就是gluLooktAt()和gluPerspective(),它們簡化了設置視圖和投影矩陣的工作。但是,這兩個函數都使用老版的特性(矩陣棧等)。這在現代OpenGL程序中不提倡。
如果你使用現代OpenGL(3.0及其以上),建議你要么使用非基於OpenGL的第三方庫要么自己寫這種庫,要么就使用OpenGL 3.0以上的核心特性。這里有一些可用的第三方庫。
如果你仍然打算使用GLU,你要知道Windows提供glu32.dll的是1.2版的GLU。你的編譯器需要glu32.lib或glu32.a。最新的GLU是1.3版。你可以下載Mesa3D(http://www.mesa3d.org)提供的完整包。
這個包里有GLU1.3的源碼,你得自己編譯它。記住,你不能替換Microsoft的glu32.dll,它是系統文件,永遠都不要覆蓋它。