一、图形相关框架简介
1.1、图形框架
OpenGL(Open Graphics Library): 用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。这个接口由近350个不同的函数调用组成,用来绘制从简单的图形比特到复杂的三维景象。OpenGL常用于CAD、虚拟现实、科学可视化程序和电子游戏开发。
OpenGL ES(OpenGL for Embedded System): OpenGL三维图形API的子集,针对手机、PDA和游戏主机等嵌入式设备而设计,去除了许多不需要和性能较低的API接口
DirectX: Directx由很多API组成,但并不是一个单纯的图形API。最重要的是DirectX是属于windows上一个多媒体处理框架。并不支持Windows以外的平台。
Metal:Apple为游戏开发者推出了新的平台技术Metal,该技术能够为3D图像提高10倍的渲染性能。Metal是apple为了解决3D渲染而推出的框架。
1.2、图形API的作用
图形API主要是为了解决渲染相关的问题:
- 系统中UI控件、图片、视图、图层渲染的问题
- 游戏中的人物/场景的渲染
- 视频播放框架中(ijkplayer、kxmovie)的渲染功能
- 核心动画中的一些动画操作(旋转、缩放、移动、图层特效)
- 视频/图片的特效
二、OpenGL专业名词解析
OpenGL上下文(context):
- 在应用程序调用任何OpenGL的指令之前,需要首先创建一个OpenGL的上下文。这个上下文是一个非常庞大的状态机,保存了OpenGL中各种状态,这也是OpenGL指令执行的基础。
- OpenGL的函数不管在哪个语言中,都是类似C语言一样的面向过程的函数,本质上都是对OpenGL上下文这个庞大的状态机中某个状态或者对象进行操作,当然你得首先把这个对象设置为当前的对象。因此通过对OpenGL的指令封装,是可以将OpenGL的相关调用封装成一个面向对象的图形API
- 由于OpenGL上下文是一个巨大的状态机,切换上下文往往会产生较大的开销,但是不同的绘制模块,可能需要使用完全独立的状态管理。因此可以在应用程序中分别创建多个不同的上下文,在不同的线程中使用不同的上下文,上下文之间共享纹理、缓冲区等资源。这样的方案比反复切换上下文或者大量修改渲染状态更加合理高效。
OpenGL状态机器:
- OpenGL可以记录自己的状态(例如:当前颜色、是否开启某种功能等)
- OpenGL可以接收输入(OpenGL调用函数,可以看成在接收我们的输入)
- OpenGL可以进入停止状态,不在接收输入
| 名词 | 解析 |
|---|---|
| 渲染 | 将图像数据转换成2D空间图像的操作 |
| 定点数组 | 画图首先是先确定好图形的骨架,再对其进行填充。顶点数据就是要画的图像的骨架 |
| 顶点缓冲区 | 一个图中会有许多个顶点数组,为了使内存尽快的拿到,所以提前分配一块显存,将顶点数据先传入到显存中,这部分的显存就是顶点缓冲区 |
| 位图 | 常见的图片(png jpg等)都是压缩图片,图片解压过后就是位图。例如: 120 x 120 = 14400像素点, 每一个像素点包含RGBA数据(4x8=32位),所以位图的大小就是14400 x 4 = 57600bit |
| 映射 | 将位图一一对应到屏幕像素点上,呈现出图片 |
| 管线 | 类似于流水线,例如将我们的图片进行流水线式加工,得到最终的效果 |
| 固定管线 | 对图片进行流水线式加工的方式是固定的,只能用提供的固定的加工方式 |
| 可编程管线 | 对图片进行流水线式加工的方式是自定义的,可以通过代码(GLSL)来驱使GPU进行自定义处理 |
| 着色器 | 通过shader(着色器)(代码段)来调用GPU |
| 固定着色器 | 已经处理好的shader(着色器)(代码段),调用参数(OpenGL提供)就可以完成相关的GPU调用 |
| 自定义着色器 | 进行自定义(基于GLSL语法)处理shader(着色器)(代码段),来实现自定义调用GPU |
| 顶点着色器 | 用来处理顶点数据相关代码,来确定位置,实现缩放/平移/旋转等功能,以及用于显示3D |
| 片元着色器 | 指的是像素点,又叫像素着色器,它就是用于处理一个一个的像素点。一个图像是由一个个像素点组成的,可以通过片元着色器一个一个像素的去调整,就可以改变图片的效果 |
| GLSL | 自定义着色器需要通过写代码来进行自定义,再通过OpenGL的标准来调用GPU做计算。这里需要写的代码就是GLSL语言 |
| 光栅化 | 将顶点数据转换片元数据的过程:确定图形所在像素范围,将颜色附着到这个范围上去。这个过程不可自定义编程。 |
| 纹理 | 可以理解成图片,把压缩图片转化成位图显示出来 |
| 混合 | 假设layer有两个图层,分别为蓝色与红色叠加一起会产生一个颜色混合效果 |
| 变换矩阵 | 图形想要发生平移、缩放、旋转变换,就需要使用变换矩阵 |
| 投影矩阵 | 用于将3D坐标转换成二维屏幕坐标,实际线条也将在二维坐标下进行绘制 |
| 2D笛卡尔坐标系 | 只有x y轴的一个十字坐标系,用x y就能确定一个点的位置 |
| 3D笛卡尔坐标系 | 有 x y z轴的坐标系,需要用x y z才能确定一个点 |
| 视口 | 坐标系对应屏幕显示区域的范围(不一定会对应整个屏幕) |
| 投影方式 | 有且只有两种,正投影:不管远近,物体显示大小都是固定的(用于显示2D效果)。透视投影:遵循实际的远小近大的规则(用于显示3D效果)。 |
| 摄像机坐标系 | 观察者坐标系,相当于人的眼睛去看物体的时候,人眼睛的坐标系 |
| 物体坐标系 | 物体本身建立的一个坐标系 |
| 世界坐标系 | 物体所存在的世界建立成一个世界坐标系 |
2.1、坐标变换全局图
2.2、着色器的渲染流程
2.3、着色器渲染流程
三、OpenGL简单绘制一个三角形
使用如下工具:
- GLTools
- GLUT、
- GLShaderManager
实现流程:
- 初始化glut
- 设置窗口大小、窗口标题
- 注册相关方法回调(重塑、显示、键盘事件等)
- 显示
- 清除界面缓存信息(清除所有颜色等作用)
- 设置颜色,初始化渲染管理器
- 提交绘制
- 交换缓冲区
- 重塑
- 根据参数来设定窗口大小
- 键盘事件
- 监控键盘按钮事件
- 显示
- 检查是否可渲染
- 设置清屏颜色(背景色)
- 初始化渲染管理器
- 设置图形顶点数据
- 设置数据读取方式,传入顶点数据
- 结束绘制
//在窗口大小改变时,接收新的宽度&高度。
void changeSize(int w,int h)
{
//x,y 参数代表窗口中视图的左下角坐标,而宽度、高度是像素为表示,通常x,y 都是为0
glViewport(0, 0, w, h);
}
void RenderScene(void)
{
//清除一个或者一组特定的缓存区, 意义为初始化界面
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
//设置一组浮点数来表示红色
GLfloat vRed[] = {1.0,1.00,0.0,0.5f};
//传递到存储着色器,即GLT_SHADER_IDENTITY着色器,这个着色器只是使用指定颜色以默认笛卡尔坐标第在屏幕上渲染几何图形
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
//提交着色器
triangleBatch.Draw();
//将后台缓冲区进行渲染,然后结束后交换给前台
glutSwapBuffers();
}
void setupRC()
{
//设置清屏颜色(背景颜色)
glClearColor(0.98f, 0.40f, 0.7f, 1);
//初始化一个渲染管理器。没有着色器,在OpenGL 核心框架中是无法进行任何渲染的。
shaderManager.InitializeStockShaders();
//指定图形顶点
//在OpenGL中,三角形是一种基本的3D图元绘图原素。 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();
}
int main(int argc,char *argv[])
{
//初始化GLUT库,这个函数只是传说命令参数并且初始化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");
//注册重塑函数,用于改变窗口大小的事件
glutReshapeFunc(changeSize);
//注册显示函数,用于绘制显示界面
glutDisplayFunc(RenderScene);
/*
初始化一个GLEW库,确保OpenGL API对程序完全可用。
在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
*/
GLenum status = glewInit();
if (GLEW_OK != status) {
printf("GLEW Error:%s\n",glewGetErrorString(status));
return 1;
}
//设置我们的渲染环境
setupRC();
//类似于iOS的runloop
glutMainLoop();
return 0;
}