案例涉及的主要有:
- 点
- 线
- 线段
- 线闭环
- 三角透视金字塔
- 绘制圆环
- 三角形扇形之六边形
- 甜甜圈
对于画这些形态还需添加的对应的头文件以及一些变量的声明:
#include <stdio.h>
//该头文件包含了大部分GLTool中类似C语言的独立函数
#include "GLTools.h"
//矩阵的工具类。可以利于GLMatrixStack 加载单元矩阵/矩阵/矩阵相乘/压栈/出栈/缩放/平移/旋转
#include "GLMatrixStack.h"
//矩阵工具类,表示位置,通过设置vOrigin,vForward,vUp
#include "GLFrame.h"
//矩阵工具类,用来快速设置正/透视投影矩阵,完成坐标从3D->2D映射过程
#include "GLFrustum.h"
//三角形批次类,帮助类,利用它可以传输顶点/光照/纹理/颜色数据到存储着色器中
#include "GLBatch.h"
//变换管道类,用来快速在代码中传输时图矩阵/投影矩阵/视图投影变换矩阵等。
#include "GLGeometryTransform.h"
//数学库
#include "math3d.h"
//在Mac系统下, `#include<glut/glut.h>`,而在windows和Linux上, 我们使用freelut的静态库版本并且需要添加一个宏
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#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; //设置图元绘制时的投影⽅式.
//容器类(7种不同的图元对应7种容器对象)
GLBatch pointBatch; //点
GLBatch lineBatch; //线
GLBatch lineStripBatch; //线段
GLBatch lineLoopBatch; //线闭环
GLBatch triangleBatch; //三角透视金字塔
GLBatch triangleStripBatch; //绘制圆环
GLBatch triangleFanBatch; //三角形扇形之六边形
//几何变换的管道
GLGeometryTransform transformPipeline;
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
//颜色
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
// 跟踪效果步骤
int nStep = 0;
当然,我们从入口 main函数开始:
/**
main函数: 程序⼊⼝.OpenGL 是⾯向过程编程.所以你会发现利⽤OpenGL处理图形/图像都是链式形式.以及基于OpenGL封装的图像处理框架也是链式编程
*/
int main(int argc,char* argv[]) {
printf("入口文件在这里");
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
//申请一个双缓存区、颜色缓存区、深度缓存区、模板缓存区
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//设置window 的尺寸
glutInitWindowSize(800, 600);
//创建window的名称
glutCreateWindow("GLUT_POINT");
//注册回调函数(改变尺寸)
glutReshapeFunc(changeSize);
//点击空格时,调用的函数
glutKeyboardFunc(keyPressFunc);
//特殊键位函数(上下左右
glutSpecialFunc(SpecialKeys);
//显示函数
glutDisplayFunc(RenderScene);
//判断一下是否能初始化glew库,确保项目能正常使用OpenGL 框架
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n",glewGetErrorString(err));
return 1;
}
//绘制
setupRC();
//runloop运行循环
glutMainLoop();
return 0;
}
glutReshapeFunc
在main函数中的注册回调函数 glutReshapeFunc(changeSize);
主要是
- 设置OpenGL 视⼝
- 设置OpenGL 投影⽅式等
它的触发条件如下:
- 新建窗口
- 窗口尺寸发生调整
/**
自定义函数, 通过glutReshaperFunc(函数名)注册为重塑函数。
当屏幕大小发生变化/或者第一次创建窗口时,会调用该函数调整窗口大小/视口大小,以及可以设置投影矩阵
*/
void changeSize(int w, int h) {
//设置视口
glViewport(0, 0, w, h);
//设置一下投影矩阵
//setPerspective函数的参数是一个从顶点方向看去的视场角度(用角度值表示)
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
//拿到投影矩阵
//把透视矩阵加载到透视矩阵对阵中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//先加载一个单元矩阵,指的是 对角线为1 ,其他的为0(可加可不加,modelViewMatrix默认自带一个单元矩阵)
modelViewMatrix.LoadIdentity();
// 初始化渲染管线
//transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
glutKeyboardFunc
对于glutKeyboardFunc(keyPressFunc);
它是点击空格时,会调用的函数,用来通过空格控制切换OpenGL绘画完的各种形态。
//根据空格次数。切换不同的“窗口名称”
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;
}
//重新刷新
glutPostRedisplay();
}
glutSpecialFunc
对于函数glutSpecialFunc(SpecialKeys)
是控制特殊键位函数(上下左右),代码如下:
/**
SpecialKeys函数:特殊键位处理(上、下、左、右移动)
*/
void SpecialKeys(int key, int x, int y) {
//旋转这个物体
if (key == GLUT_KEY_UP)
//围绕一个指定的X轴旋转。
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
glutPostRedisplay();
}
glutDisplayFunc(RenderScene);
对于main函数中的glutDisplayFunc(RenderScene);
是显示渲染函数,RenderScene是⾃定义函数,通过glutDisplayFunc(函数名)
注册为显示渲染函数当屏幕发⽣变化/或者开发者主动渲染会调⽤此函数,⽤来实现数据->渲染过程. 通过调用glutPostRedisplay()方法
也会调用重新渲染。
对于渲染过程,先是清除颜色/深度缓冲区、模版缓冲区 ,然后模型视图矩阵类
modelViewMatrix
通过压栈出栈的方式来操作数据变换。在这里栈是记录状态、回退,记录临时结果
首先进行压栈单元矩阵:
//记录状态 、回退;单元矩阵--> 单元矩阵
modelViewMatrix.PushMatrix();
获取观察者矩阵,并与栈顶的单元矩阵相乘:
//camera -->观察者矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
//栈顶上的单元矩阵 * mCamera = 新的mCamera
modelViewMatrix.MultMatrix(mCamera);
获取物体矩阵,并与栈顶的观察者矩阵相乘:
//物体矩阵
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
//矩阵相乘-->模型视图矩阵 新的mCamera * mObjectFrame
modelViewMatrix.MultMatrix(mObjectFrame);
再使用平面着色器:
//参数1:平面着色器 GLT_SHADER_FLAT
//参数2:模型视图投影矩阵 transformPipeline.GetModelViewProjectionMatrix()简称 mvp
//参数3:颜色
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
再者是图元装配方式,例如:
glPointSize(4.0f);//设置点的大小
pointBatch.Draw();//画上去
glPointSize(1.0f);//用完要还原
最后再出栈,单元矩阵也要还原:
modelViewMatrix.PopMatrix();//出栈
//单元矩阵(还原)
glutSwapBuffers();
完整的RenderScene
函数代码如下:
/**
自定义函数, 通过glutDisplayFunc(函数名)注册为显示渲染函数。
当屏幕发生变化/或者开发者主动渲染会调用次函数,用来实现数据-->渲染过程
召唤场景
*/
void RenderScene(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//压栈 (栈做什么用? 记录一下状态 撤回 临时的结果 可以继续压栈 出栈)
//记录状态 、回退;单元矩阵--> 单元矩阵
modelViewMatrix.PushMatrix();
//camera -->观察者矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
//栈顶上的单元矩阵 * mCamera = 新的mCamera
modelViewMatrix.MultMatrix(mCamera);
//物体矩阵
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
//矩阵相乘-->模型视图矩阵 新的mCamera * mObjectFrame
modelViewMatrix.MultMatrix(mObjectFrame);
//模型视图矩阵(观察者矩阵,物体变换矩阵) 投影矩阵 mvp
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
//图元装配方式
switch (nStep) {
case 0:
glPointSize(4.0f);//设置点的大小
pointBatch.Draw();//画上去
glPointSize(1.0f);//用完要还原
break;
case 1:
glLineWidth(4.0f);//设置线的宽度
lineBatch.Draw();//画上去
glLineWidth(1.0f);//用完要还原
break;
case 2:
glLineWidth(2.0f);
lineStripBatch.Draw();
glLineWidth(1.0f);
break;
case 3:
glLineWidth(4.0f);
lineLoopBatch.Draw();
glLineWidth(1.0f);
break;
case 4:
DrawWireFrameBatch(&triangleBatch);
break;
case 5:
DrawWireFrameBatch(&triangleStripBatch);
break;
case 6:
DrawWireFrameBatch(&triangleFanBatch);
break;
default:
break;
}
modelViewMatrix.PopMatrix();//出栈
//单元矩阵(还原)
glutSwapBuffers();
}
最后在SetupRC()函数
中进行绘制,代码如下:
/**
自定义函数,设置你需要渲染的图形的相关顶点数据/颜⾊数据等数据装备⼯作
*/
void setupRC() {
//会在main函数中调用,而且只会调用一次,用于初始化
//1.初始化
glClearColor(0.7f, 0.7f, 0.7f, 1.0);
//2.
shaderManager.InitializeStockShaders();
//3.平面着色器
/**
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
*/
//投影变化(矩阵)、移动变化(矩阵)--> 通过变化管道(一个名词,能快速将投影变化矩阵与移动变化矩阵相乘)
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
//4. 观察者,修改观察者的位置 MoveForward跳出来看物体 MoveUp改的是Y MoveRight改的是X
cameraFrame.MoveForward(-15.0f);
//5. 顶点数据 --> 物体坐标系(物体本身转为规范坐标系)
GLfloat vCoast[9] = {
3,3,0,
0,3,0,
3,0,0
};
//提交批次类
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();
//线环
lineLoopBatch.Begin(GL_LINE_LOOP, 3);
lineLoopBatch.CopyVertexData3f(vCoast);
lineLoopBatch.End();
//通过三角形创建金字塔
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个顶点定义一个新的三角形
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
// 三角形扇形--六边形
GLfloat vPoints[100][3];
int nVerts = 0;
//半径
GLfloat r = 3.0f;
//原点(x,y,z) = (0,0,0);
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个顶点(包括圆心)
//添加闭合的终点
//课程添加演示:屏蔽177-180行代码,并把绘制节点改为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++;
}
// 关闭循环
printf("三角形带的顶点数:%d\n",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();
}
甜甜圈案例:
甜甜圈在绘制3D场景的时候,我们需要决定哪些部分是对观察者可⻅的,或者哪些部分对观察者不可⻅的.对于不可⻅的部分,应该及早丢弃.例如在⼀个不透明的墙壁后,就不应该渲染.这种情况叫做”隐藏⾯消除”(Hidden surface elimination).
对于在渲染过程中产生的这种问题:我们首先可以想到的是油画算法
.
油画算法:
-
先绘制场景中的离观察者较远的物体,再绘制较近的物体
一层一层画上去,便可以解决隐藏面消除的问题。
油画1
不过油画算法也有它对应的弊端,如果是几个物体对应叠加的话,这种情况用油画算法就不能处理了,如下图:
油画2
那么就出现了另一种解决方案:
正背面剔除
在一个3D图形中,从任何一个方向看,最多我们只能看到3个面,而其他看不到的几个面都看不到,我们为何不可以把它去掉呢,这样的话也能提升了OpenGL的渲染性能。
任何平面都有正反两面,那么我们如何确认观察者视野中的正反面呢,答案是通过
通过分析顶点数据的顺序和观察者⽅向共同决定的.随着观察者的⻆度⽅向的改变,正⾯背⾯也会跟着改变
正反面区分:
- 默认正⾯: 按照逆时针顶点连接顺序的一⾯
- 默认反⾯: 按照顺时针顶点连接顺序的一⾯
在OpenGL中也有对应正反面的API:
//开启表⾯剔除(默认背⾯剔除)
void glEnable(GL_CULL_FACE);
//关闭表⾯剔除(默认背⾯剔除)
void glDisable(GL_CULL_FACE);
//⽤户选择剔除那个⾯(正⾯/背⾯)
void glCullFace(GLenum mode); //mode参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默认GL_BACK
//⽤户指定绕序那个为正⾯
void glFrontFace(GLenum mode); //mode参数为: GL_CW,GL_CCW,默认值:GL_CCW
//例如,剔除正⾯实现(1)
glCullFace(GL_BACK);
glFrontFace(GL_CW);
//例如,剔除正⾯实现(2)
glCullFace(GL_FRONT);
这其中有一个 添加右键菜单栏操作:
// Create the Menu
glutCreateMenu(ProcessMenu);
glutAddMenuEntry("Toggle depth test",1);
glutAddMenuEntry("Toggle cull backface",2);
glutAddMenuEntry("Set Fill Mode", 3);
glutAddMenuEntry("Set Line Mode", 4);
glutAddMenuEntry("Set Point Mode", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
ProcessMenu
自定义函数:
void ProcessMenu(int value)
{
switch(value)
{
case 1:
//深度测试
iDepth = !iDepth;
break;
case 2:
//正面剔除
iCull = !iCull;
break;
case 3:
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case 4:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
break;
case 5:
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
break;
}
glutPostRedisplay();
}
甜甜圈完整代码如下:
//演示了OpenGL背面剔除,深度测试,和多边形模式
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
////设置角色帧,作为相机
GLFrame viewFrame;
//使用GLFrustum类来设置透视投影
GLFrustum viewFrustum;
GLTriangleBatch torusBatch;
GLMatrixStack modelViewMatix;
GLMatrixStack projectionMatrix;
GLGeometryTransform transformPipeline;
GLShaderManager shaderManager;
//标记:背面剔除、深度测试
int iCull = 0;
int iDepth = 0;
//渲染场景
void RenderScene()
{
//1.清除窗口和深度缓冲区
//可以给学员演示一下不清空颜色/深度缓冲区时.渲染会造成什么问题. 残留数据
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//开启/关闭正背面剔除功能
if (iCull) {
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
}else
{
glDisable(GL_CULL_FACE);
}
//2.把摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
//3.设置绘图颜色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//4.
//使用平面着色器
//参数1:平面着色器
//参数2:模型视图投影矩阵
//参数3:颜色
// shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
//使用默认光源着色器
//通过光源、阴影效果跟提现立体效果
//参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
//参数2:模型视图矩阵
//参数3:投影矩阵
//参数4:基本颜色值
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
//5.绘制
torusBatch.Draw();
//6.出栈 绘制完成恢复
modelViewMatix.PopMatrix();
//7.交换缓存区
glutSwapBuffers();
}
void SetupRC()
{
//1.设置背景颜色
glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
//2.初始化着色器管理器
shaderManager.InitializeStockShaders();
//3.将相机向后移动7个单元:肉眼到物体之间的距离
viewFrame.MoveForward(7.0);
//4.创建一个甜甜圈
//void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
//参数1:GLTriangleBatch 容器帮助类
//参数2:外边缘半径
//参数3:内边缘半径
//参数4、5:主半径和从半径的细分单元数量
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
//5.点的大小(方便点填充时,肉眼观察)
glPointSize(4.0f);
}
//键位设置,通过不同的键位对其进行设置
//控制Camera的移动,从而改变视口
void SpecialKeys(int key, int x, int y)
{
//1.判断方向
if(key == GLUT_KEY_UP)
//2.根据方向调整观察者位置
viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
//3.重新刷新
glutPostRedisplay();
}
//窗口改变
void ChangeSize(int w, int h)
{
//1.防止h变为0
if(h == 0)
h = 1;
//2.设置视口窗口尺寸
glViewport(0, 0, w, h);
//3.setPerspective函数的参数是一个从顶点方向看去的视场角度(用角度值表示)
// 设置透视模式,初始化其透视矩阵
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
//4.把透视矩阵加载到透视矩阵对阵中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//5.初始化渲染管线
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
void ProcessMenu(int value)
{
switch(value)
{
case 1:
//深度测试
iDepth = !iDepth;
break;
case 2:
//正面剔除
iCull = !iCull;
break;
case 3:
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case 4:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
break;
case 5:
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
break;
}
glutPostRedisplay();
}
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("Geometry Test Program");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
//添加右击菜单栏
// Create the Menu
glutCreateMenu(ProcessMenu);
glutAddMenuEntry("Toggle depth test",1);
glutAddMenuEntry("Toggle cull backface",2);
glutAddMenuEntry("Set Fill Mode", 3);
glutAddMenuEntry("Set Line Mode", 4);
glutAddMenuEntry("Set Point Mode", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
网友评论