了解了OpenGL的相關(guān)概念,我們?cè)偻ㄟ^兩個(gè)小示例來看看OpenGL的相關(guān)API以及圖形的繪制流程。
在實(shí)現(xiàn)示例之前,我們需要配置相關(guān)的環(huán)境:
- 添加
OpenGl.framework和GLUT.framework系統(tǒng)庫,添加libGLTools.a靜態(tài)庫, - 引入
CLTools、glew,并且在Build Settings的Header Search Paths中添加CLTools的路徑, - 移除原來的
main、AppDelegate、ViewController文件,創(chuàng)建一個(gè)main.cpp文件。
這樣,我們就可以在main.cpp實(shí)現(xiàn)相關(guān)功能了。
三角形
引入工具類
#include "GLTools.h"
#include <GLUT/GLUT.h>
#include "GLShaderManager.h"
-
GLTools.h:包含了?部分GLTools中類似C語言的獨(dú)立函數(shù) -
GLUT/GLUT.h:使用glut -
GLShaderManager.h:著?器管理器,允許我們使用并管理著色器,另外還提供了一組存儲(chǔ)著色器,能夠進(jìn)行一些基本的渲染操作
創(chuàng)建全局變量
著色管理器和GL批處理類:
GLShaderManager shaderManager;
GLBatch glBatch;
代碼實(shí)現(xiàn)
- 初始化、基礎(chǔ)設(shè)置操作
- 注冊(cè)窗口調(diào)整和渲染的回調(diào)函數(shù)
- 配置三角形的頂點(diǎn)數(shù)據(jù)
int main(int argc,char* argv[]) {
// 設(shè)置當(dāng)前工作目錄,針對(duì)MAC OS X
gltSetWorkingDirectory(argv[0]);
// 初始化GLUT庫
glutInit(&argc, argv);
// 初始化雙緩沖窗口
// GLUT_DOUBLE表示雙緩沖窗口、GLUT_RGBA表示RGBA顏色模式、GLUT_DEPTH表示深度測(cè)試、GLUT_STENCIL表示模板緩沖區(qū)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
// 設(shè)置GLUT窗口大小、標(biāo)題
glutInitWindowSize(800, 800);
glutCreateWindow("Triangle");
// 注冊(cè)回調(diào)函數(shù)
// 窗口調(diào)整
glutReshapeFunc(reshapeAction);
// 渲染操作
glutDisplayFunc(renderAction);
// 驅(qū)動(dòng)程序的初始化中容錯(cuò)判斷
GLenum error = glewInit();
if(GLEW_OK != error) {
fprintf(stderr,"glew error:%s\n",glewGetErrorString(error));
return 1;
}
// 初始化設(shè)置
setupTriangle();
glutMainLoop();
return 0;
}
void setupTriangle() {
// 設(shè)置窗口背景顏色
glClearColor(1, 1, 1, 1);
// 初始化著色管理器
shaderManager.InitializeStockShaders();
// 設(shè)置三角形的頂點(diǎn)坐標(biāo) x y z
GLfloat vertCoordinates[] = {
-0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f,
};
// 批次處理
glBatch.Begin(GL_TRIANGLES, 3);
glBatch.CopyVertexData3f(vertCoordinates);
glBatch.End();
}
// 窗口調(diào)整
void reshapeAction(int width, int height) {
// glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
// x,y 以像素為單位,指定了窗口的左下角位置。width,height表示視口矩形的寬度和高度,根據(jù)窗口的實(shí)時(shí)變化重繪窗口。
glViewport(0, 0, width, height);
}
// 渲染
void renderAction() {
// 清除一個(gè)或一組特定的緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// 設(shè)置顏色
GLfloat vRed[] = {1.0, 0.0, 0.0, 1.0};
// 著色管理器使用存儲(chǔ)著色器進(jìn)行渲染
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
glBatch.Draw();
// 雙緩沖處理,交換緩沖區(qū)指針
glutSwapBuffers();
}
固定管線存儲(chǔ)著色器
-
GLT_SHADER_IDENTITY:?jiǎn)挝恢?單元著色器
使用默認(rèn)笛卡爾坐標(biāo)系,坐標(biāo)范圍為(-1.0,1.0),所有片段都應(yīng)用同一種顏色。使用方法:
shaderManager.UserStockShader(GLT_SHADER_IDENTITY, GLfloat vColor[4]);
- 參數(shù)1:著色器類型
- 參數(shù)2:顏色參數(shù)
-
GLT_SHADER_FLAT:平面著色器
用以模型/投影變化。可以為幾何圖形變化指定一個(gè)4*4變換矩陣,該矩陣被稱為“模型視圖投影矩陣”。使用方法:
shaderManager.UserStockShader(GLT_SHADER_FLAT,GLfloat mvp[16],GLfloat vColor[4]);
- 參數(shù)1:著色器類型
- 參數(shù)2:4*4變換矩陣
- 參數(shù)3:顏色參數(shù)
-
GLT_SHADER_SHADED:上色著色器
用以將顏色平滑的插入到頂點(diǎn)之間,進(jìn)行平滑著色。使用方法:
shaderManager.UserStockShader(GLT_SHADER_SHADED,GLfloat vColor[4]);
- 參數(shù)1:著色器類型
- 參數(shù)2:顏色參數(shù)
-
GLT_SHADER_DEFAULT_LIGHT:默認(rèn)光源著色器
用來為圖形產(chǎn)生陰影和光照效果。使用方法:
shaderManager.UserStockShader(GLT_SHADER_DEFAULT_LIGHT,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vColor[4]);
- 參數(shù)1:著色器類型
- 參數(shù)2:模型視圖矩陣
- 參數(shù)3:投影矩陣
- 參數(shù)4:顏色參數(shù)
-
GLT_SHADER_POINT_LIGHT_DIFF:點(diǎn)光源著色器
用來為圖形產(chǎn)生陰影和光照效果。與默認(rèn)光源著色器非常類似,區(qū)別在與點(diǎn)光源著色器可以指定光源位置。使用方法:
shaderManager.UserStockShader(GLT_SHADER_DEFAULT_LIGHT,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vColor[4]);
- 參數(shù)1:著色器類型
- 參數(shù)2:模型視圖矩陣
- 參數(shù)3:投影矩陣
- 參數(shù)4:視點(diǎn)坐標(biāo)光源位置
- 參數(shù)5:顏色參數(shù)
-
GLT_SHADER_TEXTURE_REPLACE:紋理替換矩陣著色器
通過給定的模型視圖投影矩陣,使用紋理單元來進(jìn)行填充,其每個(gè)像素點(diǎn)的顏色是從紋理中獲取。使用方法:
shaderManager.UserStockShader(GLT_SHADER_TEXTURE_REPLACE,GLfloat mvMatrix[16],GLint nTextureUnit);
- 參數(shù)1:著色器類型
- 參數(shù)2:模型視圖矩陣
- 參數(shù)3:紋理單元
-
GLT_SHADER_TEXTURE_MODULATE:紋理調(diào)整著色器
通過給定的模型視圖投影矩陣,將一個(gè)基本色乘以一個(gè)取自紋理單元nTextureUnit的紋理,將顏色與紋理進(jìn)行混合后填充到片段中。使用方法:
shaderManager.UserStockShader(GLT_SHADER_TEXTURE_MODULATE,GLfloat mvMatrix[16],GLfloat vColor[4],GLint nTextureUnit);
- 參數(shù)1:著色器類型
- 參數(shù)2:模型視圖矩陣
- 參數(shù)3:顏色參數(shù)
- 參數(shù)4:紋理單元
-
GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF:紋理光源著色器
通過給定的模型視圖投影矩陣,將一個(gè)紋理通過漫反射照明計(jì)算進(jìn)行調(diào)整(相乘)。使用方法:
shaderManager.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:需要處理的紋理單元
設(shè)置初始顯示模式
| 值 | 對(duì)應(yīng)宏定義 | 含義 |
|---|---|---|
| GLUT_RGB | 0x0000 | 指定 RGB 顏色模式的窗口 |
| GLUT_RGBA | 0x0000 | 指定 RGBA 顏色模式的窗口 |
| GLUT_INDEX | 0x0001 | 指定顏色索引模式的窗口 |
| GLUT_SINGLE | 0x0000 | 指定單緩存窗口 |
| GLUT_DOUBLE | 0x0002 | 指定雙緩存窗口 |
| GLUT_ACCUM | 0x0004 | 窗口使用累加緩存 |
| GLUT_ALPHA | 0x0008 | 窗口的顏色分量包含 alpha 值 |
| GLUT_DEPTH | 0x0010 | 窗口使用深度緩存 |
| GLUT_STENCIL | 0x0020 | 窗口使用模板緩存 |
| GLUT_MULTISAMPLE | 0x0080 | 指定支持多樣本功能的窗口 |
| GLUT_STEREO | 0x0100 | 指定立體窗口 |
| GLUT_LUMINANCE | 0x0200 | 窗口使用亮度顏色模型 |
幾何圖元
-
GL_POINTS:點(diǎn) -
GL_LINES:線段,二個(gè)點(diǎn)確定線段 -
GL_LINE_STRIP:第一個(gè)點(diǎn)依次連接的線段 -
GL_LINE_LOOP:和GL_LINE_STRIP相同,但首尾連接,形成環(huán)狀 -
GL_POLYGON:多邊形 -
GL_QUADS:由四個(gè)點(diǎn)組成一個(gè)四邊形 -
GL_QUADS_STRIP:四邊形帶 -
GL_TRIANGLES:三角形,三個(gè)點(diǎn)確定 -
GL_TRIANGLE_STRIP:共用一個(gè)條帶上的頂點(diǎn)的一組三角形 -
GL_TRIANGLE_FAN:以一個(gè)原點(diǎn)為中心呈扇形排列,公共相鄰頂點(diǎn)的一組三角形

可移動(dòng)的正方形
渲染矩形和渲染三角形在初始化設(shè)置上基本一樣。實(shí)現(xiàn)矩形可以通過修改幾何圖元的類型即可,下面我們提供兩種思路,一種是使用批處理類渲染三角形的方式(GL_TRIANGLES),將兩個(gè)三角形拼接成矩形,這樣需要6個(gè)頂點(diǎn);另一種是直接使用批處理類渲染矩形的方式(GL_QUADS或者GL_TRIANGLE_FAN),需要4個(gè)頂點(diǎn)。
GLfloat squareEdge = 0.1;
// 給定初始坐標(biāo)
GLfloat vVerts[] = {
-squareEdge, -squareEdge, 0.0,
squareEdge, -squareEdge, 0.0,
squareEdge, squareEdge, 0.0,
-squareEdge, squareEdge, 0.0
};
// 偏移量
GLfloat xPos = 0.0;
GLfloat yPos = 0.0;
void setupSquare() {
glClearColor(1, 1, 1, 1);
// 初始化著色管理器
shaderManager.InitializeStockShaders();
// 批次類處理設(shè)置為矩形
glBatch.Begin(GL_TRIANGLE_FAN, 4);
glBatch.CopyVertexData3f(vVerts);
glBatch.End();
}
由于我們需要通過鍵盤控制所繪制矩形的位置,所以我們需要監(jiān)聽鍵盤的輸入,并且修改矩形的位置重新渲染。
// 注冊(cè)鍵盤的回調(diào)
glutSpecialFunc(squareSpecialKey);
此處,我們使用鍵盤的上下左右鍵來移動(dòng)矩形。這里需要做個(gè)特殊處理,當(dāng)矩形移動(dòng)到窗口的邊界時(shí),就需要停止繼續(xù)移動(dòng)。注意,這里每次只響應(yīng)一個(gè)鍵。
void squareSpecialKey(int key, int x, int y) {
// 每次移動(dòng)的步長(zhǎng)(距離)
GLfloat stepSize = 0.025;
// 只響應(yīng)上下左右鍵
if (key == GLUT_KEY_UP || key == GLUT_KEY_DOWN || key == GLUT_KEY_LEFT || key == GLUT_KEY_RIGHT) {
switch (key) {
case GLUT_KEY_UP:
yPos += stepSize;
break;
case GLUT_KEY_DOWN:
yPos -= stepSize;
break;
case GLUT_KEY_LEFT:
xPos -= stepSize;
break;
case GLUT_KEY_RIGHT:
xPos += stepSize;
break;
default:
break;
}
// 邊界檢測(cè)
// -x
if (xPos < -1.0 + squareEdge) {
xPos = -1.0 + squareEdge;
}
// x
if (xPos > 1.0 - squareEdge) {
xPos = 1.0 - squareEdge;
}
// -y
if (yPos < -1.0 + squareEdge) {
yPos = -1.0 + squareEdge;
}
// y
if (yPos > 1.0 - squareEdge) {
yPos = 1.0 - squareEdge;
}
glutPostRedisplay();
}
}
下面就是渲染過程了,考慮到每次移動(dòng)都需要修改矩陣的坐標(biāo),我們可以使用平面著色器,將新的坐標(biāo)矩陣通過變換之后,直接傳給著色器管理器即可。
void squareRender() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// 設(shè)置顏色
GLfloat vRed[] = {1.0, 0.0, 0.0, 1.0};
// 矩陣變化 對(duì)原來矩形的頂點(diǎn)數(shù)組進(jìn)行變換 將變換之后的結(jié)果存入mTransfromMatrix
M3DMatrix44f mTransfromMatrix;
m3dTranslationMatrix44(mTransfromMatrix, xPos, yPos, 0.0);
shaderManager.UseStockShader(GLT_SHADER_FLAT, mTransfromMatrix, vRed);
glBatch.Draw();
glutSwapBuffers();
}
圓
渲染一個(gè)圓形,我們可以使用幾何圖元GL_POLYGON,也就是從圓周上選定無限多個(gè)點(diǎn)作為頂點(diǎn),把這些頂點(diǎn)連接起來,即可組成一個(gè)圓。下面示例中,我們選定了10000個(gè)頂點(diǎn)。
int n = 10000;
GLfloat PI = 3.1415926;
// 半徑
GLfloat r = 0.5;
void setupCircle() {
// 設(shè)置窗口背景顏色
glClearColor(1, 1, 1, 1);
// 初始化著色管理器
shaderManager.InitializeStockShaders();
GLfloat vVerts[30000] = {0};
// 計(jì)算頂點(diǎn)
for (int i = 0; i < n; i++) {
vVerts[i*3] = r * cos(2 * PI * i / n);
vVerts[i*3+1] = r * sin(2 * PI * i / n);
vVerts[i*3+2] = 0;
}
// 批次處理
glBatch.Begin(GL_POLYGON, n);
glBatch.CopyVertexData3f(vVerts);
glBatch.End();
}