// GLTool.h头⽂文件包含了大部分GLTool中类似C语言的独立函数
#include "GLTools.h"
// 矩阵的⼯具类.可以利于GLMatrixStack 加载单元矩阵/矩阵/矩阵相乘/压栈/出栈/缩放/平移/旋转
#include "GLMatrixStack.h"
// 矩阵工具类,表示位置.通过设置vOrigin,vForward,vUps等
#include "GLFrame.h"
// 矩阵⼯具类,⽤来快速设置正/透视投影矩阵.完成坐标从3D->2D的映射过程
#include "GLFrustum.h"
// 三⻆形批次类,帮助类,利用它可以传输顶点/光照/纹理/颜⾊数据到存储着⾊器中
#include "GLBatch.h"
// 变换管道类,⽤来快速在代码中传输视图矩阵/投影矩阵/视图投影变换矩阵等
#include "GLGeometryTransform.h"
// 在Mac 系统下,`#include`在Windows 和 Linux上,我们使用freeglut的静态库版本并且需要添加一个宏
#include // 数学库
#ifdef __APPLE__
#include
#else
#define FREEGLUT_STATIC
#include
#endif
/*
GLMatrixStack 变化管线使用矩阵堆栈
GLMatrixStack 构造函数允许指定堆栈的最大深度、默认的堆栈深度为64.这个矩阵堆在初始化时已经在堆栈中包含了单位矩阵。
GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
//通过调用顶部载入这个单位矩阵
void GLMatrixStack::LoadIndentiy(void);
//在堆栈顶部载入任何矩阵
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
*/
GLShaderManager shaderManager; // 固定管线管理器
GLMatrixStack modelViewMatrix; // 模型视图矩阵(变换)
GLMatrixStack projectionMatrix; // 投影矩阵
GLFrame cameraFrame; // 观察者位置
GLFrame objectFrame; // 世界坐标位置
GLFrustum viewFrustum; // 投影矩阵
GLGeometryTransform transformPipeline; // 几何变换的管道
// 不同的图元对应容器对象
GLBatch triangleBatch;
GLBatch triangleStripBatch;
GLBatch triangleFanBatch;
GLfloat vGreen[] = {
1.0f, 0.0f, 0.0f, 1.0f};// 图元面颜色
GLfloat vBlack[] = {
0.0f, 0.0f, 0.0f, 1.0f};// 图元棱颜色
// 跟踪控制效果步骤
int stepNumber = 0;
int main(int argc ,char *argv[]) {
gltSetWorkingDirectory(argv[0]);
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);
// 设置window的大小和名称
glutInitWindowSize(800, 600);
glutCreateWindow("四棱锥");
// 注册回调函数(window改变尺寸)
glutReshapeFunc(changeSize);
// 注册特殊键位函数(上下左右)
glutSpecialFunc(specialKeys);
// 注册键盘普通键位的调用函数
glutKeyboardFunc(keyPressFunc);
// 显示函数
glutDisplayFunc(renderScene);
// 判断是否能初始化glew库,确保项目能正常使用OpenGL框架;在视图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
GLenum statue = glewInit();
if (GLEW_OK != statue) {
printf("GLEW Error:%s\n",glewGetErrorString(statue));
return 1;
}
// 设置渲染环境
setupRC();
glutMainLoop();
return 0;
}
// 窗口刚刚创建或者已更改大小,都需要使用窗口维度设置视口和投影矩阵
void changeSize(int w, int h) {
// 设置视口
glViewport(0, 0, w, h);
// 设置投影矩阵,载入投影矩阵堆栈中
// fFov:从顶点方向看去的视场角度(用角度值表示)
// fAspect:宽和高的纵横比(w/h)
// fNear
// fFar
viewFrustum.SetPerspective(40.0f, float(w)/float(h), 1.f, 500.f);
// 往投影矩阵堆栈projectionMatrix里加载一个矩阵,从视景体viewFrustum里获取投影矩阵GetProjectionMatrix()
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 模型视图矩阵是用来旋转、平移等操作的,并没有变化,所以只需要加载一个单元矩阵;
modelViewMatrix.LoadIdentity();
}
// 点击“空格”处理:根据点击空格的次数,切换不同的”窗口名称“,重新渲染
void keyPressFunc(unsigned char key, int x, int y) {
if (key == 32) {
stepNumber += 1;
if (stepNumber > 2) {
stepNumber = 0;
}
}
switch (stepNumber) {
case 0:
glutSetWindowTitle("四棱锥");
break;
case 1:
glutSetWindowTitle("圆环");
break;
case 2:
glutSetWindowTitle("三角形扇");
break;
default:
glutSetWindowTitle("");
break;
}
glutPostRedisplay();
}
// 特殊键位的逻辑处理(上、下、左、右)
void specialKeys(int key, int x, int y) {
// objectFrame记录旋转的物体
if (key == GLUT_KEY_UP) {
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.f, 0.f, 0.f);
}
if (key == GLUT_KEY_DOWN) {
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.f, 0.f, 0.f);
}
if (key == GLUT_KEY_LEFT) {
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.f, 1.0f, 0.f);
}
if (key == GLUT_KEY_RIGHT) {
objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.f, 1.0f, 0.f);
}
glutPostRedisplay();
}
void setupRC() {
// 设置窗口背景色
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
shaderManager.InitializeStockShaders();
// 设置变换管线两个矩阵堆栈(模型视图矩阵、投影矩阵)
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
// 修改观察者的位置
cameraFrame.MoveForward(-20.f);
/*
常见函数:
void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
参数1:表示使用的图元
参数2:顶点数
参数3:纹理坐标(可选)
* 复制顶点坐标
void GLBatch::CopyVertexData3f(GLFloat *vNorms);
* 结束,表示已经完成数据复制工作
void GLBatch::End(void);
*/
// 绘制"四棱锥"
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
};
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
// 绘制“三角形扇”
GLfloat vPoints[100][3];
int nVerts = 0;
GLfloat r = 3.0f;
vPoints[nVerts][0] = 0.0f;
vPoints[nVerts][1] = 0.0f;
vPoints[nVerts][2] = 0.0f;
// M3D_2PI 就是2Pi,绘制圆形
for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
//数组下标自增(每自增1次就表示一个顶点)
nVerts++;
/*
弧长=半径*角度,这里的角度是弧度制,不是平时的角度制
既然知道了cos值,那么角度=arccos,求一个反三角函数就行了
*/
//x点坐标 cos(angle) * 半径
vPoints[nVerts][0] = float(cos(angle)) * r;
//y点坐标 sin(angle) * 半径
vPoints[nVerts][1] = float(sin(angle)) * r;
//z点的坐标
vPoints[nVerts][2] = -0.5f;
}
// 结束扇形,一共绘制7个顶点(包括圆心,然后添加闭合的终点
nVerts++;
vPoints[nVerts][0] = r;
vPoints[nVerts][1] = 0;
vPoints[nVerts][2] = 0.0f;
// 加载:GL_TRIANGLE_FAN 以一个圆心为中心呈扇形排列,共用相邻顶点的一组三角形
triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
triangleFanBatch.CopyVertexData3f(vPoints);
triangleFanBatch.End();
// "圆环":三角形条带,一个小环或圆柱段
int iCounter = 0;
GLfloat radius = 3.0f;
// 从0度~360度,以0.3弧度为步长
for(GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f) {
// 获取圆形的顶点的X,Y
GLfloat x = radius * sin(angle);
GLfloat y = radius * cos(angle);
// 绘制2个三角形(他们的x,y顶点一样,只是z点不一样)
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++;
}
// 关闭循环,结束循环,在循环位置生成2个三角形
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 共用一个条带(strip)上的顶点的一组三角形
triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
triangleStripBatch.CopyVertexData3f(vPoints);
triangleStripBatch.End();
}
// 场景绘制
void renderScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
modelViewMatrix.PushMatrix();
// 观察者矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
// 矩阵相乘,放入模型视图矩阵
modelViewMatrix.MultMatrix(mCamera);
// 物体矩阵
M3DMatrix44f mObjectFrame;
// 只要使用GetMatrix函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载。
// 用来使用GLShaderManager的使用。或者是获取顶部矩阵的顶点副本数据
objectFrame.GetMatrix(mObjectFrame);
// 矩阵相乘堆栈的顶部矩阵,相乘的结果随后简存储在堆栈的顶部
modelViewMatrix.MultMatrix(mObjectFrame);
// GetNormalMatrix()(法线矩阵),GetProjectionMatrix()(投影矩阵),GetModelViewMatrix()(模型视图矩阵),GetModelViewProjectionMatrix()(模型视图投影矩阵)
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
switch (stepNumber) {
case 0:
DrawWireFramedBatch(&triangleBatch);
break;
case 1:
DrawWireFramedBatch(&triangleStripBatch);
break;
case 2:
DrawWireFramedBatch(&triangleFanBatch);
break;
default:
break;
}
// 还原到以前的模型视图矩阵(单位矩阵)
modelViewMatrix.PopMatrix();
// 进行缓冲区交换
glutSwapBuffers();
}
void DrawWireFramedBatch(GLBatch* pBatch) {
/*------------画红色部分----------------*/
/* GLShaderManager 中的Uniform 值——平面着色器
参数1:平面着色器
参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
--transformPipeline 变换管线(指定了2个矩阵堆栈)
参数3:颜色值
*/
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
pBatch->Draw();
/*-----------边框部分-------------------*/
/*
glEnable(GLenum mode); 用于启用各种功能。功能由参数决定
参数列表:http://blog.csdn.net/augusdi/article/details/23747081
注意:glEnable() 不能写在glBegin() 和 glEnd()中间
GL_POLYGON_OFFSET_LINE 根据函数glPolygonOffset的设置,启用线的深度偏移
GL_LINE_SMOOTH 执行后,过虑线点的锯齿
GL_BLEND 启用颜色混合。例如实现半透明效果
GL_DEPTH_TEST 启用深度测试 根据坐标的远近自动隐藏被遮住的图形(材料
glDisable(GLenum mode); 用于关闭指定的功能 功能由参数决定
*/
// 画黑色边框
glPolygonOffset(-1.0f, -1.0f);// 偏移深度,在同一位置要绘制填充和边线,会产生z冲突,所以要偏移
glEnable(GL_POLYGON_OFFSET_LINE);
// 画反锯齿,让黑边好看些
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 绘制线框几何黑色版 三种模式,实心,边框,点,可以作用在正面,背面,或者两面
// 通过调用glPolygonMode将多边形正面或者背面设为线框模式,实现线框渲染
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//设置线条宽度
glLineWidth(2.5f);
/* GLShaderManager 中的Uniform 值——平面着色器
参数1:平面着色器
参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
--transformPipeline.GetModelViewProjectionMatrix() 获取的
GetMatrix函数就可以获得矩阵堆栈顶部的值
参数3:颜色值(黑色)
*/
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
pBatch->Draw();
// 复原原本的设置
// 通过调用glPolygonMode将多边形正面或者背面设为全部填充模式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glLineWidth(1.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
modelViewMatrix.PushMatrix();
// 观察者矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
// 矩阵相乘,放入模型视图矩阵
modelViewMatrix.MultMatrix(mCamera);
// 物体矩阵
M3DMatrix44f mObjectFrame;
// 只要使用GetMatrix函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载。
// 用来使用GLShaderManager的使用,或者是获取顶部矩阵的顶点副本数据
objectFrame.GetMatrix(mObjectFrame);
modelViewMatrix.MultMatrix(mObjectFrame);
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
........... // 绘制图元
// 还原到以前的模型视图矩阵(单位矩阵)
modelViewMatrix.PopMatrix();
// 进行缓冲区交换
glutSwapBuffers();
完整代码和具体逻辑传送门:OpenGL之矩阵堆栈绘制立体图元。