欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

X.OpenGL 案例 3 -- 方形键盘控制(矩阵)

最编程 2024-04-13 14:37:48
...

正方形键盘控制(坐标更新)一文中,我们通过计算各顶点移动后的坐标来实现图形的移动,今天我们换个方式,使用变换矩阵来移动正方形。实现过程和坐标更新方式大体一致,不同之处主要在于以下3点:

  • 因为需要应用变换矩阵,所以使用平面着色器来绘制。
  • 每次方向键输入后,计算图形中心点的坐标。
  • 在内容显示回调方法里再进行矩阵计算,然后渲染。

一、绘制思路

OpenGL - 正方形控制(矩阵)

二、代码实现

1. 准备工作
  • 引入"GLTools.h"和<GLUT/GLUT.h>这两个文件;
  • 声明一个GLBatch和一个GLShaderManager对象,设置相关属性。
  • 声明窗口重塑事件、内容显示事件、方向键输入事件的回调函数。
#include "GLTools.h"
#include <GLUT/GLUT.h>

GLBatch triangleBatch;
GLShaderManager shaderManger;

//正方形边长的一半
GLfloat blockSize = 0.1f;
//图形中心点坐标
GLfloat centerX = 0;
GLfloat centerY = 0;

//窗口重塑回调函数
void ChangeSize(int w, int h){}
//内容显示回调函数
void RenderScene(void){}
//方向键输入事件
void SpecialKeys(int key, int x, int y){}
2. 设置当前工作目录

glSetWorkingDrectory是GLTools里的函数,用来设置当前工作目录,针对于macOS X。因为在Windows中工作目录默认就是与程序可执行程序相同的目录。但是在macOS X中,程序会将当前工作文件夹改为应用程序捆绑包中的/Resource文件夹。

PS:暂时还不知道这个设置工作目录有什么作用,因为在macOS X上注释掉这一行代码,也是可以正常渲染。只能留待后续解惑,读者中如果有知道的话,请留言告知。

gltSetWorkingDirectory(argv[0]);
3. 设置窗口

这里使用glut库实现窗口相关设置,glutSpecialFunc()函数可以监听特定按键的输入事件,包括:功能键、方向键和 PAGE-UP / PAGE-DOWN / HOME/END / INSERT 键。

在设置窗口显示模式时,其中GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区*:

  • GLUT_DOUBLE:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后迅速转换成窗口视图。这种方式可以避免渲染动画时帧与帧之间的闪烁情况。
  • GLUT_DEPTH:标志将一个深度缓存区分配为显示的一部分,因此我们能够执行深度测试;
  • GLUT_STENCIL:确保我们也会有一个可用的模板缓存区。
//初始化glut库
glutInit(&argc, argv);
//设置窗口显示模式(开启双缓冲机制、RGBA颜色模式、深度测试、模板缓冲区)
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
//设置窗口大小
glutInitWindowSize(500, 500);
//创建窗口,设置窗口标题
glutCreateWindow("Triangle");
//注册事件回调函数
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);   //方向键输入事件
4. 初始化glew库
  • 初始化glew库:glew库能够识别当前平台支持的OpenGL API,确保OpenGL API对程序完全可用。
  • 判断初始化结果:在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题。
GLenum err = glewInit();
if(err != GLEW_OK){
    
    fprintf(stderr, "glew error:%s\n", glewGetErrorString(err));
    return 1;
}
5. 设置渲染相关信息

这里进行重置背景色、批次处理图形顶点数据以及初始化着色器操作。

void SetupRC(void){
    
    //重置背景色
    glClearColor(0.98f, 0.40f, 0.7f, 1);

   //设置图形顶点数据,左下角设为第一个顶点
    GLfloat vVerts[] = {

        -blockSize, -blockSize, 0,
        blockSize, -blockSize, 0,
        blockSize, blockSize, 0,
        -blockSize, blockSize, 0,
    };

    //利用三角形批次对象将数据拷贝到上下文
    triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
    triangleBatch.CopyVertexData3f(vVerts);
    triangleBatch.End();

    //初始化着色管理器
    shaderManager.InitializeStockShaders();
}
6. 实现窗口事件回调函数

6.1 重塑事件回调函数
重塑事件是在窗口第一次显示或窗口大小发生改变时触发。
在回调函数里设置视口大小和窗口大小一致,投影方式默认为正投影。

void ChangeSize(int w, int h){
    
    //0,0代表窗口中视口的左下角坐标,w,h代表像素
    glViewport(0, 0, w, h);
}

6.2 内容显示事件回调函数
内容显示事件在重塑事件发生后或主动重绘内容时触发,因此在回调函数里执行图形渲染,处理如下:

  • 清空缓存区:缓冲区是一块存有图像信息的存储空间,在每次渲染前都需要清空缓存区。
  • 矩阵计算:矩阵相乘,实现平移和旋转的效果。
  • 设置平面着色器:因为需要应用变换矩阵,所以使用平面着色器。
  • 开始渲染:通过三角形批次类发起渲染。
  • 交换前后台缓冲区:在前面设置openGL窗口的时候,指定了一个双缓冲区的渲染环境,使得图形在后台缓冲区渲染,所以在渲染完后需要手动交互缓冲区,交由前台显示。
//内容显示回调函数
void RenderScene(void){

    //清除一个或一组特定的缓冲区,当不确定需要清除哪个时,就全部清除掉。
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)

   //声明3和矩阵,平移矩阵,旋转矩阵,相乘结果矩阵
    M3DMatrix44f mFinalTransform, mTransfromMatrix, mRotationMatrix;
    //设置平移矩阵
    m3dTranslationMatrix44(mTransfromMatrix, centerX, centerY, 0);
    //设置旋转矩阵,每次平移时绕z轴旋转5度
    static float yRot = 0.0f;
    yRot += 5.f;
    m3dRotationMatrix44(mRotationMatrix, m3dDegToRad(yRot), 0, 0, 1.0);
    //合并两个矩阵(矩阵相乘)
    m3dMatrixMultiply44(mFinalTransform, mTransfromMatrix, mRotationMatrix);
    
    //设置平面着色器
    GLfloat vRed[] = {1.0f, 0.0f, 0.0f, 0.0f};
    shaderManager.UseStockShader(GLT_SHADER_FLAT, mFinalTransform, vRed);
    
    //开始渲染
    triangleBatch.Draw();
    //渲染完后交换前后台缓冲区,实现双缓冲机制
    glutSwapBuffers();
}

6.3 方向键输入事件回调函数
这里主要实现每次方向键输入后移动正方形,处理如下:

  • 设置每一步的移动距离。
  • 判断输入键方向,计算中心点移动后的坐标。
  • 碰撞检查,防止移动到窗外。
  • 发起重绘。
void SpecialKeys(int key, int x, int y){
    
    //设定每次移动距离
    GLfloat stepSize = 0.025f;
    
    //计算中心点移动后的坐标
    switch (key) {
        case GLUT_KEY_UP:     //向上方向键
            centerY += stepSize;
            break;
        case GLUT_KEY_DOWN:   //向下方向键
            centerY -= stepSize;
            break;
        case GLUT_KEY_LEFT:   //向左方向键
            centerX -= stepSize;
            break;
        case GLUT_KEY_RIGHT:   //向右方向键
            centerX += stepSize;
            break;
             
        default:
            break;
    }
    
    //判断是否超出边界
    if(centerX < -1.0 + blockSize){
        
        centerX = -1.0 + blockSize;
    }
    else if(centerX > 1.0 - blockSize){
        
        centerX = 1.0 - blockSize;
    }
    if(centerY < -1.0 + blockSize){
        
        centerY = -1.0 + blockSize;
    }
    else if(centerY > 1.0 - blockSize){
        
        centerY = 1.0 - blockSize;
    }
    
    //重绘
    glutPostRedisplay();
}

三、程序运行效果

扩展阅读:

1. OpenGL在MAC上的配置
2. OpenGL案例2 -- 正方形键盘控制(坐标更新)