個人
OpenGL
專題學習目錄,但願和你們一塊兒學習交流進步!編程
- OpenGL學習(一)-- 術語瞭解
- OpenGL學習(二)-- Xcode 搭建 OpenGL 環境
- OpenGL學習(三)-- OpenGL 基礎渲染
- OpenGL學習(四)-- 正面&背面剔除和深度測試
- OpenGL學習(五)-- 裁剪與混合
- OpenGL學習(六)-- 基礎紋理
- OpenGL學習(七)-- 基礎變化綜合練習實踐總結
- OpenGL學習(八)-- OpenGL ES 初探(上)
- OpenGL學習(九)-- OpenGL ES 初探(下)GLKit
- OpenGL學習(十)-- 着色語言 GLSL 語法介紹
- OpenGL學習(十一)-- 用 GLSL 實現加載圖片
- OpenGL學習(十二)-- OpenGL ES 紋理翻轉的策略對比
如下我總結了一些最近學習 OpenGL 中經常使用的一些函數,添加了比較多的註釋,既是對本身學習的一個鞏固總結,也是防止之後遺忘能夠快速查看的記錄,同時但願也能幫助到更多在學習 OpenGL 的朋友們。緩存
Xcode 尚未搭建OpenGL環境的朋友能夠參照我這篇文章去搭建,OpenGL學習(二)-- Xcode 搭建 OpenGL 環境。 另外還有以前的我總結的一些 OpenGL 基礎文章,能夠從文章上方的目錄查看。安全
#include "GLTools.h" // OpenGL toolkit
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"
#include "StopWatch.h"
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
複製代碼
GLShaderManager shaderManager;// 固定管線管理器
GLMatrixStack modelViewMatrix;// 模型視圖矩陣堆棧
GLMatrixStack projectionMatrix;// 投影視圖矩陣堆棧
GLFrame cameraFrame;// 觀察者位置
GLFrame objectFrame;// 世界座標位置
GLFrustum viewFrustum;// 投影方式,學名:視景體。用來構造投影矩陣。
GLBatch triangleBatch;// 簡單的批次容器,是GLTools的一個簡單的容器類。
GLTriangleBatch CC_Triangle;// 三角形批次類
GLTriangleBatch sphereBatch;// 球
GLTriangleBatch torusBatch;// 環
GLTriangleBatch cylinderBatch;// 圓柱
GLTriangleBatch coneBatch;// 錐
GLTriangleBatch diskBatch;// 磁盤
GLGeometryTransform transformPipeline; // 變換管道,專門用來管理投影和模型矩陣的
GLfloat vGreen[] = {0.0f, 1.0f, 0.0f, 1.0f };// 定義一個顏色值,綠色
複製代碼
int main(int argc,char *argv[]) {
//設置當前工做目錄,針對MAC OS X
/* `GLTools`函數`glSetWorkingDrectory`用來設置當前工做目錄。實際上在Windows中是沒必要要的,由於工做目錄默認就是與程序可執行執行程序相同的目錄。可是在Mac OS X中,這個程序將當前工做文件夾改成應用程序捆綁包中的`/Resource`文件夾。`GLUT`的優先設定自動進行了這個中設置,可是這樣中方法更加安全。 */
gltSetWorkingDirectory(argv[0]);
//初始化GLUT庫,這個函數只是傳輸命令參數而且初始化glut庫
glutInit(&argc, argv);
/* 初始化雙緩衝窗口,其中標誌GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分別指 雙緩衝窗口、RGBA顏色模式、深度測試、模板緩衝區 --GLUT_DOUBLE`:雙緩存窗口,是指繪圖命令其實是離屏緩存區執行的,而後迅速轉換成窗口視圖,這種方式,常常用來生成動畫效果; --GLUT_DEPTH`:標誌將一個深度緩存區分配爲顯示的一部分,所以咱們可以執行深度測試; --GLUT_STENCIL`:確保咱們也會有一個可用的模板緩存區。 深度、模板測試後面會細緻講到 */
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
//GLUT窗口大小、窗口標題
glutInitWindowSize(800, 600);
glutCreateWindow("Triangle");
/* GLUT 內部運行一個本地消息循環,攔截適當的消息。而後調用咱們不一樣時間註冊的回調函數。咱們一共註冊2個回調函數: 1)爲窗口改變大小而設置的一個回調函數 2)包含OpenGL 渲染的回調函數 */
//註冊重塑函數
glutReshapeFunc(changeSize);
//註冊顯示函數
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpeacialKeys);
/* 初始化一個GLEW庫,確保OpenGL API對程序徹底可用。 在試圖作任何渲染以前,要檢查肯定驅動程序的初始化過程當中沒有任何問題 */
GLenum status = glewInit();
if (GLEW_OK != status) {
printf("GLEW Error:%s\n",glewGetErrorString(status));
return 1;
}
//設置咱們的渲染環境
setupRC();
glutMainLoop();
return 0;
}
複製代碼
重塑函數,爲窗口改變大小而設置的一個回調函數,窗口大小改變時,接收新的寬度&高度。 在 main 函數中使用
glutReshapeFunc
函數進行了註冊:glutReshapeFunc(changeSize);//註冊重塑函數
函數
/* 在窗口大小改變時,接收新的寬度&高度。 */
void changeSize(int w,int h) {
/* 一、設置窗口座標 x,y 參數表明窗口中視圖的左下角座標,而寬度、高度是像素爲表示,一般x,y 都是爲0 */
// 防止h變爲0
if(h == 0){
h = 1;
}
glViewport(0, 0, w, h);
// 二、
// 若是繪製的是立體圖形,還須要設置透視投影
// 透視投影
// 參數1:從頂點方向看去的視場角度(用角度值表示)
// 參數2:寬和高的縱橫比
// 參數3:fNear
// 參數4:fFar
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
// 三、
// 上面只是設置了但沒有用,咱們須要把設置的轉化爲矩陣,把結果帶回去。
// 往投影矩陣堆棧projectionMatrix里加載一個矩陣,從咱們的視景體viewFrustum裏獲取投影矩陣GetProjectionMatrix()
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 四、
// 模型視圖矩陣是用來旋轉、平移等操做的,這裏並無變化,因此只是加載一個單元矩陣。
// 往模型視圖矩陣堆棧modelViewMatrix里加載一個單元矩陣
modelViewMatrix.LoadIdentity();
}
複製代碼
每次點擊空格,切換窗口的標題,並從新渲染圖形oop
//點擊空格,切換渲染圖形
void KeyPressFunc(unsigned char key, int x, int y) {
if(key == 32) {
nStep++;
if(nStep > 4)
nStep = 0;
}
switch(nStep) {
case 0:
glutSetWindowTitle("Sphere");
break;
case 1:
glutSetWindowTitle("Torus");
break;
case 2:
glutSetWindowTitle("Cylinder");
break;
case 3:
glutSetWindowTitle("Cone");
break;
case 4:
glutSetWindowTitle("Disk");
break;
}
glutPostRedisplay();
}
複製代碼
點擊鍵盤的上下左右,讓世界座標系發生變化(並非移動物體自己)post
//上下左右,移動世界座標系
void SpecialKeys(int key, int x, int y) {
if(key == GLUT_KEY_UP)
// 移動世界座標系,而不是去移動物體。
// 將世界座標系在X方向移動-5.0
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();
}
複製代碼
進行必要的初始化,設置咱們的渲染環境,在 main
函數中調用。通常在這裏是去描述咱們要渲染的圖形長什麼樣子。真正去繪製的時候不在這裏,而是在 RenderScene()
函數 中,後面會講解。學習
// 將上下文中,進行必要的初始化
void SetupRC() {
// 一、設置背景顏色
glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
// 二、初始化固定着色器管理器
shaderManager.InitializeStockShaders();
// 三、繪製立體圖形的時候,須要開啓深度測試
glEnable(GL_DEPTH_TEST);
// 四、經過GLGeometryTransform管理矩陣堆棧
// 使用transformPipeline 管道管理模型視圖矩陣堆棧 和 投影矩陣堆棧(把兩個矩陣做爲兩個參數放進去)
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
// 五、爲了讓效果明顯,將觀察者座標位置Z移動往屏幕裏移動15個單位位置
// 參數:表示離屏幕之間的距離。 負數,是往屏幕後面移動;正數,往屏幕前面移動
cameraFrame.MoveForward(-15.0f);
// ----------------------GLTriangleBatch類型--------------------------
// 利用「GLTriangleBatch」類型的三角形批次類構造圖形對象,GLTriangleBatch類型封裝了不少經常使用立體圖形。
// 一、球
/* gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks); 參數1:sphereBatch,三角形批次類對象 參數2:fRadius,球體半徑 參數3:iSlices,從球體底部堆疊到頂部的三角形帶的數量;其實球體是一圈一圈三角形帶組成 參數4:iStacks,圍繞球體一圈排列的三角形對數 建議:一個對稱性較好的球體的片斷數量是堆疊數量的2倍,就是iStacks = 2 * iSlices,參數4是參數3的2倍。 繪製球體都是圍繞Z軸,這樣+z就是球體的頂點,-z就是球體的底部。 */
gltMakeSphere(sphereBatch, 3.0, 10, 20);
// 二、環面
/* gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor); 參數1:torusBatch,三角形批次類對象 參數2:majorRadius,甜甜圈中心到外邊緣的半徑 參數3:minorRadius,甜甜圈中心到內邊緣的半徑 參數4:numMajor,沿着主半徑的三角形數量 參數5:numMinor,沿着內部較小半徑的三角形數量 */
gltMakeTorus(torusBatch, 3.0f, 0.75f, 15, 15);
// 三、圓柱
/* void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks); 參數1:cylinderBatch,三角形批次類對象 參數2:baseRadius,底部半徑 參數3:topRadius,頭部半徑 參數4:fLength,圓形長度 參數5:numSlices,圍繞Z軸的三角形對的數量 參數6:numStacks,圓柱底部堆疊到頂部圓環的三角形數量 */
gltMakeCylinder(cylinderBatch, 2.0f, 2.0f, 3.0f, 15, 2);
// 四、錐
/* void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks); 參數1:cylinderBatch,三角形批次類對象 參數2:baseRadius,底部半徑 參數3:topRadius,頭部半徑 參數4:fLength,圓形長度 參數5:numSlices,圍繞Z軸的三角形對的數量 參數6:numStacks,圓柱底部堆疊到頂部圓環的三角形數量 */
//圓柱體,從0開始向Z軸正方向延伸。
//圓錐體,是一端的半徑爲0,另外一端半徑可指定。
gltMakeCylinder(coneBatch, 2.0f, 0.0f, 3.0f, 13, 2);
// 五、磁盤
/* void gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks); 參數1:diskBatch,三角形批次類對象 參數2:innerRadius,內圓半徑 參數3:outerRadius,外圓半徑 參數4:nSlices,圓盤圍繞Z軸的三角形對的數量 參數5:nStacks,圓盤外網到內圍的三角形數量 */
gltMakeDisk(diskBatch, 1.5f, 3.0f, 13, 3);
// ------------------------GLBatch類型--------------------------
// 用上面GLTriangleBatch的代碼的時候,把下面這段代碼先註釋掉再運行。兩塊兒代碼原本是分開的,爲了方便寫在了一塊兒。
//上面的是用的「GLTriangleBatch」類型的三角形批次類,下面咱們使用最基礎的「GLBatch」類型的三角形批次類繪製金字塔。
// 首先建立三角形批次類,告訴它咱們須要多少個頂點,
// 1.函數BeginMesh( GLuint nMaxVerts )// 指定有多少個頂點
CC_Triangle.BeginMesh(300);// 指定有300個頂點
// 2.建立一個頂點
M3DVector3f m[] = {
0.5, 0.0, 0.0,
-0.5, 0.0, 0.0,
0.0, 0.5, 0.0
};
// 3.將頂點複製進去,可使用copy函數,也可使用add函數添加
// 參數1:表示頂點座標值
// 參數2:表示法線座標值,沒有就寫NULL
// 參數3:表示紋理座標值,沒有就寫NULL
CC_Triangle.AddTriangle(m, NULL, NULL);
//CC_Triangle.CopyVertexData3f
CC_Triangle.End();
CC_Triangle.Draw();
// --------------------------------------------------------------
}
複製代碼
在發生任何變化,調用 glutPostRedisplay()
方法時,會調用 RenderScene()
這個函數。 在 main
函數中註冊了 RenderScene
顯示函數 glutDisplayFunc(RenderScene);
測試
下面我先着重解釋一下這幾段代碼:動畫
- (1)壓棧 PushMatrix();
modelViewMatrix.PushMatrix();
這句代碼的意思是壓棧,若是 PushMatix() 括號裏是空的,就表明是把棧頂的矩陣複製一份,再壓棧到它的頂部。若是不是空的,好比是括號裏是單元矩陣,那麼就表明壓入一個單元矩陣到棧頂了。ui
- (2)矩陣相乘 MultMatrix(mObjectFrame)
將 modelViewMatrix 的堆棧中的矩陣 與 mOjbectFrame 矩陣相乘,存儲到 modelViewMatrix 矩陣堆棧中。
modelViewMatrix.MultMatrix(mObjectFrame);
這句代碼的意思是把 模型視圖矩陣堆棧 的 棧頂 的矩陣 copy 出一份來和新矩陣進行矩陣相乘,而後再將相乘的結果賦值給棧頂的矩陣。
- (3)出棧PopMatrix();
modelViewMatrix.PopMatrix();
把棧頂的矩陣出棧,恢復爲原始的矩陣堆棧,這樣就不會影響後續的操做了。下面是我本身總結的一個《矩陣入棧、相乘、出棧》的流程圖:
- (4)cameraFrame.GetCameraMatrix(mCamera) :
cameraFrame.GetCameraMatrix(mCamera);
這裏和 OC 的語法有些不同,它的意思是從 cameraFrame 這個 觀察者座標系 中獲取矩陣,而後賦值給 mCamera。同理的還有獲取 世界座標位置 的矩陣objectFrame.GetMatrix(mObjectFrame);
。 cameraFrame 和 mCamera 都是GLFrame
類型的結構體。
接下來看一下 RenderScene() 代碼:
//召喚場景
void RenderScene(void) {
//1.清除一個或者一組特定的緩存區
/* 緩衝區是一塊存在圖像信息的儲存空間,紅色、綠色、藍色和alpha份量一般一塊兒份量一般一塊兒做爲顏色緩存區或像素緩存區引用。 OpenGL 中不止一種緩衝區(顏色緩存區、深度緩存區和模板緩存區) 清除緩存區對數值進行預置 參數:指定將要清除的緩存的 GL_COLOR_BUFFER_BIT :指示當前激活的用來進行顏色寫入緩衝區 GL_DEPTH_BUFFER_BIT :指示深度緩存區 GL_STENCIL_BUFFER_BIT:指示模板緩衝區 */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//模型視圖矩陣棧堆,壓棧
modelViewMatrix.PushMatrix();
//獲取攝像頭矩陣
M3DMatrix44f mCamera;
//從camereaFrame中獲取矩陣到mCamera
cameraFrame.GetCameraMatrix(mCamera);
//模型視圖堆棧的 矩陣與mCamera矩陣 相乘以後,存儲到modelViewMatrix矩陣堆棧中
modelViewMatrix.MultMatrix(mCamera);
//建立矩陣mObjectFrame
M3DMatrix44f mObjectFrame;
//從ObjectFrame 獲取矩陣到mOjectFrame中
objectFrame.GetMatrix(mObjectFrame);
//將modelViewMatrix 的堆棧中的矩陣 與 mOjbectFrame 矩陣相乘,存儲到modelViewMatrix矩陣堆棧中
modelViewMatrix.MultMatrix(mObjectFrame);
//使用平面着色器
//參數1:類型
//參數2:經過transformPipeline獲取模型視圖矩陣
//參數3:顏色
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
//判斷你目前是繪製第幾個圖形
// 注意這裏傳遞的是地址 &sphereBatch
switch(nStep) {
case 0:
DrawWireFramedBatch(&sphereBatch);// 球
break;
case 1:
DrawWireFramedBatch(&torusBatch);// 環面
break;
case 2:
DrawWireFramedBatch(&cylinderBatch);// 圓柱
break;
case 3:
DrawWireFramedBatch(&coneBatch);// 錐
break;
case 4:
DrawWireFramedBatch(&diskBatch);// 磁盤
break;
}
// 繪製完畢了,須要把棧頂的矩陣pop出去,不要影響我下一次繪圖
modelViewMatrix.PopMatrix();
// Flush drawing commands
glutSwapBuffers();
}
複製代碼
void DrawWireFramedBatch(GLTriangleBatch* pBatch) {
// 平面着色器,繪製三角形
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
// 傳過來的參數,對應不一樣的圖形Batch
pBatch->Draw();
// 畫出黑色輪廓
glPolygonOffset(-1.0f, -1.0f);
// 開啓線段圓滑處理
glEnable(GL_LINE_SMOOTH);
// 開啓混合功能
glEnable(GL_BLEND);
// 顏色混合
// 表示源顏色乘以自身的alpha 值,目標顏色乘以1.0減去源顏色的alpha值,這樣一來,源顏色的alpha值越大,則產生的新顏色中源顏色所佔比例就越大,而目標顏色所佔比例則減 小。這種狀況下,咱們能夠簡單的將源顏色的alpha值理解爲「不透明度」。這也是混合時最經常使用的方式。
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 經過程序點大小模式來設置點的大小
glEnable(GL_POLYGON_OFFSET_LINE);
// 多邊形模型(背面、線) 將多邊形背面設爲線框模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// 線條寬度
glLineWidth(2.5f);
// 平面着色器繪製線條
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
pBatch->Draw();
// 恢復多邊形模式和深度測試
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glLineWidth(1.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
複製代碼
以上的總結參考了並部分摘抄瞭如下文章,很是感謝如下做者的分享!:
一、《OpenGL超級寶典 第5版》
二、《OpenGL編程指南(第八版)》
轉載請備註原文出處,不得用於商業傳播——凡幾多