案例练习,在窗口绘制一个正方形,并且用方向键控制移动
如果你是初学者,请先阅读 案例--绘制三角形,这篇文章里会对基本函数做详细的解读。这里,我们就不再赘述了。
绘制正方形与绘制三角形基本流程是一样的,不同的是,正方形有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();
}
至此,利用平移矩阵对于此案例的实现就完成了。
网友评论