美文网首页
OpenGL-你好,正方形

OpenGL-你好,正方形

作者: 卖馍工程师 | 来源:发表于2020-07-10 14:17 被阅读0次

    案例练习,在窗口绘制一个正方形,并且用方向键控制移动

    如果你是初学者,请先阅读 案例--绘制三角形,这篇文章里会对基本函数做详细的解读。这里,我们就不再赘述了。

    绘制正方形与绘制三角形基本流程是一样的,不同的是,正方形有4个顶点,三角形有3个顶点,我们只需要在绘制三角形的基础上做一些小小的修改。

    为了方便后面的正方形移动,我们事先定义正方形4个顶点到中心点的距离为blockSize

    GLfloat blockSize = 0.1f;
    

    由图很容易可以看出,那么正方形的边长为:blockSize *2, 即0.2。

    修改顶点数组:

    //正方形四个点的坐标
    GLfloat vVerts[] = {
        -blockSize, -blockSize, 0.0f,   // A点
        blockSize, -blockSize, 0.0f,    // B点
        blockSize, blockSize, 0.0f,     // C点
        -blockSize, blockSize, 0.0f,    // D点
    };
    

    修改setupRC函数中图元连接方式:

    //将 GL_TRIANGLES(三角形) 修改为 GL_TRIANGLE_FAN (正方形),4个顶点
    triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
    

    只需要对三角形案例修改上面的代码,我们就可以得到了正方形

    绘制正方形

    接下来实现通过键位来控制正方形移动

    案例目标:通过 键盘上的“ 上 下 左 右 ”按钮来控制正方形的移动

    要想通过键盘来控制图形,我们需要在main函数中注册一个函数。

    glutSpecialFunc(SpecialKeys);
    

    当我们通过键位控制图形的时候,会在SpecialKeys函数中回调。

    对于正方形ABCD,当我们移动ABCD的时候,ABCD四个顶点的位置会发生变化,但是ABCD四个顶点的相对位置,并不会改变,我们可以以D点为例,计算D点移动之后D‘的位置信息,最后 通过四个顶点之间的相对位置关系,计算出其他顶点更新之后的位置信息。

    首先定义移动步长:

    //步长
    GLfloat stepSize = 0.025f;
    

    当D点向上移动一个键位时,此时D’点坐标x保持不变,y增加一个步长;
    当D点向下移动一个键位时,此时D’点坐标x保持不变,y减少一个步长;
    当D点向左移动一个键位时,此时D’点坐标y保持不变,x减少一个步长;
    当D点向右移动一个键位时,此时D’点坐标y保持不变,x增加一个步长;

    实现代码如下:

    //key 枚举值,x、y是位置
    void SpecialKeys(int key, int x, int y){
        //步长
        GLfloat stepSize = 0.025f;
        
        //取出参考点D点的坐标的x,y值
        GLfloat blockX = vVerts[0];
        GLfloat blockY = vVerts[10];
        
        //根据移动方向,更新D点移动后的坐标
        if (key == GLUT_KEY_UP) {
            blockY += stepSize;
        }
        if (key == GLUT_KEY_DOWN) {
            blockY -= stepSize;
        }
        if (key == GLUT_KEY_LEFT) {
            blockX -= stepSize;
        }
        if (key == GLUT_KEY_RIGHT) {
            blockX += stepSize;
        }
      
        //根据相对位置信息更新其他顶点坐标
        // 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;
        printf("(%f,%f)\n",vVerts[9],vVerts[10]);
        
        //更新顶点数据
        triangleBatch.CopyVertexData3f(vVerts);
        
        //重新渲染提交 --> RenderScene
        glutPostRedisplay();
        
    }
    

    至此,我们可以完成了,可以通过键位控制正方形移动的功能。

    运行代码一直向右移动,正方形移出了窗口边界。

    正方形移动出边界了

    通常情况下,这不是我们想要的结果,我们要做边界碰撞检测。

    在规范化坐标系下,坐标系统是在-1.0~1.0之间的。

    当正方形移动到最左边的时候,实际上就是 blockX 到达-1.0点;
    当正方形移动到最右边的时候,实际上是 blockX + 边长 到达了1.0点;
    当正方形移动到最上边的时候,实际上是 blockY 到达了-1.0点;
    当正方形移动到最下边的时候,实际上是 blockY + 边长到达了1.0点

    所以我们添加边界检测代码,如下:

    //key 枚举值,x、y是位置
    void SpecialKeys(int key, int x, int y){
        //步长
        GLfloat stepSize = 0.025f;
        
        //取出参考点D点的坐标的x,y值
        GLfloat blockX = vVerts[0];
        GLfloat blockY = vVerts[10];
     
        //根据移动方向,更新D点移动后的坐标
        if (key == GLUT_KEY_UP) {
            blockY += stepSize;
        }
        if (key == GLUT_KEY_DOWN) {
            blockY -= stepSize;
        }
        if (key == GLUT_KEY_LEFT) {
            blockX -= stepSize;
        }
        if (key == GLUT_KEY_RIGHT) {
            blockX += stepSize;
        }
        
        //触碰到边界(4个边界)的处理
        
        //当正方形移动超过最左边的时候
        if (blockX < -1.0f) {
            blockX = -1.0f;
        }
        //当正方形移动到最右边时
        //1.0 - blockSize * 2 = 总边长 - 正方形的边长 = 最左边点的位置
        if (blockX > (1.0f - blockSize * 2)) {
            blockX = 1.0f - blockSize * 2;
        }
        //当正方形移动到最下面时
        //-1.0 - blockSize * 2 = Y(负轴边界) - 正方形边长 = 最下面点的位置
        if (blockY < -1.0f + blockSize * 2) {
            blockY = -1.0f + blockSize * 2;
        }
        //当正方形移动到最上面时
        if (blockY > 1.0f) {
            blockY = 1.0f;
        }
       
        //根据相对位置信息更新其他顶点坐标
        // 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;
        
        //更新顶点数据
        triangleBatch.CopyVertexData3f(vVerts);
        
        //重新渲染提交 --> RenderScene
        glutPostRedisplay();
        
    }
    

    至此,边界碰触问题得到了解决。

    此案例中我们用到了4个顶点,通过计算其中一个顶点位置信息,根据顶点间相对位置计算其他顶点位置信息。那么,当有100个,1000个顶点的时候呢?

    显然是不能够这样做的,为此,我们引入变换矩阵,下面我们利用变换矩阵来实现上面的案例。

    利用矩阵实现

    我们可以根据x,y移动的距离, 通过平移矩阵 图形顶点位置 * 平移矩阵 = 移动后的图形顶点位置,得到最终的效果。

    我们对上面的实现,做一些小小的修改。

    首先我们不再需要计算每一个顶点的移动距离,我们只需要记录图形在x,y方向上的整体平移距离。

    // 记录移动图形时,在x轴上平移的距离
    GLfloat xPos = 0.0f;
    // 记录移动图形时,在y轴上平移的距离
    GLfloat yPos = 0.0f;
    // 步长
    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;
      }
    

    不同的是对于边界检测,我们要换个思维来理解一下

    我们可以将平移矩阵变化过程中的图形平移的距离 理解成 根据图形中心点 来进行移动的,所以我们要计算的移动距离就是两个中心点之间的平移距离。

    当正方形中心点移动到最左边的时候,实际上正方形的最左边已经超出了屏幕的一半边长值,xPos走多了,所以要让xPos - blockSize > -1.0始终成立;即当xPos < (-1.0 + blockSize)时,让其等于(-1.0 + blockSize)
    当正方形中心点移动到最右边的时候,实际上正方形的最右边已经超出了屏幕的一半边长值,xPos走多了,所以要让xPos + blockSize < 1.0始终成立;即当xPos > 1.0- blockSize时,让其等于1.0-blockSize
    当正方形中心点移动到最上边的时候,实际上正方形的最上边已经超出了屏幕的一半边长值,yPos走多了,所以要让yPos + blockSize < 1.0始终成立;即当yPos > 1.0 - blockSize时,让其等于1.0 - blockSize
    当正方形中心点移动到最下边的时候,实际上正方形的最下边已经超出了屏幕的一半边长值,yPos走多了,所以要让yPos -blockSize > -1.0始终成立;即当yPos < -1.0 + blockSize 时,让其等于-1.0 + blockSize

    综上,SpecialKeys函数中的完整代码如下:

    //使用矩阵方式(一起搞定),不需要修改每个顶点,只需要记录移动步长,碰撞检测
    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;
        }
        
        //碰撞检测 xPos是x轴上平移距离, yPos是y轴上平移距离
        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();
        
    }
    

    至此,对于正方形的移动以及边缘碰就做好了,下面我们要应用到着色器中,在三角形以及上面的实现方法时我们用到了 单元着色器 ,当使用平移矩阵的时候,我们需要用到 平面着色器 ,来传入相应的变换矩阵(关于着色器类型的了解,可以阅读 渲染架构中关于固定着色器的介绍

    修改代码如下:

    //开始渲染
    void RenderScene(void)
    
    {
        //1.清除一个或者一组特定的缓存区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        
        //2.设置颜色RGBA
        GLfloat vRed[] = {1.0f, 0.5f, 0.0f, 1.0f};
    
        //定义矩阵
        M3DMatrix44f mTransformMatrix;
        
        //平移矩阵
        m3dTranslationMatrix44(mTransformMatrix, xPos, yPos, 0.0f);
        
        //使用平面着色器
        //参数1:存储着色器类型
        //参数2:使用什么矩阵变换
        //参数3:颜色
        shaderManager.UseStockShader(GLT_SHADER_FLAT, mTransformMatrix, vRed);
        
        //提交着色器
        triangleBatch.Draw();
        glutSwapBuffers();
    }
    

    至此,利用平移矩阵对于此案例的实现就完成了。

    相关文章

      网友评论

          本文标题:OpenGL-你好,正方形

          本文链接:https://www.haomeiwen.com/subject/nwujcktx.html