在前面的 案例 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();
}
最后
以上内容就是关于 OpenGL 的两个案例, 可以下载 demo 了解一下, 不懂的地方欢迎在下面一起交流。此后会继续更新关于 OpenGL 相关的内容, 感谢观看~
和谐学习, 不急不躁~