OpenGL--案例2-绘制正方形

2,021 阅读5分钟

相关目录:

  1. OpenGL--图形API简介
  2. OpenGL--相关名词解释
  3. OpenGL--环境配置
  4. OpenGL--案例1-绘制一个三角形
  5. OpenGL--案例2-绘制正方形
  6. OpenGL--图像撕裂
  7. OpenGL--3D数学相关(向量和矩阵)
  8. OpenGL--矩阵变换和矩阵堆栈

在前面的 案例 1 中, 我们使用 OpenGL 绘制出了一个三角形。接下来, 我们在一起来绘制一个 正方形, 并且通过键盘控制这个正方形进行上下左右的移动。一起来玩一下吧~

实现

首先准备工作这里就不做多余的阐述了, 可以去 案例 1-绘制一个三角形 查看, 这里主要介绍一下如何去绘制一个 正方形, 然后怎样实现通过键盘控制其进行上下左右的移动。

绘制正方形

1.设置正方形的边长和顶点坐标

//
GLfloat blockSize = 0.2f;

//正方形的4个点坐标
GLfloat vVerts[] = {
        -blockSize,-blockSize,0.0f,
        blockSize,-blockSize,0.0f,
        blockSize,blockSize,0.0f,
        -blockSize,blockSize,0.0f
};

2.渲染设置

方法同之前的绘制三角形方法, 但是图元链接方式改用 GL_TRIANGLE_FAN

void setupRC(){
    //设置清屏颜色(背景颜色)
    glClearColor(0.0f, 0.0f, 0.7f, 1);
    //没有着色器,在OpenGL 核心框架中是无法进行任何渲染的。初始化一个渲染管理器。
    //
    shaderManager.InitializeStockShaders();
    
    //修改为GL_TRIANGLE_FAN ,4个顶点
    triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
    triangleBatch.CopyVertexData3f(vVerts);
    triangleBatch.End();
}

图元链接方式 (补充)

图元 描述
GL_POINTS 每个顶点在屏幕上都是单独点
GL_LINES 每一对顶点定义⼀个线段
GL_LINE_STRIP 一个从第⼀个顶点依次经过每一个后续顶点而绘制的线条
GL_LINE_LOOP 和 GL_LINE_STRIP 相同,但是最后一个顶点和第一个顶点连接起来了了
GL_TRIANGLES 每3个顶点定义一个新的三⻆形
GL_TRIANGLE_STRIP 共⽤一个条带(strip)上的顶点的一组三角形
GL_TRIANGLE_FAN 以⼀个圆点为中心呈扇形排列,共用相邻顶点的一组三角形

示例如下:

3. 绘制效果

这里省略掉了跟之前案例 1 相似的代码, 那么直接执行看一下效果先:

可以看到我们想要的正方形成功地画好了, 那么接下来开始进行下一步, 如何实现通过键盘控制正方形的移动 ?

实现键盘控制移动

在 案例1 中, 我们通过 glutReshapeFunc 函数注册的回调函数 ChangeSize 来实现窗口改变的时候调用回调函数进行重绘。接下来再来看下面这个新的函数

// 对键盘上特殊的4个方向按键的响应函数
// key  GLUT_KEY_UP、GLUT_KEY_DOWN、GLUT_KEY_LEFT和GLUT_KEY_RIGHT
// x和y是捕捉的当按键事件发生时显示窗口上鼠标点所处的位置,需要注意的是x和y是以左上角为起点(0,0),右下角为终点(windowWidth,windowHeight)
extern void APIENTRY glutSpecialFunc(void (*func)(int key, int x, int y)) OPENGL_DEPRECATED(10_0, 10_9);
// 注册鼠标响应事件
extern void APIENTRY glutMouseFunc(void (*func)(int button, int state, int x, int y)) OPENGL_DEPRECATED(10_0, 10_9);
// 注册键盘响应事件
extern void APIENTRY glutKeyboardFunc(void (*func)(unsigned char key, int x, int y)) OPENGL_DEPRECATED(10_0, 10_9);

下面就来实现以下回调函数 SpecialKeys :

// 特殊函数回调
void SpecialKeys(int key, int x, int y){
    // 设置每一步的距离,就是键盘按一次,图形移动的距离
    GLfloat stepSize = 0.05f;
    
    // 取任意一点的x和y坐标,当作初始坐标
    // 这里取正方形左上角定点的坐标
    GLfloat startX = vVerts[9];
    GLfloat startY = vVerts[10];
    
    // 按键:上
    if (key == GLUT_KEY_UP) {
        startY += stepSize;
    }
    // 按键:下
    if (key == GLUT_KEY_DOWN) {
        startY -= stepSize;
    }
    // 按键:左
    if (key == GLUT_KEY_LEFT) {
        startX -= stepSize;
    }
    // 按键:右
    if (key == GLUT_KEY_RIGHT) {
        startX += stepSize;
    }
      
    // 更新四个顶点坐标, 普通更新方法,顶点数量少时,可以使用
    // 左上角
    vVerts[9] = startX;
    vVerts[10] = startY;
    // 左下角
    vVerts[0] = vVerts[9];
    vVerts[1] = vVerts[10] - blockSize*2;
    // 右下角
    vVerts[3] = vVerts[0] + blockSize*2;
    vVerts[4] = vVerts[1];
    // 右上角
    vVerts[6] = vVerts[3];
    vVerts[7] = vVerts[10];
    
    printf("startX = %f, startY = %f\n", startX, startY);
    
    triangleBatch.CopyVertexData3f(vVerts);
    // 标记当前需要重新绘制
    glutPostRedisplay();
}

运行效果:

优化方案

边界问题

上下左右的移动已经实现了, 但是如果一直往一个方向去移动的话就可能把正方形移出边界, 接下来再做一下边缘处理, 在 SpecialKeys() 函数更新顶点坐标之前添加判断:

// 边缘碰撞处理
//当正方形移动超过最左边的时候
if (startX < -1.0f) {
  startX = -1.0f;
}
//当正方形移动到最右边时
//1.0 - blockSize * 2 = 总边长 - 正方形的边长 = 最左边点的位置
if (startX > (1.0 - blockSize * 2)) {
  startX = 1.0f - blockSize * 2;
}
//当正方形移动到最下面时
//-1.0 - blockSize * 2 = Y(负轴边界) - 正方形边长 = 最下面点的位置
if (startY < -1.0f + blockSize * 2 ) {
  startY = -1.0f + blockSize * 2;
}
//当正方形移动到最上面时
if (startY > 1.0f) {
  startY = 1.0f;
}

执行效果:

平移矩阵

  • 定义全局变量,用来存储移动的距离
//  矩阵更新需要用到
GLfloat xPos = 0.0f;
GLfloat yPos = 0.0f;
  • 更新 xPos 和 yPos 位置, 碰撞检测
void SpecialKeys(int key, int x, int y) {
    GLfloat stepSize = 0.025f;
    
    if (key == GLUT_KEY_UP) {
        yPos += stepSize;
    }
    
    if (key == GLUT_KEY_DOWN) {
        yPos -= stepSize;
    }
    
    if (key == GLUT_KEY_LEFT) {
        xPos -= stepSize;
    }
    
    if (key == GLUT_KEY_RIGHT) {
        xPos += stepSize;
    }
    
    //碰撞检测
    if (xPos < (-1.0f + blockSize)) {
        xPos = -1.0f + blockSize;
    }
    
    if (xPos > (1.0f - blockSize)) {
        xPos = 1.0f - blockSize;
    }
    
    if (yPos < (-1.0f + blockSize)) {
        yPos = -1.0f + blockSize;
    }
    
    if (yPos > (1.0f - blockSize)) {
        yPos = 1.0f - blockSize;
    }
    glutPostRedisplay();
}
  • renderScene方法中,进行矩阵更新的设置
void RenderScene(void) {

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
    
    GLfloat vRed[] = {1.0f,0.0f,0.0f,0.0f};
    
    M3DMatrix44f mFinalTransform,mTransfromMatrix,mRotationMartix;
    
    //平移
    m3dTranslationMatrix44(mTransfromMatrix, xPos, yPos, 0.0f);
    
    //每次平移时,旋转5度
    static float yRot = 0.0f;
    yRot += 5.0f;
    m3dRotationMatrix44(mRotationMartix, m3dDegToRad(yRot), 0.0f, 0.0f, 1.0f);
    
    //将旋转和移动的矩阵结果 合并到mFinalTransform (矩阵相乘)
    m3dMatrixMultiply44(mFinalTransform, mTransfromMatrix, mRotationMartix);
    
    //将矩阵结果 提交给固定着色器(平面着色器)中绘制
    shaderManager.UseStockShader(GLT_SHADER_FLAT,mFinalTransform,vRed);
    triangleBatch.Draw();
    
    //执行交换缓存区
    glutSwapBuffers();
}

案例 2 - Demo 下载

最后

以上内容就是关于 OpenGL 的两个案例, 可以下载 demo 了解一下, 不懂的地方欢迎在下面一起交流。此后会继续更新关于 OpenGL 相关的内容, 感谢观看~

和谐学习, 不急不躁~