學(xué)習(xí)目標(biāo):
- OpenGL 渲染結(jié)構(gòu)
- 如何使用7種OpenGL基礎(chǔ)圖元
- 如何使用儲(chǔ)存著色器
- 如何使用Uniform屬性
- 如何使用GLBatch 幫助類傳遞幾何圖形
OpenGL 渲染結(jié)構(gòu)
-
基礎(chǔ)圖形管線
- OpenGL 中的圖元是頂點(diǎn)的集合以預(yù)定義的?式結(jié)合?起。
- 例如:?個(gè)單獨(dú)的點(diǎn)就是?個(gè)圖元。它只需要一個(gè)頂點(diǎn)
- OpenGL 中的圖元是頂點(diǎn)的集合以預(yù)定義的?式結(jié)合?起。
-
渲染管線簡(jiǎn)化流程
流程圖.png-
客戶端和服務(wù)端
- 管線分為上下兩部分,上部分是客戶端,而下部分則是服務(wù)端。
- 客戶端是存儲(chǔ)在CPU儲(chǔ)存器中的,并且在應(yīng)用程序中執(zhí)行或者在主系統(tǒng)內(nèi)存中驅(qū)動(dòng)程序中執(zhí)行。驅(qū)動(dòng)程序回將渲染命令和數(shù)組組合起來,發(fā)送給服務(wù)器執(zhí)行?。ㄔ?臺(tái)典型的個(gè)人計(jì)算機(jī)上,服務(wù)器實(shí)際上就是圖形加速卡上的硬件和內(nèi)存)
- 服務(wù)?和客戶機(jī)在功能上也是異步的。它們是各?獨(dú)立的軟件塊或硬件塊。我們是希望它們2個(gè)端都盡量在不停的工作。客戶端不斷的把數(shù)據(jù)塊和命令塊組合在?起輸送到緩沖區(qū),然后緩沖區(qū)就會(huì)發(fā)送到服務(wù)器執(zhí)行。
- 如果服務(wù)器?停?工作等待客戶機(jī),或者客戶機(jī)停?工作來等待服務(wù)?做好接受更多的命令和準(zhǔn)備,我們把這種情況成為
管線停滯
-
著色器
- 上圖的Vertex Shader(頂點(diǎn)著??) 和 Fragment Shader(?段著??)。
- 著??是使用GLSL編寫的程序,看起來與C語?非常類似。 著??必須從源代碼中編譯和鏈接在一起。最終準(zhǔn)備就緒的著??程序
- 頂點(diǎn)著??-->處理從客戶機(jī)輸?的數(shù)據(jù)、應(yīng)?變換、進(jìn)?其他的類型的數(shù)學(xué)運(yùn)算來計(jì)算關(guān)照效果、位移、顏?值等等。(**為了渲染共有3個(gè)頂點(diǎn)的三?形,頂點(diǎn)著??將執(zhí)行3次,也就是為了每個(gè)頂點(diǎn)執(zhí)??次)在?前的硬件上有多個(gè)執(zhí)?單元同時(shí)運(yùn)?,就意味著所有的3個(gè)頂點(diǎn)可以同時(shí)進(jìn)?處理!
- 上圖的Vertex Shader(頂點(diǎn)著??) 和 Fragment Shader(?段著??)。
- primitive Assembly(圖元裝配)
- 說明的是:3個(gè)頂點(diǎn)已經(jīng)組合在一起,而三角形已經(jīng)逐個(gè)片段地進(jìn)行了光柵化。每個(gè)片段通過執(zhí)行
片元著色器
- 說明的是:3個(gè)頂點(diǎn)已經(jīng)組合在一起,而三角形已經(jīng)逐個(gè)片段地進(jìn)行了光柵化。每個(gè)片段通過執(zhí)行
-
注意要點(diǎn)- 必須要為著色器提供數(shù)據(jù),否則無法實(shí)現(xiàn)!
- 為著色器傳遞渲染數(shù)據(jù)的方法有三種
- 屬性(Attributes)
- uniform值
- 紋理Texture Data
-
客戶端和服務(wù)端
-
屬性、uniform值、紋理、輸出
-
屬性
- 對(duì)每一個(gè)頂點(diǎn)都要作改變的數(shù)據(jù)元素。實(shí)際上,頂點(diǎn)位置本身就是一個(gè)屬性。屬性值可以是浮點(diǎn)數(shù)、整數(shù)、布爾數(shù)據(jù)。
- 屬性還可以是:紋理坐標(biāo)、顏色值、關(guān)照計(jì)算表面法線
- 在頂點(diǎn)程序(shader渲染)可以代表你想要的任何意義。因?yàn)槎际悄阍O(shè)定的。
- 屬性會(huì)從本地客戶機(jī)內(nèi)存中復(fù)制存儲(chǔ)在圖形硬件中的一個(gè)緩沖區(qū)上。這些屬性只提供給頂點(diǎn)著??使用,對(duì)于?元著??沒有太?意義。
- 聲明: 這些屬性對(duì)每個(gè)頂點(diǎn)都要做改變,但并不意味著它們的值不能重復(fù)。通常情況下,它們都是不一樣的,但有可能整個(gè)數(shù)組都是同一值的情況。
-
Uniform值
- 屬性是?種對(duì)整個(gè)批次屬性都取統(tǒng)一值的單一值。它是不變的。通過設(shè)置uniform變量就緊接著發(fā)送?個(gè)圖元批次命令,而Uniform變量實(shí)際上可以?數(shù)次限制地使?,設(shè)置一個(gè)應(yīng)用于整個(gè)表面的單個(gè)顏色值,還可以設(shè)置一個(gè)時(shí)間值。在每次渲染某種類型的頂點(diǎn)動(dòng)畫時(shí)修改它。
- 注意: 這?的uniform 變量每個(gè)批次改變一次,?不是每個(gè)頂點(diǎn)改變?次。
- 與屬性相同點(diǎn):可以是浮點(diǎn)值、整數(shù)、布爾值
- 與屬性不同:頂點(diǎn)著色器和片元著色器都可以使用uniform變量。uniform變量還可以是標(biāo)量類型、矢量類型、uniform矩陣。
-
紋理
- 傳遞給著??的第三種數(shù)據(jù)類型:紋理數(shù)據(jù)
- 在頂點(diǎn)著??、?段著色?中都可以對(duì)紋理數(shù)據(jù)進(jìn)行采樣和篩選。
- 典型的應(yīng)用場(chǎng)景: 片段著??對(duì)?個(gè)紋理值進(jìn)行采樣,然后在一個(gè)三?形表面應(yīng)用渲染紋理數(shù)據(jù)。
- 紋理數(shù)據(jù),不僅表現(xiàn)在圖形,很多圖形文件格式都是以無符號(hào)字節(jié) (每個(gè)顏?色通道8位)形式對(duì)顏色分量進(jìn)行存儲(chǔ)的。
- 傳遞給著??的第三種數(shù)據(jù)類型:紋理數(shù)據(jù)
- 輸出(outs)
- 在圖表中第四種數(shù)據(jù)類型是輸出(out);輸出數(shù)據(jù)是作為?個(gè)階段著色器的輸出定義的,而后續(xù)階段的著??則作為輸?定義。
- 輸出數(shù)據(jù)可以簡(jiǎn)單的從?個(gè)階段傳遞到下一個(gè)階段,也可以?不同的方式插入。
- 客戶端的代碼接觸不到這些內(nèi)部變量我們的OpenGL開發(fā)暫時(shí)接觸不到。
-
屬性
-
創(chuàng)建坐標(biāo)系
-
正投影圖
正投影圖.png 這就是一個(gè)正投影的例子,在3個(gè)軸(X,Y,Z)中,它們的范圍都是-100到+100.這個(gè)視景體將包括所有的幾何圖形。
如果你指定了視景體外的幾何圖形,就會(huì)被裁減掉!(它將沿著視景體的邊界進(jìn)行剪切)
在正投影中,所有在這個(gè)空間范圍內(nèi)的所有東西都將被呈現(xiàn)在屏幕上。?不存在照相機(jī)或視點(diǎn)坐標(biāo)系的概念。
-
透視投影
透視投影.png透視投影會(huì)進(jìn)行透視除法對(duì)距離觀察者很遠(yuǎn)的對(duì)象進(jìn)行縮短和收縮。在投影到屏幕之后,視景體背?與視景體正面的寬度測(cè)量標(biāo)準(zhǔn)不同。- 上圖所示:平截頭體(frustum)的幾何體,它的觀察方向是從金字塔的尖端到寬闊端。觀察著的視點(diǎn)與金字塔的尖端拉開一定距離。
GLFrustum類通過setPerspective ?方法為我們構(gòu)建?個(gè)平截頭體。 CLFrustum::SetPerspective(float fFov,float fAspect,float fNear ,float fFar); 參數(shù): fFov:垂直?向上的視場(chǎng)?度 fAspect:窗口的寬度與高度的縱橫比 fNear:近裁剪?距離 (視角到近裁剪面距離為fNear) fFar:遠(yuǎn)裁剪面距離(視角到遠(yuǎn)裁剪面距離為fFar) 縱橫比 = 寬(w)/?(h)
- 上圖所示:平截頭體(frustum)的幾何體,它的觀察方向是從金字塔的尖端到寬闊端。觀察著的視點(diǎn)與金字塔的尖端拉開一定距離。
-
-
使用儲(chǔ)存著色器
- 使用背景
- 在OpenGL 核?框架中,并沒有提供任何內(nèi)建渲染管線,在提交?個(gè)?何圖形進(jìn)行渲染之前,必須實(shí)現(xiàn)?個(gè)著色?。 在前面的課程可以使用存儲(chǔ)著??。這些存儲(chǔ)著??由GLTools的C++類 GLShaderManager管理。它們能夠滿足進(jìn)行基本渲染的基本要求。要求不高的程序員,這些存儲(chǔ)著??已經(jīng)?以滿?他們的需求。但是,隨著時(shí)間 和經(jīng)驗(yàn)的提升,?部分開發(fā)者可能不滿?于此。 會(huì)開始?己著手去寫著色?。
- 儲(chǔ)存著色器的使用
-
GLShaderManager的初始化// GLShaderManager 的初始化 GLShaderManager shaderManager; shaderManager.InitializeStockShaders(); -
GLShaderManager屬性
GLShaderManager 屬性.png存儲(chǔ)著??為每?個(gè)變量都使?一致的內(nèi)部變量命名規(guī)和相同的屬性槽。以上就是存儲(chǔ)著??的屬性列表
-
GLShanderManager的uniform值- ?般情況,要對(duì)?何圖形進(jìn)?渲染,我們需要給對(duì)象遞交屬性矩陣,?先要綁定我們想要使?的著?程序上,并提供程序的uniform值。但是GLShanderManager 類可以暫時(shí)為我們完成工作。
-
useStockShader函數(shù)會(huì)選擇?個(gè)存儲(chǔ)著?器并提供這個(gè)著??的uniform值。
GLShaderManager::UserStockShader(GLeunm shader...); -
單位(Identity 著??)
GLShaderManager::UserStockShader(GLT_ATTRIBUTE_VERTEX,GLfloat vColor[4]);- 單位著??:只是簡(jiǎn)單地使用默認(rèn)笛卡爾坐標(biāo)系(坐標(biāo)范圍(-1.0, 1.0))。
- 所有的?段都應(yīng)用同?種顏?,?何圖形為實(shí)?和未渲染的。
- 需要設(shè)置存儲(chǔ)著色器?個(gè)屬性: GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量)
- 參數(shù)2:vColor[4],你需要的顏色
-
平面著色器
GLShaderManager::UserStockShader(GLT_SHADER_FLAT,GLfloat mvp[16],GLfloat vColor[4]);- GLT_SHADER_FLAT:平面著色器
- mvp[16]:允許變化的4*4矩陣
- 顏色:vColor[4]
- 它將統(tǒng)一著?色器?進(jìn)行了拓展。允許為?何圖形變換指定一個(gè) 4 * 4 變換矩陣。經(jīng)常被稱為
模型視圖投影矩陣
-
上色著色器
- 在?何圖形中應(yīng)用的變換矩陣。需要設(shè)置存儲(chǔ)著??的 GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量) 和GLT_ATTRIBUTE_COLOR(顏?分量) 2個(gè)屬性。顏色值將被平滑地插?頂點(diǎn)之間(平滑著?)
GLShaderManager::UserStockShader(GLT_SHADER_SHADED,GLfloat mvp [16]); -
默認(rèn)光源著色器
GLShaderManager::UserStockShader(GLT_SHADER_DEFAULT_LIGHT,GLfl oat mvMatrix[16],GLfloat pMatrix[16],GLfloat vColor[4]);- GLT_SHADER_DEFAULT_LIGHT: 默認(rèn)光源著??
- mvMatrix[16]: 模型視圖矩陣
- pMatrix[16]: 投影矩陣
- vColor[4]: 顏?值
-
點(diǎn)光源著??
LShaderManager::UserStockShader(GLT_SHADER_DEFAULT_LIGHT_DIEF,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vLightPos[3],GLfloat vColor[4]);- 參數(shù)1:點(diǎn)光源著??
- 參數(shù)2:模型視圖矩陣
- 參數(shù)3:投影矩陣
- 參數(shù)4:視點(diǎn)坐標(biāo)光源位置
- 參數(shù)5:顏?值
- 區(qū)別:點(diǎn)光源著??和默認(rèn)光源著??很相似,區(qū)別在于:光源位置是特定的。 同樣需要設(shè)置存儲(chǔ)著??的 GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量) 和
GLT_ATTRIBUTE_NORMAL(表?法線)
-
紋理理替換矩陣
- 著色器通過給定的模型視圖投影矩陣,使?綁定到 nTextureUnit (紋理單元) 指定紋理單元的紋理對(duì)?何圖形進(jìn)行變化。片段顏色:是直接從紋理樣本中直接獲取的。
需要設(shè)置存儲(chǔ)著??的 GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量) 和
GLT_ATTRIBUTE_NORMAL(表?法線)GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_REPLACE,GLfloat mvMatrix[16],GLint nTextureUnit);
- 著色器通過給定的模型視圖投影矩陣,使?綁定到 nTextureUnit (紋理單元) 指定紋理單元的紋理對(duì)?何圖形進(jìn)行變化。片段顏色:是直接從紋理樣本中直接獲取的。
-
紋理調(diào)整著??
- 將?個(gè)基本色乘以一個(gè)取自紋理單元 nTextureUnit 的紋理。需要設(shè)置存儲(chǔ)著??的
GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量)和
GLT_ATTRIBUTE_TEXTURE0(紋理坐標(biāo)).GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_MODULATE,GLfloat mvMatrix[16],GLfloat vColor[4],GLint nTextureUnit);
- 將?個(gè)基本色乘以一個(gè)取自紋理單元 nTextureUnit 的紋理。需要設(shè)置存儲(chǔ)著??的
-
紋理光源著??
- 將?個(gè)紋理通過漫反射照明計(jì)算機(jī)進(jìn)?調(diào)整(相乘)。光線在視覺空間中 的位置是給定的。需要設(shè)置存儲(chǔ)著色?的
GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量)和
GLT_ATTRIBUTE_TEXTURE0(紋理坐標(biāo))、GLT_ATTRIBUTE_NORMAL(表面法線)GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIEF,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vLightPos[3],GLfloat vBaseColor[4],GLint nTextureUnit); - 參數(shù)1:紋理理光源著??
- 參數(shù)2:投影矩陣
- 參數(shù)3:視覺空間中的光源位置
- 參數(shù)4:?何圖形的基本色
- 參數(shù)5:將要使?的紋理單元
- 將?個(gè)紋理通過漫反射照明計(jì)算機(jī)進(jìn)?調(diào)整(相乘)。光線在視覺空間中 的位置是給定的。需要設(shè)置存儲(chǔ)著色?的
-
-
點(diǎn)鏈接
第一次學(xué)習(xí)任何計(jì)算機(jī)系統(tǒng)中繪制任何類型的 2 D 圖形時(shí),大多數(shù)可能從繪制像素開始。像素是計(jì)算機(jī)屏幕上顯示的最小元素。以下是最簡(jiǎn)單額計(jì)算機(jī)圖形:在計(jì)算機(jī)屏幕繪制一個(gè)點(diǎn),并將它設(shè)置一個(gè)特定的顏色。在這個(gè)簡(jiǎn)單的基礎(chǔ)。上慢慢學(xué)會(huì)創(chuàng)建線、多邊形、圓和其他性質(zhì)和圖形。但是!使用 OpenGL 在計(jì)算機(jī)屏幕上進(jìn)行繪圖則完全不同。我們不關(guān)物理屏幕坐標(biāo)和像素,關(guān)注的是視景體中的位置坐標(biāo)。將這些點(diǎn)、線和三角形從創(chuàng)建 3 D 空間投影到計(jì)算機(jī)屏幕上的 2 D 圖形則是著色器程序和光柵化硬件所要完成的工作。
-
點(diǎn)和線
- 今天我們將從更底層更基礎(chǔ)的角度來詳細(xì)學(xué)習(xí)OpenGL圖元渲染。點(diǎn),是最簡(jiǎn)單的圖像。每個(gè)特定的頂點(diǎn)在屏幕上都僅僅是一個(gè)單獨(dú)的點(diǎn)。默認(rèn)的情況下,點(diǎn)的大小是一個(gè)像素的大小。
- 修改點(diǎn)大小的方法:
//1.最簡(jiǎn)單也是最常用的4.0f ,表示點(diǎn)的大小 glPointSize(4.0f) ; //2.設(shè)置點(diǎn)的大小范圍和點(diǎn)與點(diǎn)之間的間隔 GLfloat sizes[2] = {2.0f,4.0f}; GLfloat step = 1.0f; //獲取點(diǎn)大小范圍和最小步長(zhǎng) glGetFloatv(GL_POINT_SIZE_RANGE,sizes); glGetFloatv(GL_ POINT_GRAULARITY ,&step); //3.通過使用程序點(diǎn)大小模式來設(shè)置點(diǎn)大小 glEnable (GL_ PROGRAM_ POINT_SIZE) ; //這種模式下允許我們]通過編程在頂點(diǎn)著器或幾何著色器中設(shè)置點(diǎn)大小。著色器內(nèi)建變量: gl_ PointSize,并且可以在器源碼直接寫 gl_PointSize = 5.0;
-
線
- 比點(diǎn)更進(jìn)一步的就是獨(dú)立線段了。一個(gè)線段就是2個(gè)頂點(diǎn)之間繪制的。
默認(rèn)情況下,線段的寬度是一個(gè)像素。改變線段唯一的方式通過:
//1.設(shè)置獨(dú)立線段寬度為2.5f; glLineWidth(2.5f); - 比點(diǎn)更進(jìn)一步的就是獨(dú)立線段了。一個(gè)線段就是2個(gè)頂點(diǎn)之間繪制的。
-
線環(huán)
- 線環(huán)是線帶的一種簡(jiǎn)單拓展。在線帶的基礎(chǔ)上額外增加一條線帶閉合的。
-
繪制三角形
-
最簡(jiǎn)單的實(shí)體多邊形就是三角形,它只有3個(gè)邊。光柵化硬件最歡迎三角形。并且現(xiàn)在OpenGL已經(jīng)是OpenGL中支持的唯一種多邊形。每3個(gè)頂點(diǎn)定義一個(gè)新的三角形。
三角形.png -
在本次的學(xué)習(xí)中,我們不僅繪制一個(gè)三角形,更繪制4個(gè)三角形組成金字塔形的三角形。我們可以使用方向鍵來旋轉(zhuǎn)金字塔,從不同角度進(jìn)行觀察。但是這個(gè)金字塔沒有底面,所以我們可以看到它的底部。
image.png
-
-
環(huán)繞
-
將順時(shí)針方向繪制的三角形用逆時(shí)針的方式繪制。在繪制第一個(gè)三角形時(shí),線條是按照從VO-V1,再到V2。最后再回到VO的個(gè)閉合三角形。這個(gè)是沿著頂點(diǎn)順時(shí)針方向。這種順序與方向結(jié)合來指定頂點(diǎn)的方式稱為環(huán)繞。
image.png 上圖的2個(gè)三角形的纏繞方向完全相反。
在默認(rèn)的情況下,OpenGL認(rèn)為具有逆時(shí)針方向環(huán)繞的多邊形是正面的。而右側(cè)的順時(shí)針方向三角形是三角形的背面。
-
為什么會(huì)認(rèn)為這個(gè)問題會(huì)很重要了?因?yàn)槲覀兂3OM麨橐粋€(gè)多邊形的正面和背面分別設(shè)置不同的物理特征。我們可以完全隱藏一個(gè)多邊形的背面,或者給它設(shè)置一種不同的顏色和反射屬性。紋理圖像在背面三角形中也是相反的。在一個(gè)場(chǎng)景中,使所有的多邊形保持環(huán)繞方向的一致,并使用正面多邊形來繪制所有實(shí)心物體的表面是非常重要的。//定義前向和背向的多變形: glF rontFace (mode) 參數(shù): GL_CW | GL_CCW GL_CCW: 表示傳入的mode會(huì)選擇逆時(shí)針為前向 GL_CW: 表示順時(shí)針為前向。 默認(rèn): GL_CCW。逆向時(shí)針為前向。 -
三角地帶
image.png- 對(duì)于很多表面和形狀來說,我們可能需要繪制幾個(gè)相連的三角形。我們可以使用GL_TRIANGLE_STRIP圖元繪制一串相連的三角形。從而節(jié)省大量的時(shí)間。使用三角帶而不是分別指定每個(gè)三角形,這樣做的優(yōu)點(diǎn):
- 1.用前3個(gè)頂點(diǎn)指定第1個(gè)三角形之后,對(duì)于接下來的每一個(gè)三角形,只需要再指定1個(gè)頂點(diǎn)。需要繪制大量的三角形時(shí),采用這種方法可以節(jié)省大量的程序代碼和數(shù)據(jù)存儲(chǔ)空間。
-
2.提供運(yùn)算性能和節(jié)省帶寬。更少的頂點(diǎn)意味著數(shù)據(jù)從內(nèi)存?zhèn)鬏數(shù)綀D形卡的速度更快,并且頂點(diǎn)著色器需要處理的次數(shù)也更少了。
image.png
- 對(duì)于很多表面和形狀來說,我們可能需要繪制幾個(gè)相連的三角形。我們可以使用GL_TRIANGLE_STRIP圖元繪制一串相連的三角形。從而節(jié)省大量的時(shí)間。使用三角帶而不是分別指定每個(gè)三角形,這樣做的優(yōu)點(diǎn):
-
-
簡(jiǎn)單批次容器
- GLTools庫中包含額一個(gè)簡(jiǎn)單的容器類,叫做GBatch。這個(gè)類可以作為7種圖元的簡(jiǎn)單批次容器使用。而且它知道在使用GL_ ShaderManager 支持的任意存儲(chǔ)著色器時(shí)如何對(duì)圖元進(jìn)行渲染。
void GLBatch: :Begain(GLeunm primitive,GLuint nVerts,GLuint nTe xttureUnints = 0) ; 參數(shù)1:圖元 參數(shù)2:頂點(diǎn)數(shù) 參數(shù)3:一組或者2組紋理坐標(biāo)(可選) // 復(fù)制表面法線 void GLBatch:: CopyNormalDataf(GLfloat *vNorms) ; //復(fù)制顏色 void GLBatch:: CopyColorData4f(GLfloat *vColors) ; //復(fù)制紋理坐標(biāo) void GLBatch::CopyTexCoordData2f(GLFloat *vTextCoords , GLuint uiTextureLayer); // 結(jié)束繪制 void GLBatch::End(void);
- GLTools庫中包含額一個(gè)簡(jiǎn)單的容器類,叫做GBatch。這個(gè)類可以作為7種圖元的簡(jiǎn)單批次容器使用。而且它知道在使用GL_ ShaderManager 支持的任意存儲(chǔ)著色器時(shí)如何對(duì)圖元進(jìn)行渲染。
- 使用背景
源碼實(shí)例
/*
實(shí)現(xiàn)功能:
點(diǎn)擊屏幕,將固定位置上的頂點(diǎn)數(shù)據(jù)以6種不同形態(tài)展示!
*/
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
/*
GLMatrixStack 變化管線使用矩陣堆棧
GLMatrixStack 構(gòu)造函數(shù)允許指定堆棧的最大深度、默認(rèn)的堆棧深度為64.這個(gè)矩陣堆在初始化時(shí)已經(jīng)在堆棧中包含了單位矩陣。
GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
//通過調(diào)用頂部載入這個(gè)單位矩陣
void GLMatrixStack::LoadIndentiy(void);
//在堆棧頂部載入任何矩陣
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
*/
// 各種需要的類
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrame cameraFrame;//觀察者位置
GLFrame objectFrame;//被觀察者位置
//投影矩陣
GLFrustum viewFrustum;
//容器類(7種不同的圖元對(duì)應(yīng)7種容器對(duì)象)
GLBatch pointBatch; // 點(diǎn)
GLBatch lineBatch; // 線
GLBatch lineStripBatch; // 線段
GLBatch lineLoopBatch;//線環(huán)
GLBatch triangleBatch;
GLBatch triangleStripBatch;
GLBatch triangleFanBatch;
//幾何變換的管道
GLGeometryTransform transformPipeline;
M3DMatrix44f shadowMatrix;
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
// 跟蹤效果步驟
int nStep = 0;
// 此函數(shù)在呈現(xiàn)上下文中進(jìn)行任何必要的初始化。.
// 這是第一次做任何與opengl相關(guān)的任務(wù)。
void SetupRC()
{
//灰色背景
glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
//初始化
shaderManager.InitializeStockShaders();
// 開啟深度測(cè)試
glEnable(GL_DEPTH_TEST);
//設(shè)置變換管線以使用兩個(gè)矩陣堆棧
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
//觀察者移動(dòng)位置
cameraFrame.MoveForward(-20.0f);
/*
常見函數(shù):
void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
參數(shù)1:表示使用的圖元
參數(shù)2:頂點(diǎn)數(shù)
參數(shù)3:紋理坐標(biāo)(可選)
//負(fù)責(zé)頂點(diǎn)坐標(biāo)
void GLBatch::CopyVertexData3f(GLFloat *vNorms);
//結(jié)束,表示已經(jīng)完成數(shù)據(jù)復(fù)制工作
void GLBatch::End(void);
*/
//定義一些點(diǎn),類似佛羅里達(dá)州的形狀。
GLfloat vCoast[24][3] = {
{2.80, 1.20, 0.0 }, {2.0, 1.20, 0.0 },
{2.0, 1.08, 0.0 }, {2.0, 1.08, 0.0 },
{0.0, 0.80, 0.0 }, {-.32, 0.40, 0.0 },
{-.48, 0.2, 0.0 }, {-.40, 0.0, 0.0 },
{-.60, -.40, 0.0 }, {-.80, -.80, 0.0 },
{-.80, -1.4, 0.0 }, {-.40, -1.60, 0.0 },
{0.0, -1.20, 0.0 }, { .2, -.80, 0.0 },
{.48, -.40, 0.0 }, {.52, -.20, 0.0 },
{.48, .20, 0.0 }, {.80, .40, 0.0 },
{1.20, .80, 0.0 }, {1.60, .60, 0.0 },
{2.0, .60, 0.0 }, {2.2, .80, 0.0 },
{2.40, 1.0, 0.0 }, {2.80, 1.0, 0.0 }
};
// 用點(diǎn)的形式--表示佛羅里達(dá)州的形狀
pointBatch.Begin(GL_POINTS, 24);
pointBatch.CopyVertexData3f(vCoast);
pointBatch.End();
//通過線的形式--表示佛羅里達(dá)州的形狀
lineBatch.Begin(GL_LINES, 24);
lineBatch.CopyVertexData3f(vCoast);
lineBatch.End();
//通過線段的形式表示表示佛羅里達(dá)州的形狀
lineStripBatch.Begin(GL_LINE_STRIP, 24);
lineStripBatch.CopyVertexData3f(vCoast);
lineStripBatch.End();
//通過線環(huán)的形式表示佛羅里達(dá)州的形狀
lineLoopBatch.Begin(GL_LINE_LOOP, 24);
lineLoopBatch.CopyVertexData3f(vCoast);
lineLoopBatch.End();
//通過三角形逆時(shí)針創(chuàng)建金字塔
GLfloat vPyramid[12][3] = {
-2.0f, 0.0f, -2.0f,
2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, -2.0f,
2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
-2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f,
};
// GL_TRIANGLES 每3個(gè)頂點(diǎn)定義一個(gè)新的三角形
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
//三角形扇形--六邊形
GLfloat vPoints[100][3]; // 超過我們需要的數(shù)組
int nVerts = 0;
//半徑
GLfloat r = 3.0f;
//原點(diǎn)(x,y,z); = (0, 0, 0)
vPoints[nVerts][0] = 0.0f;
vPoints[nVerts][1] = 0.0f;
vPoints[nVerts][2] = 0.0f;
//M3D_2PI 就是2Pi 的意思,就一個(gè)圓的意思。 繪制圓形
for (GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0) {
//數(shù)組下標(biāo)自增(每自增1次就表示一個(gè)頂點(diǎn))
nVerts++;
/*
弧長(zhǎng)=半徑*角度,這里的角度是弧度制,不是平時(shí)的角度制
既然知道了cos值,那么角度=arccos,求一個(gè)反三角函數(shù)就行了
*/
//x點(diǎn)坐標(biāo) = cos(angle) * 半徑
vPoints[nVerts][0] = float(cos(angle)) * r;
//y點(diǎn)坐標(biāo) = sin(angle) * 半徑
vPoints[nVerts][1] = float(sin(angle)) * r;
//z 點(diǎn)的坐標(biāo)
vPoints[nVerts][2] = -0.5f;
}
//結(jié)束扇形 前面一共繪制7個(gè)頂點(diǎn)(包括圓心)
printf("三角形扇形頂點(diǎn)數(shù):%d\n", nVerts);
//添加閉合的終點(diǎn)
nVerts++;
vPoints[nVerts][0] = r;
vPoints[nVerts][1] = 0.0f;
vPoints[nVerts][2] = 0.0f;
//加載!
//GL_TRIANGLE_FAN 以一個(gè)圓心為中心呈扇形排列,共用相鄰頂點(diǎn)的一組三角形
triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
triangleFanBatch.CopyVertexData3f(vPoints);
triangleFanBatch.End();
// 三角形條帶,一個(gè)小環(huán)或圓柱段
// 頂點(diǎn)下標(biāo)
int iCounter = 0;
//半徑
GLfloat radius = 3.0f;
//從0度~360度,以0.3弧度為步長(zhǎng)
for (GLfloat angle = 0.0f; angle <= (2.0*M3D_PI); angle += 0.3f) {
//獲取圓形的頂點(diǎn)x,y
GLfloat x = radius * sin(angle);
GLfloat y = radius * cos(angle);
// 繪制2個(gè)三角形(他們的x,y頂點(diǎn)一樣,只是z點(diǎn)不一樣)
vPoints[iCounter][0] = x;
vPoints[iCounter][1] = y;
vPoints[iCounter][2] = -0.5;
iCounter++;
vPoints[iCounter][0] = x;
vPoints[iCounter][1] = y;
vPoints[iCounter][2] = 0.5;
iCounter++;
}
//關(guān)閉循環(huán)
printf("三角形帶的頂點(diǎn)數(shù):%d\n", iCounter);
//結(jié)束循環(huán),在循環(huán)位置生成2個(gè)三角形
vPoints[iCounter][0] = vPoints[0][0];
vPoints[iCounter][1] = vPoints[0][1];
vPoints[iCounter][2] = -0.5;
iCounter++;
vPoints[iCounter][0] = vPoints[1][0];
vPoints[iCounter][1] = vPoints[1][1];
vPoints[iCounter][2] = 0.5;
iCounter++;
// GL_TRIANGLE_STRIP 共用一個(gè)條帶(strip)上的頂點(diǎn)的一組三角形
triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
triangleStripBatch.CopyVertexData3f(vPoints);
triangleStripBatch.End();
}
void DrawWireFramedBatch(GLBatch* pBatch)
{
/*------------畫綠色部分----------------*/
/* GLShaderManager 中的Uniform 值——平面著色器
參數(shù)1:平面著色器
參數(shù)2:運(yùn)行為幾何圖形變換指定一個(gè) 4 * 4變換矩陣
--transformPipeline 變換管線(指定了2個(gè)矩陣堆棧)
參數(shù)3:顏色值
*/
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
pBatch->Draw();
/*-----------邊框部分-------------------*/
/*
glEnable(GLenum mode); 用于啟用各種功能。功能由參數(shù)決定
參數(shù)列表:http://blog.csdn.net/augusdi/article/details/23747081
注意:glEnable() 不能寫在glBegin() 和 glEnd()中間
GL_POLYGON_OFFSET_LINE 根據(jù)函數(shù)glPolygonOffset的設(shè)置,啟用線的深度偏移
GL_LINE_SMOOTH 執(zhí)行后,過慮線點(diǎn)的鋸齒
GL_BLEND 啟用顏色混合。例如實(shí)現(xiàn)半透明效果
GL_DEPTH_TEST 啟用深度測(cè)試 根據(jù)坐標(biāo)的遠(yuǎn)近自動(dòng)隱藏被遮住的圖形(材料
glDisable(GLenum mode); 用于關(guān)閉指定的功能 功能由參數(shù)決定
*/
//畫黑色邊框
glPolygonOffset(-1.0f, -1.0f); // 偏移深度,在同一位置要繪制填充和邊線,會(huì)產(chǎn)生z沖突,所以要偏移
glEnable(GL_POLYGON_OFFSET_LINE);
// 畫反鋸齒。讓黑邊好看些
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//繪制線框幾何黑色版 三種模式,實(shí)心,邊框,點(diǎn),可以作用在正面,背面,或者兩面
//通過調(diào)用glPolygonMode將多邊形正面或者背面設(shè)為線框模式,實(shí)現(xiàn)線框渲染
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//設(shè)置線條寬度
glLineWidth(2.5f);
/* GLShaderManager 中的Uniform 值——平面著色器
參數(shù)1:平面著色器
參數(shù)2:運(yùn)行為幾何圖形變換指定一個(gè) 4 * 4變換矩陣
--transformPipeline.GetModelViewProjectionMatrix() 獲取的
GetMatrix函數(shù)就可以獲得矩陣堆棧頂部的值
參數(shù)3:顏色值(黑色)
*/
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
pBatch->Draw();
// 復(fù)原原本的設(shè)置
////通過調(diào)用glPolygonMode將多邊形正面或者背面設(shè)為全部填充模式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glLineWidth(1.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
// 召喚場(chǎng)景
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//壓棧
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
//矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果隨后簡(jiǎn)存儲(chǔ)在堆棧的頂部
modelViewMatrix.MultMatrix(mCamera);
M3DMatrix44f mObjectFrame;
//只要使用 GetMatrix 函數(shù)就可以獲取矩陣堆棧頂部的值,這個(gè)函數(shù)可以進(jìn)行2次重載。用來使用GLShaderManager 的使用?;蛘呤谦@取頂部矩陣的頂點(diǎn)副本數(shù)據(jù)
objectFrame.GetMatrix(mObjectFrame);
//矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果隨后簡(jiǎn)儲(chǔ)存在堆棧的頂部
modelViewMatrix.MultMatrix(mObjectFrame);
/* GLShaderManager 中的Uniform 值——平面著色器
參數(shù)1:平面著色器
參數(shù)2:運(yùn)行為幾何圖形變換指定一個(gè) 4 * 4變換矩陣
--transformPipeline.GetModelViewProjectionMatrix() 獲取的
GetMatrix函數(shù)就可以獲得矩陣堆棧頂部的值
參數(shù)3:顏色值(黑色)
*/
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
//根據(jù)nStep值來表示
switch (nStep) {
case 0:
//設(shè)置點(diǎn)的大小
glPointSize(4.0f);
pointBatch.Draw();
//畫完之后,避免影響到其他的設(shè)置,所以要設(shè)置恢復(fù)默認(rèn)值
glPointSize(1.0f);
break;
case 1:
//設(shè)置線的寬度
glLineWidth(2.0f);
lineBatch.Draw();
glLineWidth(1.0f);
break;
case 2: //線段
glLineWidth(2.0f);
lineStripBatch.Draw();
glLineWidth(1.0f);
break;
case 3: //線環(huán)
glLineWidth(2.0f);
lineLoopBatch.Draw();
glLineWidth(1.0f);
break;
case 4:
DrawWireFramedBatch(&triangleBatch);
break;
case 5:
DrawWireFramedBatch(&triangleStripBatch);
break;
case 6:
DrawWireFramedBatch(&triangleFanBatch);
break;
}
//還原到以前的模型視圖矩陣(單位矩陣)
modelViewMatrix.PopMatrix();
//進(jìn)行緩沖區(qū)交換
glutSwapBuffers();
}
//特殊鍵位處理(上、下、左、右移動(dòng))
void SpecialKeys(int key, int x, int y)
{
// 圍繞一個(gè)指定的X,Y,Z軸旋轉(zhuǎn)
/**
RotateWorld(float fAngle, float x, float y, float z)
x,y,z中的值,1表示YES,0表示NO來判斷指定哪個(gè)軸旋轉(zhuǎn)
*/
if (key == GLUT_KEY_UP) {
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
}
if (key == GLUT_KEY_DOWN) {
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
}
if (key == GLUT_KEY_LEFT) {
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
}
if (key == GLUT_KEY_RIGHT) {
objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
}
glutPostRedisplay();
}
//根據(jù)空格次數(shù)。切換不同的“窗口名稱”
void KeyPressFunc(unsigned char key, int x, int y)
{
//判斷key的次數(shù)
if (key == 32) {
nStep++;
if (nStep > 6) {
nStep = 0;
}
}
//switch 判斷顯示具體操作
switch (nStep) {
case 0:
glutSetWindowTitle("GL_POINTS");
break;
case 1:
glutSetWindowTitle("GL_LINES");
break;
case 2:
glutSetWindowTitle("GL_LINE_STRIP");
break;
case 3:
glutSetWindowTitle("GL_LINE_LOOP");
break;
case 4:
glutSetWindowTitle("GL_TRIANGLES");
break;
case 5:
glutSetWindowTitle("GL_TRIANGLE_STRIP");
break;
case 6:
glutSetWindowTitle("GL_TRIANGLE_FAN");
break;
}
//重新繪制
glutPostRedisplay();
}
// 窗口已更改大小,或剛剛創(chuàng)建。無論哪種情況,我們都需要
// 使用窗口維度設(shè)置視口和投影矩陣.
void ChangeSize(int w, int h)
{
// 窗口大小
glViewport(0, 0, w, h);
//創(chuàng)建投影矩陣,并將它載入投影矩陣堆棧中
//創(chuàng)建投影矩陣,并將它載入投影矩陣堆棧中
/**
@params fFov 角度
@params fAspect 縱橫比(寬高比)
@params fNear
@params fFar
*/
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0, 500.0f);
//投影矩陣
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//調(diào)用頂部載入單元矩陣:因?yàn)椴挥米鍪裁茨P妥兓?,所以用單元矩陣就行? modelViewMatrix.LoadIdentity();
}
int main(int argc, char* argv[])
{
////設(shè)置工作路徑
gltSetWorkingDirectory(argv[0]);
//初始化
glutInit(&argc, argv);
//申請(qǐng)一個(gè)顏色緩存區(qū)、深度緩存區(qū)、雙緩存區(qū)、模板緩存區(qū)
/**
GLUT_DOUBLE 雙緩存區(qū)
GLUT_RGBA 顏色緩存區(qū)
GLUT_DEPTH 深度緩存區(qū)
GLUT_STENCIL 模板緩存區(qū)
*/
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//設(shè)置window 的尺寸
glutInitWindowSize(800, 600);
//創(chuàng)建window的名稱
glutCreateWindow("GL_Point");
//注冊(cè)回調(diào)函數(shù)(改變尺寸)
glutReshapeFunc(ChangeSize);
//點(diǎn)擊空格時(shí),調(diào)用的函數(shù)
glutKeyboardFunc(KeyPressFunc);
//特殊鍵位函數(shù)(上下左右)
glutSpecialFunc(SpecialKeys);
//顯示函數(shù)
glutDisplayFunc(RenderScene);
//判斷一下是否能初始化glew庫,確保項(xiàng)目能正常使用OpenGL 框架
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
//繪制
SetupRC();
//runloop運(yùn)行循環(huán)
glutMainLoop();
return 0;
}








