前面学习了 OpenGL 的环境配置, 接下来再来玩 2 个关于 OpenGL 的小案例。( 就像学习其他语言一样, 一开始不应该先来个 "hello world!! " 玩一下吗。) 接下来, 我们一起先从使用 OpenGL 绘制一个三角形开始
准备工作
首先在之前环境配置的时候, 我们在工程里面导入了 "GLTools"、"glew"、"libGLTools.a" 三个文件, 这次的案例就需要使用这些资源文件里面的内容。
工具类
1. GLShaderManager
着色管理器, 在 OpenGL 的核心框架中, 没有给我们提供任何内建的渲染管线, 在提交一个图形进行渲染之前, 必须先实现一个着色器。GLShaderManager
就是一个由 C++
实现的着色器管理类, 他管理者一组 "存储着色器", 并且这些着色器能够满足我们的案例。
// 头文件
#include "GLShaderManager.h"
// 重命名
GLShaderManager shaderManager;
// 初始化
shaderManager.InitializeStockShaders();
2. GLTools
OpenGL 的一个静态工具库, 包含了 OpenGL 工具函数库、实用工具库和一些其他常用的函数。方便我们对 API 的调用。
// 头文件
#include "GLTools.h"
// 包含
// Universal includes
#include <stdio.h>
#include <math.h>
#include "math3d.h"
#include "GLBatch.h"
#include "GLTriangleBatch.h"
// 和 glew
3. GLUT
这个库相当于 OpenGL 的工具箱, 在 Mac
环境下我们是没有办法直接通过 OpenGL 去创建窗口的。GLUT 提供了 创建窗口、弹出式菜单、窗口管理等的API。此外, 在 Windows 和 Linux
系统中, GLUT 的开发已经中断, 使用的是一个叫做 freeglut
的函数库。
// 头文件
#include <glut/glut.h>
// 初始化
/* GLUT initialization sub-API. */
extern void APIENTRY glutInit(int *argcp, char **argv) OPENGL_DEPRECATED(10_0, 10_9);
// 创建窗口
/* GLUT window sub-API. */
extern int APIENTRY glutCreateWindow(const char *title) OPENGL_DEPRECATED(10_0, 10_9);
// 注册回调函数
/* GLUT window callback sub-API. */
/* 在程序运行时自动调用传入的 func 回调函数, 调用时机:
* 窗口内容绘制
* 窗口大小改变
* 窗口重绘
*/
extern void APIENTRY glutDisplayFunc(void (*func)(void)) OPENGL_DEPRECATED(10_0, 10_9);
/* 当窗口的大小发生改变时, 会调用传入的参数 func回调函数 */
extern void APIENTRY glutReshapeFunc(void (*func)(int width, int height)) OPENGL_DEPRECATED(10_0, 10_9);
函数及实现
- ChangeSize 自定义函数.通过 glutReshaperFunc(函数名) 注册为重塑函数。当屏幕大小发生变化/或者第一次创建窗口时,会调用该函数调整窗口大小/视口大小
void ChangeSize(int w,int h) {
// glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
// x,y 以像素为单位, 指定了窗口的左下角位置
// width,height 表示视口矩形的宽度和高度, 根据窗口的实时变化重绘窗口
glViewport(0,0, w, h);
}
- RenderScene 自定义函数.通过 glutDisplayFunc(函数名) 注册为显示渲染函数。当屏幕发生变化/或者开发者主动渲染时会调用此函数, 用来实现数据->渲染过程
//
GLBatch triangleBatch; //批次
GLShaderManager shaderManager; //着色管理器
//
void RenderScene(void) {
//清除一个或一组特定的缓冲区
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
//设置一组浮点数来表示红色 (RGBA)
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
//传递到存储着色器,即GLT_SHADER_IDENTITY着色器,这个着色器只是使用指定颜色以默认笛卡尔坐标第在屏幕上渲染几何图形
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
//提交着色器
triangleBatch.Draw();
//将在后台缓冲区进行渲染,然后在结束时交换到前台
//(延伸部分: 双缓冲, 垂直同步, 三缓冲)
glutSwapBuffers();
}
- setupRC ⾃定义函数,设置你需要渲染的图形的相关顶点数据、颜色数据等数据装备⼯作。
void SetupRC() {
//这里可以理解为用参数表示的颜色重新设置背影颜色
glClearColor(0.0f,0.0f,1.0f,1.0f);
//初始化着色管理器
shaderManager.InitializeStockShaders();
//设置三角形,其中数组vVert包含所有3个顶点的 x,y,z 的笛卡尔坐标对。
GLfloat vVerts[] = {
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,0.5f,0.0f,
};
//批次处理
triangleBatch.Begin(GL_TRIANGLES,3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
}
- main 程序⼊口, OpenGL 是面向过程编程, 所以你会发现利用OpenGL处理 图形/图像 都是链式形式, 以及基于OpenGL封装的图像处理框架也是链式编程
int main(int argc,char* argv[]) {
//设置当前工作目录
gltSetWorkingDirectory(argv[0]);
//初始化GLUT库
glutInit(&argc, argv);
/*初始化双缓冲窗口,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指
双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区*/
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
//GLUT窗口大小,标题窗口
glutInitWindowSize(800,600);
glutCreateWindow("Triangle");
//注册回调函数 (ChangeSize 和 RenderScene 就是我们上面实现的自定义函数)
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
//驱动程序的初始化中没有出现任何问题。
GLenum err = glewInit();
if(GLEW_OK != err) {
fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
return 1;
}
//调用SetupRC
SetupRC();
//进入 GLUT 事件处理循环
glutMainLoop();
return 0;
}
执行结果
ps: 有些人可能执行后的三角形形状不对, 然后改变一下框的大小就正常了。这个据说是因为我们使用的 API 是旧的, 然后 xcode 版本是新的导致的, 不必在意这些细节。