上篇文章介绍了与OpenGL相关的一些常用的名词,这篇文章,我们就开始正式上手了,用OpenGL画一个正方形,并且可以通过键盘的方向键,控制这个正方形的移动。
一、OpenGL环境搭建
-
新建macOS项目
-
下载相关资源,然后解压缩
-
解压缩后,里面有两个文件,
include
和libGLTools.a
,把这两个文件拖进刚刚新建好的项目中
-
添加系统库
OpenGl.framework
、GLUT.framework
-
在
Build Setting
选项卡里面,添加include
文件夹的路径
-
删除
AppDelegate.h
、AppDelegate.m
、main.m
、ViewController.h
、ViewController.m
,并新建一个main.cpp
文件(文件名一定要为main)
在
main.cpp
文件里面添加如下代码
#include <stdio.h>
#include "GLTools.h"
#include "GLShaderManager.h"
#include <GLUT/GLUT.h>
int main(int argc,char* argv[]) {
return 0;
}
-
command
+B
,编译一下,如果不报错,说明OpenGL的环境搭建已经成功,🎉🎉🎉
二、画一个正方形
- 示例代码:
#include "GLTools.h"
#include <GLUT/GLUT.h>
#include "GLShaderManager.h"
// 着色管理器
GLShaderManager shaderManager;
// 批次容器
GLBatch squareBatch;
// 正方形边长
GLfloat squareWidth = 0.1f;
// 初始正方形的4个点的坐标
GLfloat vVerts[] = {
-squareWidth, -squareWidth, 0.0f,
squareWidth, -squareWidth, 0.0f,
squareWidth, squareWidth, 0.0f,
-squareWidth, squareWidth, 0.0f
};
void changeSize(int w, int h) {
glViewport(0, 0, w, h);
}
void renderScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat vRedColor[] = {1.0, 0.0, 0.0, 1.0};
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRedColor);
squareBatch.Draw();
glutSwapBuffers();
}
void setupRC() {
glClearColor(0.98, 0.4, 0.7, 1.0);
shaderManager.InitializeStockShaders();
squareBatch.Begin(GL_TRIANGLE_FAN, 4);
squareBatch.CopyVertexData3f(vVerts);
squareBatch.End();
}
int main(int argc,char* argv[]) {
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(500, 500);
glutCreateWindow("正方形移动");
glutReshapeFunc(changeSize);
glutDisplayFunc(renderScene);
GLenum status = glewInit();
if (status != GLEW_OK) {
return 1;
}
setupRC();
glutMainLoop();
return 0;
}
- 主要函数说明
函数 | 触发时机 | 处理业务 |
---|---|---|
void changeSize(int w, int h) |
1、新建窗口的时候 2、改变窗口大小的时候 |
设置OpenGL视口 |
void renderScene() |
系统自动调用 | 1、清除缓冲区 2、使用存储着色器 3、绘制图形 |
void setupRC() |
手动调用 | 1、设置窗口颜色 2、初始化固定着色器 3、设置图形定点数据 4、利用 GLBatch 批次类,将数据传递到着色器 |
int main(int argc,char* argv[]) |
程序入口 | 1、绘制的准备工作,例如初始化缓冲区,设置窗口大小、注册自定义函数回调等 2、启动运行循环,类型RunLoop |
-
具体流程
-
最终显示效果
三、通过键盘方向键控制正方形移动
有两种方式控制正方形的移动
1、坐标更新的方式
2、矩阵方式-
在OpenGL坐标系中,窗口大小为
-1
到1
,我们选取左上角的点(由于z轴为0,因此在图上没有画出来)
坐标更新的方式
- 我们需要在上面的示例代码里面加上一个自定义控制函数。注意:注意我们选取的是左上角那个点
void specialKeys(int key, int x, int y) {
// 步长
GLfloat stepSize = 0.025;
// 取出其中一个点(左上角的那个点)
GLfloat blockX = vVerts[0];
GLfloat blockY = vVerts[10];
if (key == GLUT_KEY_UP) {
// 上
blockY += stepSize;
}
if (key == GLUT_KEY_LEFT) {
// 左
blockX -= stepSize;
}
if (key == GLUT_KEY_DOWN) {
// 下
blockY -= stepSize;
}
if (key == GLUT_KEY_RIGHT) {
// 右
blockX += stepSize;
}
// 碰撞检测
// 上边
if (blockY > 1.0) {
blockY = 1.0;
}
// 左边
if (blockX < -1.0) {
blockX = -1.0;
}
// 下边
if (blockY < -(1.0 - squareWidth * 2)) {
blockY = -(1.0 - squareWidth * 2);
}
// 右边
if (blockX > 1.0 - squareWidth * 2) {
blockX = 1.0 - squareWidth * 2;
}
// 第三象限
vVerts[0] = blockX;
vVerts[1] = blockY - squareWidth * 2;
vVerts[2] = 0;
// 第四象限
vVerts[3] = blockX + squareWidth * 2;
vVerts[4] = blockY - squareWidth * 2;
vVerts[5] = 0;
// 第一象限
vVerts[6] = blockX + squareWidth * 2;
vVerts[7] = blockY;
vVerts[8] = 0;
// 第二象限
vVerts[9] = blockX;
vVerts[10] = blockY;
vVerts[11] = 0;
// 更新
squareBatch.CopyVertexData3f(vVerts);
glutPostRedisplay();
}
int main(int argc,char* argv[]) {
.
.
.
// 注册重塑函数
glutReshapeFunc(changeSize);
// 注册显示函数
glutDisplayFunc(renderScene);
// 注册特殊函数
glutSpecialFunc(specialKeys);
// 初始化一个GLEW库,确保OpenGL API对程序完全可用。
GLenum status = glewInit();
const GLubyte *description = glewGetErrorString(status);
printf("初始化状态: %s\n", description);
if (status != GLEW_OK) {
return 1;
}
// 设置渲染环境
setupRC();
glutMainLoop();
return 0;
}
- 主要流程:
1、定义一个步长
2、选取一个顶点(这儿选取的是左上角)
3、根据键位方向,更新x,y,z
4、边缘碰撞检测
5、重新渲染
矩阵的方式
我们需要修改下renderScene
和specialKeys
的代码
// 记录移动图形时,在x轴上平移的距离
GLfloat xPos = 0.0f;
// //记录移动图形时,在y轴上平移的距离
GLfloat yPos = 0.0f;
void specialKeys(int key, int x, int y) {
// 步长
GLfloat stepSize = 0.025;
// 以正方形中心点为基准,中心点移动
if (key == GLUT_KEY_UP) {
// 上
yPos += stepSize;
}
if (key == GLUT_KEY_LEFT) {
// 左
xPos -= stepSize;
}
if (key == GLUT_KEY_DOWN) {
// 下
yPos -= stepSize;
}
if (key == GLUT_KEY_RIGHT) {
// 右
xPos += stepSize;
}
// 碰撞检测
if (xPos < -1.0 + squareWidth) {
xPos = -1.0 + squareWidth;
}
if (xPos > 1.0 - squareWidth) {
xPos = 1.0 - squareWidth;
}
if (yPos < -1.0 + squareWidth) {
yPos = -1.0 + squareWidth;
}
if (yPos > 1.0 - squareWidth) {
yPos = 1.0 - squareWidth;
}
glutPostRedisplay();
}
void renderScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// 设置正方形的颜色
GLfloat vRedColor[] = {1.0, 0.0, 0.0, 1.0};
M3DMatrix44f mTransfromMatrix;
// 平移
m3dTranslationMatrix44(mTransfromMatrix, xPos, yPos, 0.0);
shaderManager.UseStockShader(GLT_SHADER_FLAT, mTransfromMatrix, vRedColor);
squareBatch.Draw();
glutSwapBuffers();
}
- 主要流程:
1、定义步长及两个全局变量(相对于x轴和y轴的平移距离)
2、根据移动方向,计算移动距离(以正方形中心点为基准进行移动)
3、边缘碰撞处理
4、手动触发重新渲染