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