OpenGL-案例-矩阵压栈绘制

OpenGL-案例-环境搭建

导入头文件,定义变量

#include <stdio.h>
#include <math.h>
#include "GLTools.h"
#include "GLBatch.h"
#include "GLShaderManager.h"
#include <GLUT/GLUT.h>
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"

//着色管理器
GLShaderManager shaderManager;
//模型视图矩阵堆栈
GLMatrixStack modelViewStack;
//投影矩阵堆栈
GLMatrixStack projectionStack;
//观察者矩阵管理器
GLFrame cameraFrame;
//物体矩阵管理器
GLFrame objectFrame;
//投影矩阵
GLFrustum viewFrustum;
//简单批次类
GLBatch triangleBatch;
//几何变换的管道
GLGeometryTransform transformPipeline;
//填充颜色
GLfloat vGreen[] = {0.f, 1.f, 0.f, 1.f};
//边框颜色
GLfloat vBlack[] = {0.f, 0.f, 0.f, 1.f};

实现setupRC函数

void setupRC() {
    //设置清屏颜色(背景颜色)
    glClearColor(0.7f, 0.7f, 0.7f, 1.f );
    //初始化着色器管理器
    shaderManager.InitializeStockShaders();
    
    //设置变换管道以使用两个矩阵堆栈
    transformPipeline.SetMatrixStacks(modelViewStack, projectionStack);
    //观察者想屏幕上方移动15
    cameraFrame.MoveForward(-15.f);
    
    //通过三角形创建金字塔
    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,12个顶点
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
}

transformPipeline: 几何变换管道GLGeometryTransform对象,是用来管理物体几何变换的,通过它可以进行矩阵的快速相乘。
SetMatrixStacks(GLMatrixStack& mModelView, GLMatrixStack& mProjection): 为变换管道设置模型视图矩阵堆栈和投影矩阵堆栈。后面就可以通过函数来获取和管理这两个堆栈了。
MoveForward(float fDelta): 向前移动,代码中表示观察者向前移动-15,其实就是向后(垂直屏幕向外)移动15。类似函数还有MoveUp(向上移动)、MoveRight(向右移动)。

实现reshapeFunc函数

void reshapeFunc(int w,int h) {
    //设置视口
    glViewport(0, 0, w, h);
    
    //创建投影矩阵
    viewFrustum.SetPerspective(35.f, float(w)/float(h), 1.f, 500.f);
    //加载投影矩阵
    projectionStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //调用顶部载入单元矩阵
    modelViewStack.LoadIdentity();
}

viewFrustum通过调用SetPerspective来设置投影方式为透视投影(这里渲染金字塔为立方体,所以为透视投影)。
SetPerspective(float fFov, float fAspect, float fNear, float fFar): 设置投影方式为正投影。参数含义为:

  • fFov: 垂直⽅向上的视角⻆度。也就是眼睛的打开角度。
  • fAspect: 窗口的宽高比。
  • fNear: 近裁剪⾯距离(视角到近裁剪面距离为fNear)。
  • fFar: 远裁剪面距离(视角到远裁剪面距离为fFar)。
    在这里插入图片描述
    由于是立体物体形态变换,有投影变换和物体的变换两种变换,所以这里调用了投影矩阵堆栈projectionStack和模型视图矩阵堆栈modelViewStack的加载函数。
    GetProjectionMatrix(): 获取投影矩阵。
    LoadMatrix(const M3DMatrix44f mMatrix): 将投影矩阵加载在投影矩阵堆栈(projectionMatrix)中。
    LoadIdentity(void): 加载单元矩阵,单元矩阵相当于数字1,任何矩阵乘以单元矩阵都等于本身。(这里由于不能确定模型视图矩阵堆栈modelViewMatrix有什么变化,所以让他乘以单元矩阵)。

实现displayFunc函数

void displayFunc(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //压栈
    modelViewStack.PushMatrix();
    
    M3DMatrix44f cameraMatrix;
    //将观察者矩阵存入cameraMatrix中
    cameraFrame.GetCameraMatrix(cameraMatrix);
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后将存储在堆栈的顶部
    modelViewStack.MultMatrix(cameraMatrix);
    
    M3DMatrix44f objectMatrix;
    //将物体矩阵存放在objectMatrix中
    objectFrame.GetMatrix(objectMatrix);
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后简存储在堆栈的顶部
    modelViewStack.MultMatrix(objectMatrix);
    
    //通过MVP设置平面着色器
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    triangleBatch.Draw();
    
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    //偏移深度,在同一位置要绘制填充和边线,会产生z冲突,所以要偏移
    glPolygonOffset(-1.0f, -1.0f);
    //开启深度偏移,偏移值通过glPolygonOffset设置
    glEnable(GL_POLYGON_OFFSET_LINE);
    //开启线段锯齿过滤
    glEnable(GL_LINE_SMOOTH);
    //开启颜色混合
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //设置正面和背面都为线框渲染模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //设置线条宽度
    glLineWidth(2.5f);

    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    triangleBatch.Draw();

    //关闭深度测试
    glDisable(GL_DEPTH_TEST);
    //关闭深度偏移
    glDisable(GL_POLYGON_OFFSET_LINE);
    //关闭线段锯齿过滤
    glDisable(GL_LINE_SMOOTH);
    //关闭颜色混合
    glDisable(GL_BLEND);
    //恢复正面和背面都为填充渲染模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    //恢复线条宽度
    glLineWidth(1.0f);
    
    //还原到以前的模型视图矩阵(单位矩阵)
    modelViewStack.PopMatrix();
    
    // 进行缓冲区交换
    glutSwapBuffers();
}
  1. 调用模型视图堆栈modelViewMatrix的入栈PushMatrix(),入栈的作用是为了记录状态,在绘制完成后可以回退回去。这里modelViewMatrix原本栈顶就有一个单元矩阵,执行PushMatrix()后又压入一个单元矩阵。
  2. 先后将观察者矩阵和物体矩阵乘入模型视图堆栈的栈顶,其结果就是栈顶存储的矩阵变成了观察者矩阵和物体矩阵相乘的结果。
  3. 设置完模型视图矩阵后,就可以通过模型视图矩阵和投影矩阵来设置平面着色器并调用批次类triangleBatch的绘制函数Draw()进行绘画了,这里通过变化管道transformPipeline调用函数GetModelViewProjectionMatrix()可以直接获取到这两个矩阵相乘的结果。所说的MVP就是指Model、View、Projection,这里面包含了模型视图矩阵(观察者矩阵和物体变化矩阵点乘的结果)和投影矩阵。
  4. 绘画边框时容易出现锯齿等现象,所以通过glEnable、glLineWidth等做一些优化设置。重要的是设置正背面都要设置为线框渲染模式。
  5. 和第3步绘画金字塔内容一样调用绘制,只不过颜色是vBlack,而且第4步已经将正面和背面都设置为线框渲染模式,所以渲染出来后会显示黑色的线,连接各个顶点。
  6. 第4步的设置是通过上下文设置的,是整个项目生效的,所以绘制完成后要进行恢复,避免影响到其他绘制。
  7. 最后调用模型视图堆栈modelViewMatrix的出栈PopMatrix()将堆栈还原。

实现specialFunc函数

void specialFunc(int key, int x, int y) {
    if (key == GLUT_KEY_LEFT) {
        objectFrame.RotateWorld(m3dDegToRad(-5.f), 0.f, 1.f, 0.f);
    } else if (key == GLUT_KEY_RIGHT) {
        objectFrame.RotateWorld(m3dDegToRad(5.f), 0.f, 1.f, 0.f);
    } else if (key == GLUT_KEY_UP) {
        objectFrame.RotateWorld(m3dDegToRad(-5.f), 1.f, 0.f, 0.f);
    } else if (key == GLUT_KEY_DOWN) {
        objectFrame.RotateWorld(m3dDegToRad(5.f), 1.f, 0.f, 0.f);
    }
    glutPostRedisplay();
}
  • objectFrame是用来管理物体平移、旋转等形态转换的矩阵的。objectFrame调用RotateWorld函数后,其内部管理的矩阵数据会刷新为转换后的数据。
  • RotateWorld表示围绕世界坐标系旋转,第一个参数表示逆/顺时针旋转5度,后面三个参数表示x、y、z轴,围绕哪个轴旋转,对应参数传1,否则传0。

流程总结

  • setupRC中设置变化管道、观察者位置、顶点数据、批次类;给变化管道(transformPipeline)设置模型视图堆栈和投影堆栈分别为modelViewStack和projectionStack。
  • reshapeFunc中设置透视投影方式,根据窗口创建投影矩阵,然后加载投影堆栈(projectionStack)和模型视图堆栈(modelViewStack)。
  • displayFunc中首先给物体堆栈(modelViewStack)压栈一个单元矩阵,然后分别将观察者矩阵和物体矩阵点乘到栈顶,最后分别绘制金字塔和边框。
  • specialFunc中监听到上下左右按键时,通过物体矩阵管理器(objectFrame)修改物体矩阵数据,然后提交更新绘制,即重新调用displayFunc进行绘制。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值