本文為L_Ares個人寫作译荞,包括圖片皆為個人親自操作瓤的,以任何形式轉(zhuǎn)載請表明原文出處。
OpenGL的一些庫中是有一些自帶圖形的吞歼,用于一些簡單的圖形繪制是很好用的圈膏,另外本節(jié)代碼也會為下一節(jié)做一下鋪墊。
直接上代碼篙骡,代碼中都有非常詳細的注釋稽坤,而且都是經(jīng)過測試可以跑起來的丈甸。
//
// main.cpp
// 07矩陣堆棧
//
// Created by EasonLi on 2020/8/29.
// Copyright ? 2020 EasonLi. All rights reserved.
//
#include <stdio.h>
#pragma mark - 引用類
//著色器管理器。負責(zé)管理和創(chuàng)建著色器管理器尿褪。里面有一些固定的著色器睦擂,可用于初級操作
#include "GLShaderManager.h"
//GLTool工具類,里面包含了GLTool大多數(shù)類似C語言的函數(shù)
#include "GLTools.h"
//矩陣堆棧類茫多。包含了矩陣堆棧的操作
#include "GLMatrixStack.h"
//管道類,用于更加方便的管理矩陣
#include "GLGeometryTransform.h"
//用于設(shè)置觀察者或者物體的位置
#include "GLFrame.h"
//投影矩陣獲取
#include "GLFrustum.h"
//三角形批次類
#include "GLBatch.h"
//3d數(shù)學(xué)類
#include "math3d.h"
//宏定義判斷系統(tǒng)忽刽,引入GLUT
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#pragma mark - 公共變量
//著色器管理器
GLShaderManager shaderManager;
//幾何變換管道
GLGeometryTransform transformPipeline;
//模型視圖矩陣
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
//觀察者(相機)位置
GLFrame cameraFrame;
//物體世界坐標(biāo)位置
GLFrame objectFrame;
//視景體天揖,用來構(gòu)造投影矩陣
GLFrustum viewFrustum;
//三角形批次類
//球
GLTriangleBatch sphereBatch;
//環(huán)
GLTriangleBatch torusBatch;
//圓柱
GLTriangleBatch cylinderBatch;
//錐
GLTriangleBatch coneBatch;
//磁盤
GLTriangleBatch diskBatch;
//顏色
GLfloat vRed[] = {1.f,0.f,0.f,1.f};
GLfloat vBlack[] = {0.f,0.f,0.f,1.f};
//記錄點擊空格的次數(shù)
int step = 0;
#pragma mark - 函數(shù)
/**
設(shè)置窗口
*/
void ChangeSize(int w,int h)
{
if (h == 0) {
h = 1;
}
//設(shè)置視口
glViewport(0, 0, w, h);
//設(shè)置透視投影
//1.觀察者垂直視角度數(shù)。2.寬高比跪帝。3.視點與近剪裁面距離。4.視點與遠剪裁面距離
viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 500.f);
//projectionMatrix矩陣堆棧加載透視投影矩陣
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//modelViewMatrix堆棧矩陣加載一個單元矩陣。其實不加載也沒事拒啰,源碼中可以看到胆数,已經(jīng)幫忙加載了一個了。
modelViewMatrix.LoadIdentity();
//通過GLGeometryTransform管理矩陣堆棧(模型視圖矩陣堆棧黎泣、投影矩陣堆棧)
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
/**
設(shè)置渲染環(huán)境
*/
void SetUpRC ()
{
//設(shè)置清屏顏色
glClearColor(0.3f, 0.3f, 0.3f, 1.f);
//著色器管理器初始化
shaderManager.InitializeStockShaders();
//開啟需要用到的環(huán)境
//開啟深度測試
glEnable(GL_DEPTH_TEST);
//設(shè)置觀察者和物體的距離恕刘,保證物體都能看得到
//這里有兩種方式(1)(2)
//(1)將觀察者(攝像機)位置沿其Z軸向內(nèi)部移動15個單位。負數(shù)往里面移動抒倚,正數(shù)向外面移動褐着。
//cameraFrame.MoveForward(-15.f);
//(2)將物體移動15個單位,觀察者(攝像機)不動
objectFrame.MoveForward(15.f);
//這里直接使用一下GLTools里面給過的圖形
/**
球:
gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
參數(shù):
(1)sphereBatch:三角形批次類對象
(2)fRadius:球體半徑
(3)iSlices:從球體底部堆疊到球體頂部的三角形帶的數(shù)量托呕『兀可以看出球體是一圈圈的三角形帶組成的。
(4)iStacks:圍繞球體一圈排列的三角形片段對數(shù)
建議:一個對稱性較好的球體一般滿足片段數(shù)量是三角形帶數(shù)量的2倍项郊。iSlices * 2 = iStacks
繪制球體都是圍繞Z軸的馅扣,因為這樣Z軸的正方向(+)就是球體頂部,Z軸負方向(-)就是球體底部着降。
*/
gltMakeSphere(sphereBatch, 3.f, 10, 20);
/**
環(huán):
gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
參數(shù):
(1)torusBatch:三角形批次類對象
(2)majorRadius:圓環(huán)中心到外邊緣的半徑(大圓半徑)
(3)minorRadius:圓環(huán)中心到內(nèi)邊緣的半徑(小圓半徑)
(4)numMajor:大圓的三角形數(shù)量
(5)numMinor:小圓的三角形數(shù)量
*/
gltMakeTorus(torusBatch, 3.f, 1, 16, 16);
/**
圓柱:
gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
參數(shù):
(1)cylinderBatch:三角形批次類對象
(2)baseRadius:底部圓半徑
(3)topRadius:頂部圓半徑
(4)fLength:圓柱體的長度(高度)
(5)numSlices:圍繞Z軸的三角形帶的對數(shù)
(6)numStacks:從圓柱底到圓柱頂?shù)娜切螏У臄?shù)量
*/
gltMakeCylinder(cylinderBatch, 3.f, 3.f, 3.f, 16, 3);
/**
錐:特殊的圓柱
gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
參數(shù):
(1)cylinderBatch:三角形批次類對象
(2)baseRadius:底部圓的半徑
(3)topRadius:頂部圓半徑差油,錐體直接給0就行了
(4)fLength:錐體長度(高度)
(5)numSlices:圍繞Z軸的三角形帶的對數(shù)
(6)numStacks:從圓柱底到圓柱頂?shù)娜切螏У臄?shù)量
*/
gltMakeCylinder(coneBatch, 3.f, 0.f, 3.f, 16, 3);
/**
磁盤:
gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks);
參數(shù):
(1)diskBatch:三角形批次類對象
(2)innerRadius:內(nèi)部圓半徑
(3)outerRadius:外部圓半徑
(4)nSlices:圍繞Z軸的三角形對的數(shù)量
(5)nStacks:從內(nèi)邊緣到外邊緣三角形帶的對數(shù)
*/
gltMakeDisk(diskBatch, 1.f, 3.f, 16, 4);
}
/**
利用三角形批次類進行繪制
*/
void DrawBatch(GLTriangleBatch *dBatch)
{
//繪制圖形
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vRed);
//這里用的是指針,看清楚上面任洞,不能用點方法
dBatch->Draw();
//繪制黑色線條
//這里就需要開啟顏色混合厌殉、多邊形偏移、抗鋸齒等功能
//開啟多邊形偏移
glEnable(GL_POLYGON_OFFSET_LINE);
//設(shè)置偏移的面和模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//指定偏移量
glPolygonOffset(-1.f, -1.f);
//開啟混合
glEnable(GL_BLEND);
//設(shè)置混合因子
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//開啟線段模式抗鋸齒(簡單的抗鋸齒)
glEnable(GL_LINE_SMOOTH);
//可以設(shè)置一下線段的寬度
glLineWidth(3.f);
//利用平面著色器畫黑線
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
dBatch->Draw();
//繪制完成記得關(guān)閉之前開啟的功能侈咕,回復(fù)圖形填充方式,并恢復(fù)線段寬度
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
glLineWidth(1.f);
}
//觀察者移動(攝像機)公罕,物體不動
void CameraMoveObjNo()
{
//復(fù)制棧頂?shù)闹担⑶覊喝霔m? modelViewMatrix.PushMatrix();
//獲取觀察者(攝像頭)矩陣
//先定義一個矩陣耀销,方便一會存儲獲取到的觀察者(攝像頭)矩陣
M3DMatrix44f mCamera;
//從cameraFrame中獲取它棧頂?shù)木仃嚶ゾ欤缓筚x值給mCamera铲汪,好用來做矩陣變換,這樣做能保證多圖形的情況下罐柳,不改變原
//cameraFrame的棧頂矩陣掌腰,而用一份復(fù)制出來的操作,不會影響到其他的圖形繪制
cameraFrame.GetCameraMatrix(mCamera);
//讓modelViewMatrix模型視圖矩陣和觀察者(攝像頭)矩陣相乘张吉,發(fā)生第一步變化齿梁,相乘的結(jié)果會存儲到
//modelViewMatrix棧頂里面
modelViewMatrix.MultMatrix(mCamera);
//獲取物體的矩陣
//先定義一個矩陣,也是方便存儲獲取到的物體仿射變換后的矩陣
M3DMatrix44f mObject;
//從objectFrame中獲取其棧頂?shù)木仃嚢褂迹⑶屹x值給mObject勺择,原理和上面的一樣
objectFrame.GetMatrix(mObject);
//讓modelViewMatrix模型視圖矩陣和mObject相乘,結(jié)果矩陣依然存儲在modelViewMatrix棧頂
modelViewMatrix.MultMatrix(mObject);
}
//物體移動伦忠,觀察者(攝像機)不動
void ObjMoveCameraNo()
{
modelViewMatrix.PushMatrix(objectFrame);
}
/**
渲染
*/
void RenderScene()
{
//清空緩沖區(qū)
//雖然模版緩沖區(qū)都沒用到省核,但是養(yǎng)成全部清空的好習(xí)慣肯定不會錯
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//當(dāng)觀察者移動(攝像機),而物體不移動的時候昆码,對應(yīng)了SetUpRC中的(1)
//CameraMoveObjNo();
//當(dāng)觀察者不動气忠,物體移動的時候,對應(yīng)了SetUpRC中的(2)
ObjMoveCameraNo();
//到上面這一步赋咽,矩陣的變換已經(jīng)都做完了旧噪,下面要繪制了
//判斷空格點擊次數(shù),更換不同的圖形
switch (step) {
case 0:
DrawBatch(&sphereBatch);
break;
case 1:
DrawBatch(&torusBatch);
break;
case 2:
DrawBatch(&cylinderBatch);
break;
case 3:
DrawBatch(&coneBatch);
break;
case 4:
DrawBatch(&diskBatch);
break;
}
//繪制完成一個圖形脓匿,就把棧頂?shù)木仃嚳梢訮op出去了舌菜,不要影響下一個圖形的繪制
modelViewMatrix.PopMatrix();
//交換緩沖區(qū)
glutSwapBuffers();
}
/**
設(shè)置特殊鍵位(上下左右)
*/
void SpecialKeys(int key,int x,int y)
{
//這里還是移動世界坐標(biāo)系,因為畫的東西都有太多的頂點了亦镶,移動物體的成本太高日月,效果是一樣的。
switch (key) {
case GLUT_KEY_UP:
objectFrame.RotateWorld(m3dDegToRad(-5.f), 1.f, 0.f, 0.f);
break;
case GLUT_KEY_DOWN:
objectFrame.RotateWorld(m3dDegToRad(5.f), 1.f, 0.f, 0.f);
break;
case GLUT_KEY_LEFT:
objectFrame.RotateWorld(m3dDegToRad(-5.f), 0.f, 1.f, 0.f);
break;
case GLUT_KEY_RIGHT:
objectFrame.RotateWorld(m3dDegToRad(5.f), 0.f, 1.f, 0.f);
break;
}
//移動完了要發(fā)送渲染信號
glutPostRedisplay();
}
/**
設(shè)置鍵盤鍵位(空格)
*/
void SpaceKey(unsigned char key,int x,int y)
{
//拿空格鍵的ASCII碼來判定點擊的是鍵盤上的空格鍵
if (key == 32) {
step++;
if(step > 4) step = 0;
}
switch (step) {
case 0:
glutSetWindowTitle("Sphere球");
break;
case 1:
glutSetWindowTitle("Torus圓環(huán)");
break;
case 2:
glutSetWindowTitle("Cylinder圓柱");
break;
case 3:
glutSetWindowTitle("Cone錐");
break;
case 4:
glutSetWindowTitle("Disk磁盤");
break;
}
//發(fā)送重新渲染的信號
glutPostRedisplay();
}
#pragma mark - main
int main(int argc,char *argv[])
{
//設(shè)置工作目錄和項目目錄都在/Resouce下面缤骨,GLUT優(yōu)先級做過爱咬,但是手動確保一定
gltSetWorkingDirectory(argv[0]);
//GLUT初始化
glutInit(&argc, argv);
//設(shè)置顯示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA | GLUT_STENCIL);
//設(shè)置窗口大小
glutInitWindowSize(600, 600);
//創(chuàng)建窗口并命名
glutCreateWindow("Sphere球");
//注冊函數(shù)
//注冊窗口
glutReshapeFunc(ChangeSize);
//注冊渲染
glutDisplayFunc(RenderScene);
//注冊特殊鍵位
glutSpecialFunc(SpecialKeys);
//注冊空格鍵位
glutKeyboardFunc(SpaceKey);
//初始化Glew庫
GLenum status = glewInit();
if (status != GLEW_OK) {
printf("glew init error : %s \n",glewGetErrorString(status));
}
//設(shè)置渲染環(huán)境
SetUpRC();
//構(gòu)建本地循環(huán)
glutMainLoop();
return 0;
}
顯示效果我就不貼圖了,有問題的話可以留言绊起。
這上面void SetUpRC()
和void RenderScene()
中的(1)(2)的變換精拟,就是視變換的結(jié)果,說的通俗點虱歪,就是山不向你走來蜂绎,你就向山走去。