9、OpenGL - 金字塔(无底)、矩阵堆栈

OpenGL 金字塔 与 矩阵堆栈

 

详细代码参见Demo 

Demo地址 -> OpenGLDemos -> 2.pyramid

 

代码解析:

头文件

#include 
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"

#include 
#ifdef __APPLE__
#include 
#else
#define FREEGLUT_STATIC
#include 
#endif

定义变量

//着色器管理类
GLShaderManager     shaderManager;
//模型视图矩阵堆栈
GLMatrixStack       modelViewMatrix;
//投影矩阵堆栈
GLMatrixStack       projectionMatrix;
//照相机 参数帧
GLFrame             cameraFrame;
//模型 参考帧
GLFrame             objectFrame;
//投影矩阵
GLFrustum           viewFrustum;

//容器类 对应GL_TRIANGLES(三角形) 图元类型
GLBatch             triangleBatch;

//几何变换的管道
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;

 

1、设置视口的大小

void ChangeSize(int w, int h)

  • 1、设置视口大小
  • 2、创建投影矩阵
  • 3、将投影矩阵载入投影堆栈
  • 4、将单元矩阵载入模型视图矩阵堆栈

ChangeSize 为重塑方法,当第一次创建窗口或者窗口改变时候系统调整,主要在该方法中使用窗口维度设置视口和投影矩阵。

   GLFrustum类通过setPerspective ⽅方法为我们构建⼀个平截头体。
   CLFrustum::SetPerspective(float fFov,float fAspect,float fNear ,float fFar);
   参数:
       fFov:垂直⽅向上的视场⻆度
       fAspect:窗口的宽度与高度的纵横比     
       fNear:近裁剪⾯距离 (视角到近裁剪面距离为fNear)
       fFar:远裁剪面距离(视角到远裁剪面距离为fFar)
       纵横比 = 宽(w)/⾼(h)

//设置视口的大小等
void ChangeSize(int w, int h)
{
//    1、设置视口大小
    glViewport(0, 0, w, h);
//    2、创建投影矩阵,并将它载入投影矩阵堆栈中
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
//    3、将投影矩阵载入投影堆栈
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());

//    将单元矩阵载入模型视图矩阵堆栈
    modelViewMatrix.LoadIdentity();
}

 

2、进行必要初始化

void SetupRC()

此函数在呈现上下文中进行任何必要的初始化
这是第一次或任何与openGL 相关的任务

 

  • 1、设置背景色
  • 2、初始化着色器
  • 3、启动深度测试,不开启的话看到的黑色边线会透视过来(如图)
  • 4、设置变换管线以使用两个矩阵堆栈
  • 5、将相机向前移动15个单元
  • 6、通过三角形创建金字塔
  • 7、每3个顶点定义一个新的三角形
  • 8、复制顶点数据批量处理三角形
  • 4、结束三角形批量处理

SetupRC 是在 main 方法中手动调用,且只执行一次,主要是做着色器初始化、窗口背景初始化、顶点数据的准备等工作,在这个案例中,此方法主要是初始化了绘制金字塔需要的顶点数据。

//此函数在呈现上下文中进行任何必要的初始化
//这是第一次或任何与openGL 相关的任务
void SetupRC()
{
//    1、灰色的背景
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
//    2、初始化着色器
    shaderManager.InitializeStockShaders();
    
//    3、启动深度测试,不开启的话看到的黑色边线会透视过来(如图)
    glEnable(GL_DEPTH_TEST);
//    4、设置变换管线以使用两个矩阵堆栈
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
//    5、将相机向前移动15个单元
    cameraFrame.MoveForward(-15.0f);

/*
 常见函数
 void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
       参数1:表示使用的图元
       参数2:顶点数
       参数3:纹理坐标(可选)

      //负责顶点坐标
      void GLBatch::CopyVertexData3f(GLFloat *vNorms);

      //结束,表示已经完成数据复制工作
      void GLBatch::End(void);
 */
//    6、通过三角形创建金字塔
    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();
}

一定要记得开启深度测试,要黑色线条会透过去的。效果如下

glEnable(GL_DEPTH_TEST);

未开启深度测试

9、OpenGL - 金字塔(无底)、矩阵堆栈_第1张图片

开启深度测试

9、OpenGL - 金字塔(无底)、矩阵堆栈_第2张图片

 

3、渲染场景

void RenderScene(void)

  • 1、清除用到的缓冲区,如果不确定,就清除所有缓冲区(如有需要,做矩阵压栈、矩阵计算逻辑)
  • 2、压栈
  • 3、获取相机矩阵
  • 4、矩阵相乘,结果存入栈顶
  • 5、设置绘图颜色
  • 6、选定需要使用的着色器,进行绘制(若已做矩阵压栈,需要做相应的出栈操作)
  • 7、还原到模型视图矩阵
  • 8、交换缓冲区

RenderScene 同 ChangeSize 一样,需要再main 函数里注册通知,当屏幕发生变化或者通过调用glutPostRedisplay 方法,从而让系统调用。
在此方法中,主要通过模型视图堆栈,做了一系列的矩阵计算,从而得到一个新的模型视图堆栈。最后通过渲染管线获取到模型视图矩阵以及投影矩阵,作为固定着色器中几何图形变换的变换矩阵。

void RenderScene(void)
{
//    用当前清除颜色清除窗口
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//    压栈
    modelViewMatrix.PushMatrix();
//    相机矩阵
    M3DMatrix44f mCanera;
    cameraFrame.GetCameraMatrix(mCanera);

//    矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后存入栈顶
    modelViewMatrix.MultMatrix(mCanera);

    M3DMatrix44f mObjectFrame;
//    只要使用 GetMatrix 函数就可以获取矩阵堆栈栈顶的值,这个函数可以进行2次重载用来使用GLShaderManager 的使用。或者是获取顶部矩阵的顶点副本数据
    objectFrame.GetMatrix(mObjectFrame);

//    矩阵乘以矩阵堆栈栈顶的矩阵,相乘的结果存储在栈顶
    modelViewMatrix.MultMatrix(mObjectFrame);

    /* GLShaderManager 中的Uniform 值——平面着色器
    参数1:平面着色器
    参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
    --transformPipeline.GetModelViewProjectionMatrix() 获取的
    GetMatrix函数就可以获得矩阵堆栈顶部的值
    参数3:颜色值(黑色)
    */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);

    DrawWireFramedBatch(&triangleBatch);

//    还原到以前的模型视图矩阵(单位矩阵)
    modelViewMatrix.PopMatrix();

//    进行缓冲区交换
    glutSwapBuffers();
}


4、转动、翻转

void SpecialKeys(int key, int x, int y)

//通过上下左右键控制翻转

GLUT_KEY_UP(上)

GLUT_KEY_DOWN(下)

GLUT_KEY_LEFT(左)

GLUT_KEY_RIGHT(右)

m3dDegToRad(x)每次翻转x/180度

#define m3dDegToRad(x)    ((x)*M3D_PI_DIV_180)

 设置X Y Z 值,去乘以fAngle 得到的是相应变换后的值

void RotateWorld(float fAngle, float x, float y, float z)

例如  objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);  每次调用向上翻转5/180 度

看一下效果

9、OpenGL - 金字塔(无底)、矩阵堆栈_第3张图片


二、矩阵堆栈

在上述的RenderScene()方法中

modelVIewMatrix.PushMatrix()

modelViewMatrix.PopMatrix()

这样的代码存在,这就是压栈与出栈,
矩阵压栈出栈的就是利用栈特性,先进后出。从栈顶入栈,从栈顶出栈。也就是说我们的矩阵是临时存入栈中,在用完需要移出。

 

1、PushMatrix()
压栈:和数据结构中的栈类似,调用这个方法的时候,若传入一个矩阵,则将该矩阵压入栈顶;若不传入参数,则默认 copy 一份当前栈顶矩阵,压入栈顶,主要作用就是记录状态,保存当时的临时结果。

2、MultMatrix(x)
矩阵相乘,将栈顶元素copy 一份,并于 矩阵相乘,得到结果赋值给矩阵堆栈的栈顶矩阵。

3、PopMatrix()
矩阵出栈:将栈顶矩阵出栈,恢复为原始的矩阵堆栈
在使用完该栈之后,这个方法必须调用。原因就是,之前有调大,OpenGL上下文本身就是一个巨大的状态机,若在这里不将栈顶矩阵出栈,对于在其他使用到该矩阵堆栈的地方来说,矩阵数据就会错乱。

 

参阅

1、案例 03:金字塔、六边形、圆环的绘制

 

 

你可能感兴趣的:(OpenGL)