OpenGL-你好,正方形

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

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

绘制正方形与绘制三角形基本流程是一样的,不同的是,正方形有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();
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356