OpenGL之控制渲染图形的移动

图形的渲染显示

  • 以正方形为例:正方形的绘制,与三角形的绘制类似。基于三角形的绘制,只需要将绘制三角形的部分代码修改,即可实现正方形的绘制;(三角形的绘制见上篇文章:OpenGL简单渲染一个三角形
  • 定义正方形顶点到原点的距离,即正方形的边长 = blockSize * 2;
    // 图形顶点到原点的距离
    GLfloat blockSize = 0.1f;
  • 修改定点数组,并设置图元的连接方式和顶点个数;
    // 设置正方形顶点,其中数组vVerts包含所有4个顶点的x,y,z笛卡尔坐标对
    GLfloat vVerts[] = {-blockSize, -blockSize, 0.0f,
                        blockSize, -blockSize, 0.0f,
                        blockSize, blockSize, 0.0f,
                        -blockSize, blockSize, 0.0f};
    // 批次处理,将数据传递到着⾊色器器
    triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
  • 至此,一个完整的正方形就绘制完成了。附上完整的正方形绘制代码:
#include "GLShaderManager.h"
#include "GLTools.h"
#include <GLUT/GLUT.h>

// 着色器
GLShaderManager shadermanager;
// 批次容器
GLBatch triangleBatch;
// 正方形的边长
GLfloat blockSize = 0.1f;


/// 窗口大小改变时接受新的宽度和高度
/// 触发条件: 1.新建窗口  2.窗⼝尺⼨发生调整
/// 处理理业务: 1.设置OpenGL 视⼝  2. 设置OpenGL 投影⽅式等
/// @param w 像素
/// @param h 像素
void changeWindowSize(int w, int h) {
    glViewport(0, 0, w, h);
}

void setupRC() {
     glClearColor(0.90f, 0.40f, 0.0f, 1.0f);
    shadermanager.InitializeStockShaders();
    GLfloat vVerts[] = {-blockSize, -blockSize, 0.0f,
                        blockSize, -blockSize, 0.0f,
                        blockSize, blockSize, 0.0f,
                        -blockSize, blockSize, 0.0f};
    triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
    triangleBatch.CopyVertexData3f(vVerts);
    triangleBatch.End();
}

void renderScene(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    GLfloat colorRed[] = {1.0f, 0.0f, 0.0f, 1.0f};
    shadermanager.UseStockShader(GLT_SHADER_IDENTITY, colorRed);
    
    triangleBatch.Draw();
    glutSwapBuffers();
}

int main(int argc, char *argv[]) {
    
    gltSetWorkingDirectory(argv[0]);
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
    
    glutInitWindowSize(400, 400);
    glutCreateWindow("正方形的移动");
    
    glutReshapeFunc(changeWindowSize);
    glutDisplayFunc(renderScene);
    
    GLenum statue = glewInit();
    if (GLEW_OK != statue) {
        fprintf(stderr,"glew error:%s\n",glewGetErrorString(statue));
        return 1;
    }
    
    setupRC();
    glutMainLoop();
    return 0;
}

图形的移动控制

  • 在main函数中需要注册特殊键位响应,在specialKeys函数中实现移动的逻辑;
    //  特殊键位触发(方向键)
    glutSpecialFunc(specialKeys);

  • 实现方式:坐标更新和矩阵变换
    以正方形的初始位置和顶点的坐标如图为例;

在这里插入图片描述

  • 坐标更新:以上图中的D点为基础,实现specialKeys的响应事件(通过以下代码即可改变的正方形顶点位置更新,从而控制整个正方形的初步移动效果);
/// 特殊键位的触发
/// @param key 键位key值
/// @param x 像素
/// @param y 像素
void specialKeys(int key, int x, int y) {
    
    // 移动的步长
    GLfloat setpSize = 0.05f;
    
    // 需要移动点的偏移X、偏移Y值
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[10];
    
    // 方向键控制D点移动
    if (key == GLUT_KEY_UP) {
        blockY += setpSize;
    }
    if (key == GLUT_KEY_DOWN) {
        blockY -= setpSize;
    }
    if (key == GLUT_KEY_LEFT) {
        blockX -= setpSize;
    }
    if (key == GLUT_KEY_RIGHT) {
        blockX += setpSize;
    }
    
    // 方向键控制A点移动
    vVerts[0] = blockX;
    vVerts[1] = blockY - blockSize * 2;
    
    // 方向键控制B点移动
    vVerts[3] = blockX + blockSize * 2;
    vVerts[4] = blockY - blockSize * 2;
    
    // 方向键控制C点移动
    vVerts[6] = blockX + blockSize * 2;
    vVerts[7] = blockY;
    
    // 方向键控制D点移动
    vVerts[9] = blockX;
    vVerts[10] = blockY;
    
    // 将顶点数组通过GLBatch帮助类将顶点传输到存储着⾊色器中,并手动出发渲染函数
    triangleBatch.CopyVertexData3f(vVerts);
    // 手动触发重新渲染
    glutPostRedisplay();
}

  • 不难发现,以上的代码存在一个问题:正方形可以无限移动,甚至可以移动出屏幕的范围,到了不可见的区域,这怎么办呢?其实很简单,只需要做“边缘检测”的处理,如下图: 在这里插入图片描述
    具体代码的逻辑该怎么实现呢?只需要在setpSize的改变时加上正方形的大小即可,所以增加specialKeys方法中的部分逻辑代码;
// 判断正方形移动时的边界检测 
    if (blockX < -1.0f) {
        blockX = - 1.0f;
    }
    if (blockX > 1.0f - blockSize * 2) {
        blockX = 1.0f - blockSize * 2;
    }
    
    if (blockY < -1.0f + blockSize * 2) {
        blockY = -1.0f + blockSize * 2;
    }
    if (blockY > 1.0f) {
        blockY = 1.0f;
    }
  • 矩阵变换:以上的坐标变换方式只能针对顶点较少、图形简单的移动变换,当图形变得复杂,或者顶点特别多的时候,这样的处理就显得特别不实用,这就需要用到矩阵的方式进行变换(用矩阵记录移动的点,进行整体变换);

  • 定义全局变量:

// x平移距离
GLfloat xPos = 0.0f;
// y平移距离
GLfloat yPos = 0.0f;
  • 修改specialKeys方法中的逻辑;
// 矩阵变换
/// 特殊键位的触发
/// @param key 键位key值
/// @param x 像素
/// @param y 像素
void specialKeys(int key, int x, int y) {
    
    // 移动的步长
    GLfloat setpSize = 0.05f;
    
    // 方向键控制D点移动
    if (key == GLUT_KEY_UP) {
        yPos += setpSize;
    }
    if (key == GLUT_KEY_DOWN) {
        yPos -= setpSize;
    }
    if (key == GLUT_KEY_LEFT) {
        xPos -= setpSize;
    }
    if (key == GLUT_KEY_RIGHT) {
        xPos += setpSize;
    }
    
    // 碰撞检测
    if (xPos < (-1.0 + blockSize)) {
        xPos = -1.0 + blockSize;
    }
    if (xPos > (1.0 - blockSize)) {
        xPos = 1.0 - blockSize;
    }
    if (yPos < (-1.0f + blockSize)) {
        yPos = -1.0f + blockSize;
    }
    if (yPos > (1.0f - blockSize)) {
        yPos = 1.0f - blockSize;
    }
    // 手动触发重新渲染
    glutPostRedisplay();
}
  • 修改renderScene方法:
// 矩阵变换
/// 开始渲染(触发条件:1.系统自动触发 2.⼿动调用函数触发)
void renderScene(void) {
    // 清除一个或一组特定的缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    GLfloat colorRed[] = {1.0f, 0.0f, 0.0f, 1.0f};
    // 根据平移距离计算平移矩阵
    M3DMatrix44f mTransFromMatrix;
    m3dTranslationMatrix44(mTransFromMatrix, xPos, yPos, 0.0f);
    
    // 将矩阵结果交给存储着色器(平面着色器)中绘制
    shadermanager.UseStockShader(GLT_SHADER_FLAT, mTransFromMatrix, colorRed);
    
    // 提交着色器
    triangleBatch.Draw();
    // 将在后台缓冲区进行渲染,然后在结束时交换到前台
    glutSwapBuffers();
}

效果展示

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Forever_wj/article/details/107139767