OpenGL的矩阵堆栈指的就是内存中专门用来存放矩阵数据的某块特殊区域。
一般说来,矩阵堆栈常用于构造具有继承性的模型,即由一些简单目标构成的复杂模型。矩阵堆栈对复杂模型运动过程中的多个变换操作之间的联系与独立十分有利。因为所有矩阵操作函数如glLoadMatrix(),glMultMatrix(),glLoadIdentity()等只处理当前矩阵或堆栈顶部矩阵,这样堆栈中下面的其它矩阵就不受影响。
矩阵堆栈
- 是先进先出,默认深度是64
- 矩阵堆在初始化时已经在堆栈中包含了单位矩阵
常见的矩阵操作:
1、单元矩阵
//在堆栈顶部载⼊一个单元矩阵
void GLMatrixStack::LoadIndentiy(void);
2、任意矩阵
//在堆栈顶部载入任何矩阵.参数:4*4矩阵
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
3、获取矩阵堆栈顶部的值
void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);
4、矩阵相乘
//矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部
void GLMatrixStack::MultMatrix(const M3DMatrix44f);
5、矩阵压栈
//将当前矩阵压⼊入堆栈
void GLMatrixStack::PushMatrix(void);
//将M3DMatrix44f 矩阵对象压入当前矩阵堆栈
void PushMatrix(const M3DMatrix44f mMatrix);
//将GLFame 对象压入矩阵对象
void PushMatrix(GLFame &frame);
6、矩阵出栈
//出栈(出栈指的是移除顶部的矩阵对象)
void GLMatrixStack::PopMatrix(void);
7、仿射变换
GLMatrixStack类也内建了对创建旋转、平移和缩放矩阵的支持
//旋转
//Rotate 函数angle参数是传递的度数,而不是弧度
void MatrixStack::Rotate(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);
//平移
void MatrixStack::Translate(GLfloat x,GLfloat y,GLfloat z);
//缩放
void MatrixStack::Scale(GLfloat x,GLfloat y,GLfloat z);
通过金字塔案例讲解矩阵堆栈相关的使用
声明全局变量
GLMatrixStack modelViewMatrix;// 模型视图矩阵堆栈
GLMatrixStack projectionMatrix;// 投影视图矩阵堆栈
GLFrame cameraFrame;// 观察者坐标系=> 观察者矩阵
GLFrame objectFrame;// 物体坐标系 => 物体矩阵
GLBatch triangleBatch;// 简单的批次容器,是GLTools的
GLFrustum viewFrustum;// 投影方式,学名:视景体。用来构造投影矩阵。
//几何变换的管道
GLGeometryTransform transformPipeline;
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
ChangeSize
函数
在main函数中这样注册glutReshapeFunc(changeSize)
,在新建窗口或窗口大小改变时会调用ChangeSize
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
//创建投影矩阵,并将它载入投影矩阵堆栈中
// 透视投影
// 参数1:从顶点方向看去的视场角度(用角度值表示)
// 参数2:宽和高的纵横比
// 参数3:fNear
// 参数4:fFar
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
//上面只是设置了但没有用,我们需要把设置的转化为矩阵
// 往投影矩阵堆栈projectionMatrix里加载一个矩阵,从我们的视景体viewFrustum里获取投影矩阵GetProjectionMatrix()
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//调用顶部载入单元矩阵 可不写 默认有一个单元矩阵
modelViewMatrix.LoadIdentity();
}
SetupRC
函数
设置渲染环境,在main函数中调用
transformPipeline:它是变换管道,类型是 GLGeometryTransform,专门用来管理投影和模型矩阵的 。其实就是把两个矩阵堆栈都存到他这个管道里,方便我们用的时候拿出来。
void SetupRC()
{
// 灰色的背景
glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
//设置变换管线以使用两个矩阵堆栈
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
cameraFrame.MoveForward(-15.0f);
// 通过三角形创建金字塔
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();
}
RenderScene
函数
在main函数注册glutDisplayFunc(RenderScene)
,窗口大小变化或调用glutPostRedisplay()方法时会走这个方法
//召唤场景
void RenderScene(void) {
//1.清除一个或者一组特定的缓存区
/*
缓冲区是一块存在图像信息的储存空间,红色、绿色、蓝色和alpha分量通常一起分量通常一起作为颜色缓存区或像素缓存区引用。
OpenGL 中不止一种缓冲区(颜色缓存区、深度缓存区和模板缓存区)
清除缓存区对数值进行预置
参数:指定将要清除的缓存的
GL_COLOR_BUFFER_BIT :指示当前激活的用来进行颜色写入缓冲区
GL_DEPTH_BUFFER_BIT :指示深度缓存区
GL_STENCIL_BUFFER_BIT:指示模板缓冲区
*/
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//模型视图矩阵栈堆,压栈
modelViewMatrix.PushMatrix();
//获取摄像头矩阵
M3DMatrix44f mCamera;
//从camereaFrame中获取矩阵到mCamera
cameraFrame.GetCameraMatrix(mCamera);
//模型视图堆栈的 矩阵与mCamera矩阵 相乘之后,存储到modelViewMatrix矩阵堆栈中
modelViewMatrix.MultMatrix(mCamera);
//创建矩阵mObjectFrame
M3DMatrix44f mObjectFrame;
//从ObjectFrame 获取矩阵到mOjectFrame中
objectFrame.GetMatrix(mObjectFrame);
//将modelViewMatrix 的堆栈中的矩阵 与 mOjbectFrame 矩阵相乘,存储到modelViewMatrix矩阵堆栈中
modelViewMatrix.MultMatrix(mObjectFrame);
//使用平面着色器
//参数1:类型
//参数2:通过transformPipeline获取模型视图矩阵
//参数3:颜色
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
// 平面着色器,绘制三角形
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
//使用三角形批次类绘制
triangleBatch.Draw();
// 画出黑色轮廓
glPolygonOffset(-1.0f, -1.0f);
// 开启线段圆滑处理
glEnable(GL_LINE_SMOOTH);
// 开启混合功能
glEnable(GL_BLEND);
// 颜色混合
// 表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减 小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 通过程序点大小模式来设置点的大小
glEnable(GL_POLYGON_OFFSET_LINE);
// 多边形模型(背面、线) 将多边形背面设为线框模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// 线条宽度
glLineWidth(2.5f);
// 平面着色器绘制线条
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
triangleBatch.Draw();
// 恢复多边形模式和深度测试
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glLineWidth(1.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
// 绘制完毕了,需要把栈顶的矩阵pop出去,不要影响我下一次绘图
modelViewMatrix.PopMatrix();
// Flush drawing commands
glutSwapBuffers();
}
main
函数
int main(int argc,char *argv[]) {
//设置当前工作目录,针对MAC OS X
/*
`GLTools`函数`glSetWorkingDrectory`用来设置当前工作目录。实际上在Windows中是不必要的,因为工作目录默认就是与程序可执行执行程序相同的目录。但是在Mac OS X中,这个程序将当前工作文件夹改为应用程序捆绑包中的`/Resource`文件夹。`GLUT`的优先设定自动进行了这个中设置,但是这样中方法更加安全。
*/
gltSetWorkingDirectory(argv[0]);
//初始化GLUT库,这个函数只是传输命令参数并且初始化glut库
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);
//GLUT窗口大小、窗口标题
glutInitWindowSize(800, 600);
glutCreateWindow("Triangle");
/*
GLUT 内部运行一个本地消息循环,拦截适当的消息。然后调用我们不同时间注册的回调函数。我们一共注册2个回调函数:
1)为窗口改变大小而设置的一个回调函数
2)包含OpenGL 渲染的回调函数
*/
//注册重塑函数
glutReshapeFunc(changeSize);
//注册显示函数
glutDisplayFunc(RenderScene);
//特殊键位
glutSpecialFunc(SpeacialKeys);
/*
初始化一个GLEW库,确保OpenGL API对程序完全可用。
在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
*/
GLenum status = glewInit();
if (GLEW_OK != status) {
printf("GLEW Error:%s\n",glewGetErrorString(status));
return 1;
}
//设置我们的渲染环境
setupRC();
glutMainLoop();
return 0;
}
网友评论