前言:前一部分了解了OpenGL環(huán)境搭建和基本API之后袜香,我們先來做一個(gè)小小的練習(xí),使用固定管線來繪制一個(gè)可移動(dòng)的三角形鲫惶,同時(shí)詳細(xì)解釋一下一些常用方法的含義
一蜈首、搭建OpenGL在Mac下的環(huán)境
搭建Xcode環(huán)境,這里不再做過多贅述了
傳送門:http://www.reibang.com/p/05555148d567
二欠母、實(shí)戰(zhàn)開始
(注:完整代碼在文章的最后面)
1欢策、首先來到main函數(shù)中進(jìn)行環(huán)境初始化和一些函數(shù)的注冊(cè)
int main(int argc,char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
glutInitWindowSize(800,600);
glutCreateWindow("Triangle");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
GLenum err = glewInit();
if(GLEW_OK != err) {
fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
下面來一一分析對(duì)應(yīng)函數(shù)和方法的意思,
gltSetWorkingDirectory(argv[0]) : 是”GLTools“用來設(shè)置當(dāng)前工作目錄的函數(shù)赏淌,實(shí)際上在windows中是不必要的踩寇,因?yàn)楣ぷ髂夸浤J(rèn)就是與程序的可執(zhí)行程序的目錄相同。但是在Mac OSX環(huán)境中六水,這個(gè)程序?qū)?dāng)前工作文件夾改為應(yīng)用程序捆綁包中的"/Resource"文件夾.'GLUT'的優(yōu)先設(shè)定會(huì)自動(dòng)設(shè)定為這個(gè)俺孙,這樣寫也是為了更加安全。
glutInit(&argc, argv): 初始化GLUT庫掷贾,這個(gè)函數(shù)只是傳入命令參數(shù)并且初始化glut庫
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL): 設(shè)置顯示模式睛榄。其中GLUT_DOUBLE 指的是初始化雙緩沖窗口顯示模式,GLUT_RGBA RGBA顏色顯示模式想帅,GLUT_DEPTH 初始化深度測試顯示模式场靴,可以用于開啟深度測試,GLUT_STENCIL 初始化模板緩沖區(qū)
glutInitWindowSize(800,600) 初始化一個(gè)GLUT窗口并設(shè)置窗口大小
glutCreateWindow("Triangle") 設(shè)置窗口的標(biāo)題
glutReshapeFunc(ChangeSize) 注冊(cè)綁定重塑函數(shù)港准,當(dāng)窗口第一次創(chuàng)建或者窗口大小發(fā)生變化憎乙,需要界面重繪時(shí),系統(tǒng)會(huì)自動(dòng)調(diào)用這個(gè)已經(jīng)注冊(cè)過的自定義函數(shù)ChangeSize
glutDisplayFunc(RenderScene) 注冊(cè)綁定顯示函數(shù)叉趣,當(dāng)屏幕發(fā)生渲染或者你使用代碼強(qiáng)制渲染泞边,需要界面重繪的時(shí)候,系統(tǒng)會(huì)自動(dòng)調(diào)用這個(gè)已經(jīng)注冊(cè)綁定過的自定義函數(shù)RenderSize
glutSpecialFunc(SpecialKeys); 當(dāng)你使用特殊鍵位的時(shí)候比如鍵盤的上下左右鍵的時(shí)候疗杉,就會(huì)走到這個(gè)方法里阵谚,在這個(gè)函數(shù)中做特殊鍵位區(qū)分的時(shí)候,這些特殊鍵位都有對(duì)應(yīng)的枚舉值比如上(GLUT_KEY_UP) 下(GLUT_KEY_DOWN)左(GLUT_KEY_LEFT)右(GLUT_KEY_RIGHT)烟具,我找了一些梢什,這個(gè)簡單了解一下就行了
#define GLUT_KEY_F1 1
#define GLUT_KEY_F2 2
#define GLUT_KEY_F3 3
#define GLUT_KEY_F4 4
#define GLUT_KEY_F5 5
#define GLUT_KEY_F6 6
#define GLUT_KEY_F7 7
#define GLUT_KEY_F8 8
#define GLUT_KEY_F9 9
#define GLUT_KEY_F10 10
#define GLUT_KEY_F11 11
#define GLUT_KEY_F12 12
/* directional keys */
#define GLUT_KEY_LEFT 100
#define GLUT_KEY_UP 101
#define GLUT_KEY_RIGHT 102
#define GLUT_KEY_DOWN 103
#define GLUT_KEY_PAGE_UP 104
#define GLUT_KEY_PAGE_DOWN 105
#define GLUT_KEY_HOME 106
#define GLUT_KEY_END 107
#define GLUT_KEY_INSERT 108
GLenum err = glewInit(); 初始化一個(gè)GLEW庫,確保OpenGL API對(duì)程序完全可用朝聋,在試圖做任何渲染之前嗡午,要檢查確定驅(qū)動(dòng)程序的初始化過程中沒有任何問題
SetupRC() 自定方法,設(shè)置我們的渲染環(huán)境
glutMainLoop() 運(yùn)行循環(huán)冀痕,類似OC的Runloop荔睹,函數(shù)在調(diào)用之后狸演,在主窗口被關(guān)閉之前都不會(huì)返回,一個(gè)應(yīng)用程序只需要調(diào)用一次僻他,這個(gè)函數(shù)負(fù)責(zé)處理我們所有的消息宵距,直到我們關(guān)閉程序?yàn)橹埂?/p>
2、接著引入固定管線著色器和OpenGL一些基本庫文件
#include "GLShaderManager.h"
#include "GLTools.h"
#include <glut/glut.h>
GLShaderManager.h 這個(gè)移入了GLTool著色器管理器類shader manager吨拗,沒有著色器满哪,我們就不能再OpenGL核心框架進(jìn)行著色,著色器管理器不僅允許我們創(chuàng)建并管理著色器劝篷,還提供了一組”存儲(chǔ)著色器“哨鸭,他們能夠進(jìn)行一些基本的渲染操作。
GLTools.h GLTool.h頭文件包含了大部分GLTool中類似C語言的獨(dú)立函數(shù)
GLUT/GLUT.h 在Mac 系統(tǒng)下娇妓,需要#include <glut/glut.h> 兔跌; 在windows和Linux上板熊,我們使用freeglut的靜態(tài)庫版本并需要添加一個(gè)宏
3它呀、重塑函數(shù)的實(shí)現(xiàn),在窗口大小改變的時(shí)候项钮,接受新的寬度和高度
void changeSize(int w,int h)
{
glViewport(0, 0, w, h);
}
glViewPort這個(gè)函數(shù)就是設(shè)置視口,x,y 參數(shù)代表窗口中視圖的左下角坐標(biāo)蕊蝗,而寬度、高度是像素為表示赖舟,通常x,y 都是為0蓬戚,調(diào)用glViewPort會(huì)調(diào)用重新渲染RenderScene
4、接著定義一個(gè)著色器管理變量和一個(gè)批次類宾抓,GLBatch是GLTools的一個(gè)簡單的容器類
GLBatch triangleBatch;
GLShaderManager shaderManager;
5子漩、接下來,我們需要設(shè)置渲染環(huán)境和頂點(diǎn)數(shù)據(jù)
void SetupRC()
{
glClearColor(0.0f,0.0f,1.0f,1.0f);
shaderManager.InitializeStockShaders();
GLfloat vVerts[] = {
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,1.0f,0.0f,
};
triangleBatch.Begin(GL_TRIANGLES,3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
}
glClearColor 設(shè)置清屏顏色石洗,設(shè)置到顏色緩沖區(qū)里面幢泼,是一個(gè)狀態(tài)基,可以理解為窗口的背景色
shaderManager.InitializeStockShaders() 初始化一個(gè)著色器管理器
GLfloat vVerts[] = {
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,1.0f,0.0f,
};
定義一個(gè)一維數(shù)組讲衫,里面存儲(chǔ)三角形的三個(gè)頂點(diǎn)的坐標(biāo)缕棵,每個(gè)頂點(diǎn)有三個(gè)數(shù)(x,y涉兽,z)
其中
triangleBatch.Begin(GL_TRIANGLES,3); 第一個(gè)參數(shù)GL_TRIANGLES指的是選擇三角形的連接方式招驴,第二個(gè)參數(shù)3代表3個(gè)頂點(diǎn)
triangleBatch.CopyVertexData3f(vVerts);把頂點(diǎn)數(shù)據(jù)copy進(jìn)去,然后triangleBatch.End();表示設(shè)置完成
這里拓展一下便于理解枷畏,如果是畫四邊形SetUpRC中的頂點(diǎn)坐標(biāo)需要修改成
如果是要繪制四邊形别厘,需要修改頂點(diǎn)數(shù)組,然后修改連接方式如下
void SetupRC()
{
glClearColor(0.0f,0.0f,1.0f,1.0f);
shaderManager.InitializeStockShaders();
GLfloat vVerts[] = {
-0.5f,-0.5f,0.0f,
-0.5f,0.5f,0.0f,
0.5f,0.5f,0.0f,
0.5f,-0.5f,0.0f,
};
triangleBatch.Begin(GL_TRIANGLE_FAN,4);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
}
對(duì)比一下拥诡,頂點(diǎn)數(shù)組變了触趴,然后連接方式由GL_TRIANGLES變?yōu)镚L_TRIANGLE_FAN氮发,頂點(diǎn)個(gè)數(shù)由3個(gè)變?yōu)?個(gè)
頂點(diǎn)數(shù)組的變化很明顯,就不細(xì)說了
連接方式和頂點(diǎn)個(gè)數(shù)變了
//三角形的
triangleBatch.Begin(GL_TRIANGLES,3);
//四邊形的
triangleBatch.Begin(GL_TRIANGLE_FAN,4);
這里再拓展一下雕蔽,OpenGL圖元只有點(diǎn)折柠、線、三角形批狐,現(xiàn)在畫點(diǎn)扇售,線以及多邊形都沒問題了,圓形就需要通過這三個(gè)做處理一下了
6嚣艇、接下來我們需要設(shè)置渲染
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
triangleBatch.Draw();
glutSwapBuffers();
}
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT) 每次渲染前需要清除特定緩沖區(qū)比如深度緩沖區(qū)承冰,顏色緩沖區(qū)
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed) 不同的著色器,參數(shù)都是不一樣的食零,但是函數(shù)名都是這個(gè)困乒,繪制簡單的三角形,用單元著色器就行了贰谣,所以選GLT_SHADER_IDENTITY娜搂,并設(shè)置繪制顏色
triangleBatch.Draw() 開始顏色渲染
glutSwapBuffers() 交換緩沖區(qū),把渲染的內(nèi)容提交上去
執(zhí)行一下吱抚,就能看到藍(lán)色背景的windows下百宇,有一個(gè)紅色的三角形
7、開始設(shè)置移動(dòng)秘豹,設(shè)置移動(dòng)之前需要把頂點(diǎn)坐標(biāo)設(shè)置為全局變量携御,這樣好處理,把下面頂點(diǎn)數(shù)組放到全局
GLfloat vVerts[] = {
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,1.0f,0.0f,
};
把這段坐標(biāo)換成下面這段既绕,然后把邊長減小點(diǎn)啄刹,便于查看效果
//blockSize 邊長
GLfloat blockSize = 0.1f;
//三角形的3個(gè)點(diǎn)坐標(biāo)
GLfloat vVerts[] = {
-blockSize,0.0f,0.0f,
blockSize,0.0f,0.0f,
0.0f,blockSize,0.0f,
};
然后開始實(shí)現(xiàn)SpecialKeys函數(shù),這里拓展一下凄贩,頂點(diǎn)較少可以使用更新頂點(diǎn)坐標(biāo)這種移動(dòng)方式誓军,頂點(diǎn)較多的復(fù)雜圖形,可以使用矩陣進(jìn)行移動(dòng)
void SpecialKeys (int key , int x, int y){
GLfloat stepSize = 0.1f;
GLfloat blockX = vVerts[0];
GLfloat blockY = vVerts[1];
if (key == GLUT_KEY_UP) blockY += stepSize;
if (key == GLUT_KEY_DOWN) blockY -= stepSize;
if (key == GLUT_KEY_LEFT) blockX -= stepSize;
if (key == GLUT_KEY_RIGHT) blockX += stepSize;
//更新點(diǎn)的坐標(biāo) ABC
vVerts[0] = blockX;
vVerts[1] = blockY;
vVerts[3] = blockX + 2 * stepSize;
vVerts[4] = blockY;
vVerts[6] = blockX + stepSize;
vVerts[7] = blockY + stepSize;
triangleBatch.CopyVertexData3f(vVerts);
glutPostRedisplay();
}
其中設(shè)置blockX和blockY只是找了第一個(gè)點(diǎn)作為移動(dòng)坐標(biāo)參照物疲扎,然后相對(duì)第一個(gè)點(diǎn)的坐標(biāo)換算出剩余兩個(gè)點(diǎn)的坐標(biāo)谭企,比如A(-0.1,0评肆,0) B(0.1债查,0,0)瓜挽,換算成A(blockX盹廷,0,0)那此時(shí)的B就是(block + 2 * 0.1久橙,0, 0)俄占, vVerts[0]這里面的下標(biāo)0管怠,代表一維數(shù)組中的第一個(gè)點(diǎn)的x坐標(biāo),只上下左右移動(dòng)缸榄,只需要改三個(gè)點(diǎn)對(duì)應(yīng)的x渤弛,y坐標(biāo)就行了, 三個(gè)點(diǎn)想x和y的坐標(biāo)對(duì)應(yīng)的下標(biāo)分別是0甚带,1和3她肯,4和6,7鹰贵,這樣思路就很清楚了晴氨。
triangleBatch.CopyVertexData3f(vVerts); 這個(gè)將頂點(diǎn)數(shù)組通過GLBatch幫助類,將頂點(diǎn)數(shù)據(jù)傳輸?shù)酱鎯?chǔ)著色器中
glutPostRedisplay()這個(gè)是手動(dòng)觸發(fā)渲染函數(shù)碉输。
然后直接運(yùn)行籽前,就能夠得到一個(gè)可以用鍵盤控制上下左右移動(dòng)的三角形。
完整代碼如下
//
// main.cpp
//
// Created by battleMage on 2019/8/11.
// Copyright ? 2019 battleMage. All rights reserved.
//
#include "GLShaderManager.h"
#include "GLTools.h"
#include <glut/glut.h>
GLBatch triangleBatch;
GLShaderManager shaderManager;
//blockSize 邊長
GLfloat blockSize = 0.1f;
//三角形的3個(gè)點(diǎn)坐標(biāo)
GLfloat vVerts[] = {
-blockSize,0.0f,0.0f,
blockSize,0.0f,0.0f,
0.0f, blockSize,0.0f,
};
void ChangeSize(int w,int h){
glViewport(0,0, w, h);
}
void SpecialKeys (int key , int x, int y){
GLfloat stepSize = 0.1f;
GLfloat blockX = vVerts[0];
GLfloat blockY = vVerts[1];
if (key == GLUT_KEY_UP) blockY += stepSize;
if (key == GLUT_KEY_DOWN) blockY -= stepSize;
if (key == GLUT_KEY_LEFT) blockX -= stepSize;
if (key == GLUT_KEY_RIGHT) blockX += stepSize;
//更新點(diǎn)的坐標(biāo) ABC
vVerts[0] = blockX;
vVerts[1] = blockY;
vVerts[3] = blockX + 2 * stepSize;
vVerts[4] = blockY;
vVerts[6] = blockX + stepSize;
vVerts[7] = blockY + stepSize;
triangleBatch.CopyVertexData3f(vVerts);
glutPostRedisplay();
}
void SetupRC(){
glClearColor(0.0f,0.0f,1.0f,1.0f);
shaderManager.InitializeStockShaders();
triangleBatch.Begin(GL_TRIANGLES,3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
}
void RenderScene(void) {
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
triangleBatch.Draw();
glutSwapBuffers();
}
int main(int argc,char* argv[]){
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
glutInitWindowSize(800,600);
glutCreateWindow("Triangle");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
GLenum err = glewInit();
if(GLEW_OK != err) {
fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
效果如下: