九、OpenGL案例2 -- 正方形键盘控制(坐标更新)

2020-07-13  本文已影响0人  东篱采桑人

今天来学习用OpenGL绘制一个正方形,并通过键盘方向键移动这个正方形。在绘制三角形案例中我们已知道如何通过OpenGL绘制一个图形,这里只需要再注册一个键盘方向键事件的回调函数,在回调函数里改变顶点坐标然后重绘就可以了。

一、 绘制思路

OpenGL移动正方形(坐标更新)

二、 代码实现

1. 准备工作
#include "GLTools.h"
#include <GLUT/GLUT.h>

GLBatch triangleBatch;
GLShaderManager shaderManger;

//正方形边长的一半
GLfloat blockSize = 0.1f;

//顶点坐标,标准化设备坐标范围(-1.0, 1.0)
//这里采用GL_TRIANGLE_FAN图元装配方式,只传入4个顶点,左下角设为第一个顶点
GLfloat vVerts[] = {
    
    -blockSize, -blockSize, 0,
    blockSize, -blockSize, 0,
    blockSize, blockSize, 0,
    -blockSize, blockSize, 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库
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库
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.4f, 0.7f, 1);

    //利用三角形批次对象将数据拷贝到上下文,这里选择GL_TRIANGLE_FAN图元装配方式
    triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
    triangleBatch.CopyVertexData3f(vVerts);
    triangleBatch.End();

    //初始化着色管理器
    shaderManager.InitializeStockShaders();
    //设置单元着色器,颜色为红色
    GLfloat vRed[] = {1.0f, 0.0f, 0.0f, 1.0f};
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
}
6. 实现窗口事件回调函数

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

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

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

void RenderScene(void){

    /*
     清除一个或一组特定的缓冲区,当不确定需要清除哪个时,就全部清除掉:
     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);
    //开始渲染
    triangleBatch.Draw();
    //渲染完后交换前后台缓冲区,实现双缓冲机制
    glutSwapBuffers();
}

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

void SpecialKeys(int key, int x, int y){
    
    //设定每次移动距离
    GLfloat stepSize = 0.025f;
    
    //计算第一个点移动后的坐标,再根据相对距离更新其他点的坐标
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[1];
    switch (key) {
        case GLUT_KEY_UP:     //向上方向键
            blockY += stepSize;
            break;
        case GLUT_KEY_DOWN:   //向下方向键
            blockY -= stepSize;
            break;
        case GLUT_KEY_LEFT:   //向左方向键
            blockX -= stepSize;
            break;
        case GLUT_KEY_RIGHT:   //向右方向键
            blockX += stepSize;
            break;
            
        default:
            break;
    }
    
    //判断是否超出边界
    if(blockX < -1.0){
        
        blockX = -1.0;
    }
    else if(blockX > 1.0 - blockSize * 2.f){
        
        blockX = 1.0 - blockSize * 2;
    }
    if(blockY < -1.0){
        
        blockY = -1.0;
    }
    else if(blockY > 1.0 - blockSize * 2){
        
        blockY = 1.0 - blockSize * 2;
    }
    
    //更新顶点坐标
    vVerts[0] = blockX;
    vVerts[1] = blockY;
    vVerts[3] = blockX + blockSize * 2;
    vVerts[4] = blockY;
    vVerts[6] = blockX + blockSize * 2;
    vVerts[7] = blockY + blockSize * 2;
    vVerts[9] = blockX;
    vVerts[10] = blockY + blockSize * 2;
    
    //利用批次类更新上下文中的顶点数据
    triangleBatch.CopyVertexData3f(vVerts);
    
    //重绘
    glutPostRedisplay();
}
7. 开启窗口运行循环

GLUT内部会运行一个本地消息循环,需要手动开启后,才能处理窗口消息事件。

glutMainLoop();

三、程序运行效果

移动正方形.gif

扩展阅读

1. OpenGL在MAC上的配置
2. OpenGL入门案例 -- 绘制三角形

上一篇下一篇

猜你喜欢

热点阅读