美文网首页
矩阵堆栈管理

矩阵堆栈管理

作者: silasjs | 来源:发表于2019-06-23 22:42 被阅读0次

矩阵堆栈管理

我们在使用OpenGL渲染时,通常需要展现出一些动画效果,每种动画都离不开基本的形变方式:平移、旋转、缩放。一个4x4的矩阵可以描述3D坐标上的一个位置和方向。(没接触过矩阵的可以简单了解下)一个顶点乘以一个形变矩阵后,得到的是这个顶点形变后的位置。一个对象的所有顶点都乘以这个形变矩阵就会得到这个对象形变后的位置。A坐标系下的向量乘以一个包含B坐标系的矩阵,得到的是B坐标系下的新向量。

而变换管线中从顶点数据开始到最终在窗口呈现,中间有多层变换。其中我们要处理的只有模型视图矩阵和投影矩阵,但复杂的需求场景也会降低开发效率。我们可以使用math3d库中的矩阵堆栈类GLMatrixStack来完成这部分工作。

矩阵堆栈类GLMatrixStack

常用函数

//构造函数:可以指定堆栈的深度,默认64。初始化时已经使用m3dLoadIdentity44()加载了一个单元矩阵。
GLMatrixStack::GLMatrixStack(int istackDepth = 64);

//在栈顶部载入一个单元矩阵
void GLMat rixStack::LoadIdentity(void);

//在栈顶载一个矩阵
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);

//矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部
void GLMatrixStack::MultMatrix(const M3DMatrix44f mMatrix);

//GetMatrix():获取矩阵堆栈顶部的值
//可以进行两次重载,为了适应GLShaderMananger的使用,或者获取顶部矩阵的副本
const M3DMatrix44f & GLMatrixStack::GetMatrix(void);
void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);

压栈出栈

矩阵堆栈的真正价值在于通过压栈存储一个状态,出栈恢复原来的状态。

//将当前矩阵压入堆栈(栈顶矩阵copy一份后,再压入栈顶)
void GLMatrixStack::PushMatrix(void);

//将mMatrix矩阵对象压入当前矩阵堆栈
void PushMatrix(const M3DMatrix44f mMatrix);

//将GLFame对象压入矩阵对象
void PushMatrix(GLFame &frame);

//出栈(出栈指的是移除顶部的矩阵对象)
void GLMatrixStack::PopMatrix(void);

仿射变换

//旋转,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);

示例代码

下面是个球体世界demo代码,其中大球自转,小球公转的变换矩阵都是用矩阵堆栈管理对象 GLMatrixStack来操作的。

//
//  main.cpp
//  OpenGLDemo
//
//  Created by silas on 6/4/19.
//  Copyright © 2019 guokai. All rights reserved.
//

#define kDebug 1
#if kDebug
#define SSPrintf(fmt, args...) (printf("\n%s--%d\n" fmt, __FUNCTION__, __LINE__, ##args))
#else
#define SSPrintf(fmt, ...)
#endif

#include <GLTools.h>
#include <GLUT/GLUT.h>

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

#include <GLShaderManager.h>
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#include "GLFrustum.h"
#include "StopWatch.h"

///MARK:- 全局变量
GLShaderManager shaderManager;
GLGeometryTransform transformPipeline;
GLFrustum viewFrustum;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectMatrix;

GLBatch floorBatch;
GLTriangleBatch sphereBigBatch;
GLTriangleBatch sphereSmallBatch;

#define kSphereSmakkNum (50)
GLFrame sphereSmallFrame[kSphereSmakkNum];

GLFrame cameraFrame;//观察者矩阵

//MARK:- 函数
void setupRC() {
    glClearColor(0.74f, 0.62f, 0.88f, 1.0f);
    glEnable(GL_DEPTH_TEST);
    shaderManager.InitializeStockShaders();
    
    floorBatch.Begin(GL_LINES, 324);
    for (GLfloat i = -20.0f; i <= 20.0f; i += 0.5f) {
        floorBatch.Vertex3f(i, -0.6f, 20.0f);
        floorBatch.Vertex3f(i, -0.6f, -20.0f);
        
        floorBatch.Vertex3f(-20.0f, -0.6f, i);
        floorBatch.Vertex3f(20.0f, -0.6f, i);
    }
    floorBatch.End();
    
    gltMakeSphere(sphereBigBatch, 0.5f, 20, 30);
    
    gltMakeSphere(sphereSmallBatch, 0.2, 22, 10);
    for (GLint i = 0; i < kSphereSmakkNum; i++) {
        //y轴不变,X,Z产生随机值
        GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
        GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
        
        sphereSmallFrame[i].SetOrigin(x, 0.0f, z);
    }
}
void changeSize(int w, int h) {
//    SSPrintf("w = %d, h = %d", w, h);
    glViewport(0, 0, w, h);
    
    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1, 100);
    projectMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    modelViewMatrix.LoadIdentity();
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectMatrix);
}
void drawWireFramedWithBatch(GLBatch *batch) {
    
}
void drawWireFramedWithTriangleBatch(GLTriangleBatch *triangleBatch) {
    GLfloat black[] = {0.0f, 0.0f, 0.0f, 1.0f};
    
    glEnable(GL_POLYGON_OFFSET_LINE);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glPolygonOffset(-1.0f, -1.0f);
    glLineWidth(2.0f);
    
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 black);
    sphereBigBatch.Draw();
    
    glDisable(GL_POLYGON_OFFSET_LINE);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glPolygonOffset(0.0f, 0.0f);
    glLineWidth(1.0f);
    glDisable(GL_LINE_SMOOTH);
    glDisable(GL_BLEND);
}
void renderScene() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    GLfloat floorColor[] = {0.0f, 0.0f, 0.0f, 1.0f};
    GLfloat sphereBigColor[] = {1.0f, 0.0f, 0.0f, 1.0f};
    GLfloat sphereSmallColor[] = {1.0f, 0.0f, 1.0f, 1.0f};
    
    //加入观察者
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.MultMatrix(mCamera);
    
    //画地板
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 floorColor);
    floorBatch.Draw();
    
    //画大球
    modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
    
    static CStopWatch timer;
    GLfloat angle = timer.GetElapsedSeconds() * 60;
    modelViewMatrix.PushMatrix();
    modelViewMatrix.Rotate(m3dDegToRad(angle), 0.0f, 1.0f, 0.0f);
    
    M3DMatrix44f lightPosition = {0.0f, 10.0f, 5.0f, 1.0f};
    
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,
                                 transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(),
                                 lightPosition,
                                 sphereBigColor);
    sphereBigBatch.Draw();
    if (1) {
        drawWireFramedWithTriangleBatch(&sphereBigBatch);
    }
    modelViewMatrix.PopMatrix();
    
    //画小球
    for (int i = 0; i < kSphereSmakkNum; i++) {
        modelViewMatrix.PushMatrix();
        modelViewMatrix.MultMatrix(sphereSmallFrame[I]);
        
        shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,
                                     transformPipeline.GetModelViewMatrix(),
                                     transformPipeline.GetProjectionMatrix(),
                                     lightPosition,
                                     sphereSmallColor);
        sphereSmallBatch.Draw();
        
        modelViewMatrix.PopMatrix();
    }
    
    //使小球公转
    modelViewMatrix.Rotate(angle * -2.0f, 0.0f, 1.0f, 0.0f);
    modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
//    shaderManager.UseStockShader(GLT_SHADER_FLAT,
//                                 transformPipeline.GetModelViewProjectionMatrix(),
//                                 sphereSmallColor);
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,
                                 transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(),
                                 lightPosition,
                                 sphereSmallColor);
    sphereSmallBatch.Draw();
    
    modelViewMatrix.PopMatrix();//pop观察者
    
    glutSwapBuffers();
    glutPostRedisplay();
}
void specialKey(int key, int x, int y) {
    if (key == GLUT_KEY_UP) {
//        cameraFrame.TranslateWorld(0.0f, 0.0f, -0.25f);
        cameraFrame.MoveForward(0.25f);
    }
    if (key == GLUT_KEY_DOWN) {
//        cameraFrame.TranslateWorld(0.0f, 0.0f, 0.5f);
        cameraFrame.MoveForward(-0.25f);
    }
    if (key == GLUT_KEY_LEFT) {
        cameraFrame.RotateWorld(m3dDegToRad(2.5f), 0.0f, 1.0f, 0.0f);
    }
    if (key == GLUT_KEY_RIGHT) {
        cameraFrame.RotateWorld(m3dDegToRad(-2.5f), 0.0f, 1.0f, 0.0f);
    }
}
void keyboardPress(unsigned char key, int x, int y) {
    
}

//MARK:- main函数
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("sphere world");
    
    glutReshapeFunc(changeSize);
    glutDisplayFunc(renderScene);
    glutSpecialFunc(specialKey);
    
    GLenum status = glewInit();
    if (status != GLEW_OK) {
        SSPrintf("glew error:%s", glewGetErrorString(status));
        return 1;
    }
    
    setupRC();
    
    glutMainLoop();
    
    return 0;
}

如果需要调试,可以把代码放在OpenGL环境下的项目中。

球体世界.gif

示例解析

demo中声明了两个矩阵堆栈对象

GLMatrixStack modelViewMatrix;//模型视图矩阵堆栈
GLMatrixStack projectMatrix;//透视投影矩阵堆栈

changeSize函数中设置了这两个矩阵堆栈的初值。

//设置投影视图
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1, 100);
//在透视投影矩阵堆栈中 压入 投影矩阵
projectMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); 

//在模型矩阵堆栈中 压入 单元矩阵
modelViewMatrix.LoadIdentity();//这句可有可无

矩阵的压栈出栈主要是在renderScene中,这里摘关键代码,完整示例代码可以用来调试。

    //1.观察者矩阵入栈
    //模型视图矩阵堆栈栈顶(单元矩阵0) copy后压入 一个单元矩阵
    modelViewMatrix.PushMatrix();
    //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1)
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    //模型视图矩阵堆栈栈顶(单元矩阵1) 乘以 观察者矩阵
    modelViewMatrix.MultMatrix(mCamera);
    //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵)
    
    //画地板
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 floorColor);
    floorBatch.Draw();
    
    //2.大球矩阵管理
    //模型视图矩阵堆栈栈顶(单元矩阵1 * 观察者矩阵) 乘以 大球平移矩阵
    modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
    //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵)
    //模型视图矩阵堆栈栈顶(单元矩阵1 * 观察者矩阵 * 大球平移矩阵) copy后压入 (单元矩阵2 * 观察者矩阵 * 大球平移矩阵)
    modelViewMatrix.PushMatrix();
    //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵, 单元矩阵2 * 观察者矩阵 * 大球平移矩阵)
    //模型视图矩阵堆栈栈顶(单元矩阵2 * 观察者矩阵 * 大球平移矩阵) * 大球旋转矩阵
    modelViewMatrix.Rotate(m3dDegToRad(angle), 0.0f, 1.0f, 0.0f);
    //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵, 单元矩阵2 * 观察者矩阵 * 大球平移矩阵 * 大球旋转矩阵)
    
    M3DMatrix44f lightPosition = {0.0f, 10.0f, 5.0f, 1.0f};
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,
                                 transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(),
                                 lightPosition,
                                 sphereBigColor);
    sphereBigBatch.Draw();
    
    //模型视图矩阵堆栈栈顶(单元矩阵2 * 观察者矩阵 * 大球平移矩阵 * 大球旋转矩阵) 出栈
    modelViewMatrix.PopMatrix();
    //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵)
    
    //3.小球矩阵管理
    for (int i = 0; i < kSphereSmakkNum; i++) {
        //模型视图矩阵堆栈栈顶(单元矩阵1 * 观察者矩阵 * 大球平移矩阵) copy后压入 (单元矩阵2 * 观察者矩阵 * 大球平移矩阵)
        modelViewMatrix.PushMatrix();
        //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵, 单元矩阵2 * 观察者矩阵 * 大球平移矩阵)
        //模型视图矩阵堆栈栈顶(单元矩阵2 * 观察者矩阵 * 大球平移矩阵) 乘以 小球矩阵
        modelViewMatrix.MultMatrix(sphereSmallFrame[I]);
        //结果://结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵, 单元矩阵2 * 观察者矩阵 * 大球平移矩阵 * 小球矩阵)
        
        shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,
                                     transformPipeline.GetModelViewMatrix(),
                                     transformPipeline.GetProjectionMatrix(),
                                     lightPosition,
                                     sphereSmallColor);
        sphereSmallBatch.Draw();
        
        //模型视图矩阵堆栈栈顶(单元矩阵2 * 观察者矩阵 * 大球平移矩阵 * 小球矩阵) 出栈
        modelViewMatrix.PopMatrix();
        //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵)
    }
    
    //4.公转小球矩阵管理
    //模型视图矩阵堆栈栈顶(单元矩阵1 * 观察者矩阵 * 大球平移矩阵) 乘以 公转小球旋转矩阵
    modelViewMatrix.Rotate(angle * -2.0f, 0.0f, 1.0f, 0.0f);
    //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵 * 公转小球旋转矩阵)
    //模型视图矩阵堆栈栈顶(单元矩阵1 * 观察者矩阵 * 大球平移矩阵 * 公转小球旋转矩阵) 乘以 公转小球平移矩阵
    modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
    //结果:模型视图矩阵堆栈(单元矩阵0,单元矩阵1 * 观察者矩阵 * 大球平移矩阵 * 公转小球旋转矩阵 * 公转小球平移矩阵)
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,
                                 transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(),
                                 lightPosition,
                                 sphereSmallColor);
    sphereSmallBatch.Draw();
    
    //1.观察矩阵出栈
    //模型视图矩阵堆栈栈顶(单元矩阵1 * 观察者矩阵 * 大球平移矩阵 * 公转小球旋转矩阵 * 公转小球平移矩阵) 出栈
    modelViewMatrix.PopMatrix();//pop观察者
    //结果:模型视图矩阵堆栈(单元矩阵0)

注释看起来有点啰嗦,还是画张简陋的图来说明吧。最后公转小球的部分就不画了,没那么大的纸。

矩阵的堆栈管理

相关文章

  • 矩阵堆栈管理

    矩阵堆栈管理 我们在使用OpenGL渲染时,通常需要展现出一些动画效果,每种动画都离不开基本的形变方式:平移、旋转...

  • OpenGL: 使用变换管线管理矩阵堆栈,绘制各种图形

    penGL: 使用变换管线管理矩阵堆栈,绘制各种图形

  • 矩阵堆栈

    矩阵堆栈的作用? 矩阵堆栈只是保存变化的状态。 GLMatrixStack::GLMatrixStack(int ...

  • OpenGL学习之路(5.3) 矩阵堆栈的了解和使用方法

    什么是矩阵堆栈? OpenGL的矩阵堆栈指的就是内存中专门用来存放矩阵数据的某块特殊区域。一般说来,矩阵堆栈常用于...

  • OpenGL利用矩阵堆栈绘制图形

    什么是矩阵堆栈 矩阵堆栈指的就是内存中专门用来存放矩阵数据的某块特殊区域。一般说来,矩阵堆栈常用于构造具有继承性的...

  • 矩阵堆栈操作

    顶点变化管线图中,我们需要对模型视图矩阵和投影矩阵进行设置或者变换。 矩阵堆栈 本质还是堆栈,只是存储着矩阵。#i...

  • OpenGL绘制地板

    1:GLMatrixStack (矩阵)堆栈的使用 GLMatrixStack,初始化时已经在堆栈中包含了单位矩阵...

  • OpenGL矩阵堆栈

    OpenGL的矩阵堆栈指的就是内存中专门用来存放矩阵数据的某块特殊区域。一般说来,矩阵堆栈常用于构造具有继承性的模...

  • 视频特效学习04-OpenGL基础变化

    学习目标: 向量、矩阵和基础变化(了解) 使用矩阵/向量移动几何图形(实践) 矩阵堆栈(理解) 1. 向量与矩阵 ...

  • OpenGL 矩阵操作

    矩阵堆栈 //类型GLMatrixStack::GLMatrixStack(int iStackDepth = 6...

网友评论

      本文标题:矩阵堆栈管理

      本文链接:https://www.haomeiwen.com/subject/hkqgqctx.html