頭文件介紹:
#include "GLFrustum.h":矩陣工具類,用來(lái)快速設(shè)置正/透視投影矩陣。完成坐標(biāo)3D->2D映射過程。
#include "GLTools.h":頭文件包含了大部分GLTool中類似C語(yǔ)言的獨(dú)立函數(shù)。
#include "GLMatrixStack.h":矩陣工具類??梢岳肎LMatrixStack加載單元矩陣、矩陣相乘、壓棧、出棧、平移、縮放、旋轉(zhuǎn)。
#include "GLFrame.h":矩陣工具類。表示位置。
#include "GLGeometryTransform.h":變換管道類。用來(lái)快速在代碼中傳輸視圖矩陣、投影矩陣、視圖投影變換矩陣等。
#include "GLBatch.h":三角形批次類,可以傳輸頂點(diǎn)、光照、紋理、顏色數(shù)據(jù)到存儲(chǔ)著色器中。
#include <stdio.h>
#include "GLShaderManager.h"
#include "GLTools.h"
#include <GLUT/GLUT.h>
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLMatrixStack.h"
#include <math.h>
#include "GLBatch.h"
#include "GLGeometryTransform.h"
//存儲(chǔ)著色器管理工具類
GLShaderManager shaderManager;
//模型視圖矩陣
GLMatrixStack modelViewMatrix;
//投影矩陣
GLMatrixStack projectionMatrix;
//觀察者坐標(biāo)
GLFrame cameraFrame;
//視圖坐標(biāo)
GLFrame objectFrame;
//投影設(shè)置
GLFrustum viewFrustum;
//幾何變換的管道 方便計(jì)算矩陣
GLGeometryTransform transformPipeline;
//容器類(7種不同的圖元對(duì)應(yīng)7種容器對(duì)象)
GLBatch pointBatch;
GLBatch lineBatch;
GLBatch lineStripBatch;
GLBatch lineLoopBatch;
GLBatch triangleBatch;
GLBatch triangleStripBatch;
GLBatch triangleFanBatch;
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
//跟蹤效果步驟
int nStep = 0;
//重塑函數(shù)
void changeSize(int w, int h){
glViewport(0, 0, w, h);
//由于在這里能夠拿到寬高,所以在這里創(chuàng)建投影矩陣,并將其加載到投影矩陣堆棧中
viewFrustum.SetPerspective(45.0f, float(w) / float(h), 1.0f, 500.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//載入模型視圖矩陣,設(shè)置為單元矩陣,可以不寫
modelViewMatrix.LoadIdentity();
//設(shè)置變換管線 把變換管線通道添加進(jìn)去
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
//設(shè)置點(diǎn)的大小
void drawPoint(){
glPointSize(4.0f);
pointBatch.Draw();
glPointSize(1.0f);
}
//設(shè)置線的寬度
void drawLine(){
glLineWidth(2.0f);
lineBatch.Draw();
glLineWidth(1.0f);
}
//設(shè)置線條的寬度
void drawLines(){
glLineWidth(2.0f);
lineStripBatch.Draw();
glLineWidth(1.0f);
}
//設(shè)置環(huán)線的寬度
void drawLoopLines(){
glLineWidth(2.0f);
lineLoopBatch.Draw();
glLineWidth(1.0f);
}
//金字塔
void DrawWireFramedBatch(GLBatch* pBatch)
{
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
pBatch->Draw();
//畫黑色邊框
glPolygonOffset(-1.0f, -1.0f);// 偏移深度,在同一位置要繪制填充和邊線,會(huì)產(chǎn)生z沖突,所以要偏移
glEnable(GL_POLYGON_OFFSET_LINE);
// 畫反鋸齒,讓黑邊好看些
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//繪制線框幾何黑色版 三種模式,實(shí)心,邊框,點(diǎn),可以作用在正面,背面,或者兩面
//通過調(diào)用glPolygonMode將多邊形正面或者背面設(shè)為線框模式,實(shí)現(xiàn)線框渲染
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//設(shè)置線條寬度
glLineWidth(2.5f);
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);
}
//顯示函數(shù)
void renderScene(void){
//清空緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//壓棧 為了每一次變化相互獨(dú)立(可以理解為復(fù)制了一份,棧頂長(zhǎng)度+1) 不影響后面的變化 此處有兩個(gè)modelViewMatrix
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
//矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果存儲(chǔ)在矩陣堆棧頂部
modelViewMatrix.MultMatrix(mCamera);
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
//此處的modelViewMatrix的矩陣值 = modelViewMatrix * mCamera * mObjectFrame,經(jīng)過了兩次相乘
modelViewMatrix.MultMatrix(mObjectFrame);
//核心方法 使用存儲(chǔ)著色器-平面著色器 參數(shù)2為4*4變換矩陣,通過transformPipeline.GetModelViewProjectionMatrix直接獲得模型視圖投影變換矩陣,不需要我們計(jì)算
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
//根據(jù)空格鍵 切換繪制的圖形
switch (nStep) {
case 0:
//設(shè)置點(diǎn)的大小
drawPoint();
break;
case 1:
//設(shè)置線的寬度
drawLine();
break;
case 2:
//設(shè)置線條的寬度
drawLines();
break;
case 3:
//設(shè)置環(huán)線的寬度
drawLoopLines();
break;
case 4:
DrawWireFramedBatch(&triangleBatch);
break;
case 5:
DrawWireFramedBatch(&triangleStripBatch);
break;
case 6:
DrawWireFramedBatch(&triangleFanBatch);
break;
break;
}
//出棧 還原到以前的模型視圖矩陣
modelViewMatrix.PopMatrix();
//緩沖區(qū)交換
glutSwapBuffers();
}
//設(shè)置渲染環(huán)境
void setupRC(void){
//設(shè)置背景顏色
glClearColor(0.8f, 0.6f, 0.8f, 1);
//.初始化著色器
shaderManager.InitializeStockShaders();
//開啟深度測(cè)試 立體
glEnable(GL_DEPTH_TEST);
//設(shè)置觀察距離 也就是觀察者和圖形的距離
cameraFrame.MoveForward(-15.0f);
//定義一些點(diǎn),三角形形狀
GLfloat vCoast[9] = {
3,3,0,
0,3,0,
3,0,0
};
//批次類繪制 Begin-Copy-End 三件套
pointBatch.Begin(GL_POINTS, 3);
pointBatch.CopyVertexData3f(vCoast);
pointBatch.End();
//通過線的形式
lineBatch.Begin(GL_LINES, 3);
lineBatch.CopyVertexData3f(vCoast);
lineBatch.End();
//通過線段的形式
lineStripBatch.Begin(GL_LINE_STRIP, 3);
lineStripBatch.CopyVertexData3f(vCoast);
lineStripBatch.End();
//通過線環(huán)的形式
lineLoopBatch.Begin(GL_LINE_LOOP, 3);
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個(gè)頂點(diǎn)定義一個(gè)新的三角形
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
// 三角形扇形--六邊形
GLfloat vPoints[100][3];
int nVerts = 0;
//半徑
GLfloat r = 3.0f;
//原點(diǎn)(x,y,z) = (0,0,0);
vPoints[nVerts][0] = 0.0f;
vPoints[nVerts][1] = 0.0f;
vPoints[nVerts][2] = 0.0f;
//M3D_2PI 就是2Pi 的意思,就一個(gè)圓的意思。 繪制圓形
for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
//數(shù)組下標(biāo)自增(每自增1次就表示一個(gè)頂點(diǎn))
nVerts++;
/*
弧長(zhǎng)=半徑*角度,這里的角度是弧度制,不是平時(shí)的角度制
既然知道了cos值,那么角度=arccos,求一個(gè)反三角函數(shù)就行了
*/
//x點(diǎn)坐標(biāo) cos(angle) * 半徑
vPoints[nVerts][0] = float(cos(angle)) * r;
//y點(diǎn)坐標(biāo) sin(angle) * 半徑
vPoints[nVerts][1] = float(sin(angle)) * r;
//z點(diǎn)的坐標(biāo)
vPoints[nVerts][2] = -0.5f;
}
// 結(jié)束扇形 前面一共繪制7個(gè)頂點(diǎn)(包括圓心)
//添加閉合的終點(diǎn)
//課程添加演示:屏蔽177-180行代碼,并把繪制節(jié)點(diǎn)改為7.則三角形扇形是無(wú)法閉合的。
nVerts++;
vPoints[nVerts][0] = r;
vPoints[nVerts][1] = 0;
vPoints[nVerts][2] = 0.0f;
// 加載!
//GL_TRIANGLE_FAN 以一個(gè)圓心為中心呈扇形排列,共用相鄰頂點(diǎn)的一組三角形
triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
triangleFanBatch.CopyVertexData3f(vPoints);
triangleFanBatch.End();
//三角形條帶,一個(gè)小環(huán)或圓柱段
//頂點(diǎn)下標(biāo)
int iCounter = 0;
//半徑
GLfloat radius = 3.0f;
//從0度~360度,以0.3弧度為步長(zhǎng)
for(GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f)
{
//或許圓形的頂點(diǎn)的X,Y
GLfloat x = radius * sin(angle);
GLfloat y = radius * cos(angle);
//繪制2個(gè)三角形(他們的x,y頂點(diǎn)一樣,只是z點(diǎn)不一樣)
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("三角形帶的頂點(diǎn)數(shù):%d\n",iCounter);
//結(jié)束循環(huán),在循環(huán)位置生成2個(gè)三角形
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 共用一個(gè)條帶(strip)上的頂點(diǎn)的一組三角形
triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
triangleStripBatch.CopyVertexData3f(vPoints);
triangleStripBatch.End();
}
//特殊鍵位函數(shù)(上、下、左、右移動(dòng))
void specialKeys(int key, int x, int y){
//由于需要多方位觀察圖形,不能每一個(gè)頂點(diǎn)坐標(biāo)都進(jìn)行旋轉(zhuǎn),所以只能通過改變觀察者的坐標(biāo)來(lái)進(jìn)行。
if (key == GLUT_KEY_UP) {
//此處的1.0表示true
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
} else if (key == GLUT_KEY_DOWN){
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
}else if (key == GLUT_KEY_LEFT){
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
}else 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){
if (key == 32) { //判斷是否是空格
nStep ++;
if (nStep > 6) {
nStep = 0;
}
}
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;
}
//發(fā)送重新渲染請(qǐng)求
glutPostRedisplay();
}
//檢查OpenGL API是否安全可用
int checkOpenGLInit(void){
GLenum status = glewInit();
if(status != GLEW_OK){
printf("GLEW Error:%s\n",glewGetErrorString(status));
return 1;
}
return 0;
}
int main(int argc,char *argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitWindowSize(600, 600);
//申請(qǐng)一個(gè)雙緩存區(qū)、顏色緩存區(qū)、深度緩存區(qū)、模板緩存區(qū)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//創(chuàng)建window的名稱
glutCreateWindow("Title");
//注冊(cè)回調(diào)函數(shù)(改變尺寸)
glutReshapeFunc(changeSize);
//點(diǎn)擊空格時(shí),調(diào)用的函數(shù)
glutKeyboardFunc(keyPressFunc);
//特殊鍵位函數(shù)(上下左右)
glutSpecialFunc(specialKeys);
//顯示函數(shù)
glutDisplayFunc(renderScene);
//判斷一下是否能初始化glew庫(kù),確保項(xiàng)目能正常使用OpenGL 框架
checkOpenGLInit();
//繪制
setupRC();
//runloop運(yùn)行循環(huán)
glutMainLoop();
return 0;
}
核心是UseStockShader方法,參數(shù)1使用的是平面著色器,參數(shù)2要傳入mvp,也就是模型視圖投影矩陣。通過transformPipeline.GetModelViewProjectionMatrix方法快速獲得mvp。而transformPipeline的設(shè)置需要 SetMatrixStacks(modelViewMatrix, projectionMatrix)方法,modelViewMatrix的矩陣需要相乘獲得,projectionMatrix需要LoadMatrix方法獲得。