[demo2].进阶啦,来一个可以移动的多边形吧
首先,来绘制一个多边形
- 在 [demo1].来一个最简单的三角形吧 的demo的基础上,将vTops里的顶点改成5个(或者6个7个8个...),并将全局容器内的绘制方式改为GL_POLYGON(或者改成其他的方式,见下图),并修改顶点个数
GLfloat vTops[] = {
-0.4, -0.3, 0, //v0
0.1, -0.7, 0, //v1
0.6, -0.1, 0, //v2
0.4, 0.5, 0, //v3
-0.2, 0.4, 0 // v4
};
/**
* GL_TRIANGLES 三角形
*/
triangleBatch.Begin(GL_POLYGON, 5);
image-20200705012351940.png
从上图可以看到有很多种绘图方式,可以根据需要选择自己需要的方式,这里是5个顶点,所以选择了GL_POLYGON(8个也可以选择POLYGON,会按照顺序依次连接并绘制封闭图形)。
-
直接运行,就可以看到一个5变形成功的绘制在窗口内了 👏👏👏
image-20200705013621924.png
接下来,让多边形动起来
-
设置一个全局变量,用于存放v0, v1, v2, v3, v4相对于v0的位置
同时设置一个全局变量,用于存放v0,v1,v2,v3,v4的初始位置和新位置
//v0, v1, v2, v3, v4 相对于v0的相对位置 GLfloat relate_tops[15] = { }; //v0, v1, v2, v3, v4 的新位置 GLfloat movie_tops[15] = { };
-
在准备时计算相对位置
for (int i = 0 ; i < 15; i++) { movie_tops[i] = vTops[i]; relate_tops[i] = vTops[i] - vTops[i % 3]; printf("%.01f - %0.1f\n", movie_tops[i], relate_tops[i]); }
这里是5个点,所以要循环3*5=15次,如果是n个点,则需要循环 3 *n次
-
注册一个新的函数,用于捕获键盘的输入
/** * @param key 键盘输入的键 */ void SpecialKeys(int key, int x, int y) { } glutSpecialFunc(SpecialKeys);
-
设置步长stepSize,用作每次移动时的移动距离,以此来控制移动速度
GLfloat stepSize = 0.01;
这里设置为0.01,每次会移动屏幕0.5%的距离(屏幕总长度为 1 - (-1) = 2)
-
确定一个参考点,根据参考点与其他点的相对位置关系,得到其他点的位置
GLfloat blockX = movie_tops[0]; GLfloat blockY = movie_tops[1];
这里将v0作为参考点,所有点会随着v0的移动而移动,之前设置相对点的时候也是以v0作为参考点的
-
检测键盘的输入,确认参考点的移动位置,向上移动,y增大,向下移动,y减小,向左移动,x减小,向右移动,x增大
//向上移动 switch (key) { //方向键 上 case GLUT_KEY_UP: blockY = blockY + stepSize; break; //方向键 下 case GLUT_KEY_DOWN: blockY = blockY - stepSize; break; //方向键 左 case GLUT_KEY_LEFT: blockX = blockX - stepSize; break; //方向键 右 case GLUT_KEY_RIGHT: blockX = blockX + stepSize; break; //键盘 i 上 case 105: blockY = blockY + stepSize; break; //键盘 k 下 case 107: blockY = blockY - stepSize; break; //键盘 < 下 case 44: blockY = blockY - stepSize; break; //键盘 j 左 case 106: blockX = blockX - stepSize; break; //键盘 l 右 case 108: blockX = blockX + stepSize; break; //键盘 u 左上 case 117: blockX = blockX - stepSize; blockY = blockY + stepSize; break; //键盘 o 右上 case 111: blockX = blockX + stepSize; blockY = blockY + stepSize; break; //键盘 m 左下 case 109: blockX = blockX - stepSize; blockY = blockY - stepSize; break; //键盘 > 右下 case 46: blockX = blockX + stepSize; blockY = blockY - stepSize; break; default: printf("Unwork Key %d\n", key); break; }
在这里,我额外根据按键时打印的信号另外增加了9个键,遗憾的是,E和D不知为何无法响应(就是按了没反应),所以并没用WSAD
-
再根据相对位置,将移动后的点和v0的关系求出
for (int i = 0 ; i < 15; i++) { if (i % 3 == 0) { movie_tops[i] = relate_tops[i] + blockX; } else if (i % 3 == 1) { movie_tops[i] = relate_tops[i] + blockY; } else { movie_tops[i] = 0; } // printf("%.01f", movie_tops[i]); }
-
在将移动后新的位置复制进容器内,提交渲染
这个时候已经有了一支画笔,所以只需要强制重新渲染就OK
triangleBatch.CopyVertexData3f(movie_tops); glutPostRedisplay();
-
运行一下,5边形显示正常,按一下键盘,都正常,太棒了~
但是!但是!奇怪的事情发生了,我再按E和D键,他们的功能和I和J的一模一样,E向上移动,D向左移动,真的神奇 =。=(有小伙伴知道吗?求解答)
-
到这里,可移动的多边形就搞定了,但是并不完善,还有边界的检测没做。
为了做边界检测,我们需要设置一组临时数据,如果临时数据超出边界,就让这一次的移动无效
GLfloat tem_point[15] = { }; for (int i = 0 ; i < 15; i++) { if (i % 3 == 0) { tem_point[i] = relate_tops[i] + blockX; } else if (i % 3 == 1) { tem_point[i] = relate_tops[i] + blockY; } else { tem_point[i] = 0; } if (tem_point[i] > 1 || tem_point[i] < -1) { return; } }
经测试,完美~👏 (也可以考虑将tem_point设置为全局变量)
但是,当点特别多的时候,循环上千次,上万次的时候,这种方式未免有些无用(一个精细的2d模型上万的点正常吧),该怎么办呢?这时候就要用上线性代数了。具体怎么用?看下一篇
~