一、认识OpenGL相关概念
OpenGL:用于创建3d图像的编程接口,用于PC端,是一个跨编程语言、跨平台的图形编程程序接口,只操作GPU芯片来保证跨平台性
OpenGL ES:是OpenGL的子集,主要针对手机、PAD和游戏主机等嵌入式设备使用,用于移动端,使用的语言是GLSL,是着色器语言,iOS框架是GLKit
DirectX:仅用于Windows平台的多媒体处理框架
Metal:是Apple在2014年推出的3D渲染框架,性能优于OpenGL ES,使用的语言是Metal shading language,iOS框架是Metal kit3D图形:有3个维度的图形,屏幕上的3D效果是2D+透视呈现出来的
三维物体:高度、宽度、深度(在坐标轴上用z轴表);二维物体:高度、宽度3D术语:
- 光栅化:图形是由像素点构成,将像素点显示到屏幕上的过程称为光栅化;
- 着色:渲染图形的时候给像素点添加颜色;
- 纹理贴图:将纹理图片附着到需要绘制的图像上;
- 混合:颜色混合效果;
- 坐标系:
2D笛卡尔坐标系
- x轴与y轴垂直定义一个xy面,绘制平面图形
3D笛卡尔坐标系
- x轴、y轴、z轴,z轴是垂直于xy面,z轴表示深度,绘制3D图形
渲染:图形图像数据转换成3D空间图像的操作,如图形绘制、图片显示到屏幕等数据可视化操作
线框渲染:平面通过线条形成;
纯色渲染:通过颜色形成形状;
纹理渲染:添加纹理到形状上;
OpenGL里只有点、线、三角形,通过这三者来拼成各种形状着色器:
- 图元:组成图像的基本单元
- OpenGL渲染管线:一系列有序的处理阶段的序列,用于把我们应用中的数据转化到OpenGL,生成一个最终图像的过程
- GLSL:专门为图形开发设计的编程语言
着色器分类:
- 顶点着色器(必要)
- 细分着色器(可选)
- 几何着色器(可选)
- 片元着色器(必选),Metal里称为片元函数
着色器渲染流程:
顶点数据->顶点着色器(必要,接收顶点数据,单独处理每个顶点)
->细分着色器(可选,描述物体的形状,在管线中生成新的几何体处理(平顺)模型,生成最终状态;对所有的图像进行修改几何图元类型或放弃所有凸缘)
->几何着色器
->图元设置(图形的形状)
->剪切(剪切视口之外的绘制)
->光栅化(输入图元的数学描述,转化为与屏幕对应的位置像素片元)
->片元着色器(必选,给像素点添加颜色)
->显示效果
CPU与GPU的通信过程
CPU将数据交给GPU处理需要通过OpenGL,OpenGL的buffers缓存区域(颜色缓存区、顶点缓存区、深度缓存区)解决数据饥饿问题,CPU和GPU的数据处理能力高于内存,CPU和GPU控制的内存是分开的,不能从一块内存复制到另一块内存执行,因为复制数据速度很慢,CPU和GPU都不能操作数据,也避免引起错误。OpenGL使用客户端--服务端的形式实现,客户端是我们编写的代码,服务端是计算机图形硬件厂商所提供的OpenGL实现,OpenGL ES框架相当于Client去发起图像处理请求,GPU相当于Server负责处理图形操作。
二、了解OpenGL
- OpenGL简介:OpenGL是跨平台计算机图形应用程序接口规范,用于可视化领域,是一种图形应用程序编程接口,图形API本质是利用GPU芯片来高效渲染图形图像
tips:CPU是串行运算,时间片快速切换调度任务,处理复杂的逻辑判断,C语言、C++代码是编译运行在CPU上的;GPU是并发运算,通过很多的计算单元执行任务,处理数据更高效,OpenGL语言GLSL,着色器语言,编译运行在GPU上
- OpenGL用处:实现图形的底层渲染
- 视频、图形、图片处理:音视频开发中视频解码后的数据渲染和滤镜处理、核心动画的绘制、地图引擎中地图上数据的渲染
- 2D/3D游戏引擎开发:游戏开发中游戏场景和人物的渲染
- 科学可视化
- 医学软件开发
- CAD(计算机辅助技术)
- 虚拟实境(AR、VR)
- AI人工智能
- OpenGL专业名词
OpenGL上下文(context)
- OpenGL上下文(context):是一个非常庞大的状态机,保存了OpenGL中的各种状态
- OpenGL函数:类似C语言是面向过程的函数,本质是操作context中的某个状态或对象
- 高效处理方案:由于OpenGL特性,切换上下文会产生较大开销,但是不同的绘制模块可能需要使用完全独立的状态管理,所以在应用程序中分别创建不同的上下文,在不同线程中使用不同的上下文,上下文之间共享纹理、缓冲区等资源
OpenGL状态机
- OpenGL状态机:状态机描述了一个对象在其生命周期内所经历的各种状态以及状态间的转变、转变的动因、条件及转变中所执行的活动
- 特点:有记忆功能,记住当前的状态;接收输入并结合输入内容和原先状态修改当前状态后输出;进入特殊状态如停机时,不再接收输入,直接停止工作
- OpenGL可以记录自己的状态,如当前所使用的颜色、是否开启混合等
- OpenGL可以接收输入,调用OpenGL函数时相当于OpenGL接收输入,如调用glColor3f,OpenGL会修改当前颜色的状态
- OpenGL停止工作,程序退出前OpenGL会先停止工作
顶点数组(VertexArray)和顶点缓冲区(VertexBuffer)
- 顶点数据由GPU处理
- 顶点数组:OpenGL中图像都是由图元组成的,在OpenGL ES中,有3种类型的图元:点、线、三角形,顶点数组用来存储他们的顶点数据,顶点数组存储在内存中
- 顶点缓冲区:高性能的做法是提前分配一块显存,将顶点数据预先传入到显存当 中,这部分的显存,就被称为顶点缓冲区,顶点缓冲区存储在显卡的显存中
管线
- 显卡在处理数据的时候是按照一个固定的顺序来执行的,而且严格按照这个顺序,像流水线一样,所以称之为管线,这个顺序就是渲染流程。
- 固定管线/存储着色器:OpenGL封装了很多种着色器程序块,内置一段包含了光 照、坐标变换、裁剪等等诸多功能的固定shader程序,来帮助开发者来完成图形的渲染
着色器程序shader
- shader:自定义可编程管线,操作GPU芯片计算数据。
OpenGL ES3.0只支持顶点着色器和片元着色器这两个最基础的着色器。 - OpenGL处理shader的流程
OpenGL在处理shader时,通过编译、链接等步骤,⽣成了着色器程序(glProgram),着色器程序同时包含了顶点着⾊器和⽚元着⾊器的运算逻辑。在OpenGL进行绘制的时候,首先由顶点着⾊器对传⼊的顶点数据进⾏运算,再通过图元装配,将顶点转换为图元,然后进⾏光栅化,将图元这种⽮量图形转换为栅格化数据,最后将栅格化数据传入⽚元着⾊器中进⾏运算。⽚元着⾊器会对栅格化数据中的每一个像素进行运算,并决定像素的颜⾊,从而形成一个图形图像。
- 顶点着色器VertexShader:计算顶点属性,用来处理图形每个顶点的变化,如旋转、平移、投影等
- 片元着色器FragmentShader:计算像素颜色,用来处理图形中每个像素点颜色的计算和填充
GLSL语言
- OpenGL的着色语言,在GPU上执行
- GLSL的着色器代码分两个部分:顶点着色器VertexShader和片元着色器FragmentShader
光栅化Rasterization
- 把顶点数据转换为片元的过程,将图转化为一个个栅格组成的图像,把物体的数学描述以及颜色信息转换为屏幕上对应位置的像素及填充像素的颜色
纹理
- 实际是位图,可以理解为图片,渲染图形时填充图片使场景更加逼真
混合Blending
- 两种颜色重合时的颜色计算,使用颜色混合方程式
变换矩阵Transformation
- 图形发生平移、缩放、旋转等会使用变换矩阵
投影矩阵Projection
- 将3D坐标转换为二维屏幕坐标,实际线条也在二维坐标下进行绘制
渲染缓冲区
- 将图像直接渲染到窗口对应的渲染缓冲区,图像就可以显示到屏幕上
但是每个窗口只有一个缓冲区,如果在绘制过程中屏幕进行了刷新,窗⼝可能显示出不完整的图像,所以常规的OpenGL程序至少都会有两个缓冲区,显示在屏幕上的称为屏幕缓冲区,没有显示的称为离屏缓冲区,在一个缓冲区渲染完成之后,通过将屏幕缓冲区和离屏缓冲区交换,实现图像在屏幕上的显示。由于显示器的刷新一般是逐⾏进⾏的,因此为了防⽌交换缓冲区的时候屏幕上下区域的图像分属于两个不同的帧,因此交换一般会等待显示器刷新完成的信号,在显示器两次刷新的间隔中进⾏交换,这个信号就被称为垂直同步信号,这个技术被称为垂直同步。使⽤了双缓冲区和垂直同步技术之后,由于总是要等待缓冲区交换之后再进⾏下一帧的渲染,使得帧率无法完全达到硬件允许的最高水平。为了解决这个问题,引⼊了三缓冲区技术,在等待垂直同步时,来回交替渲染两个离屏的缓冲区,而垂直同步发生时,屏幕缓冲区和最近渲染完成的离屏缓冲区交换,实现充分利用硬件性能的⽬的。
- 坐标系相关
视口
- glviewPort()设置视口,映射需要显示的窗口区域,一般视口与窗口等大
投影
透视投影:观察者距离的远近也就是视角的大小会影响物体的大小,远小近大,立体图形一般使用透视投影
GLFrustum::SetPerspective(float fFov, float fAspect, float fNear, float fFar); // fFov:垂直方向上的视场角度;fAspect:窗口的宽度与高度的纵横比;fNear:近裁剪面距离;fFar:远裁剪面距离;纵横比=宽(w)/高(h)
正投影:远近不影响物体大小,平面图形一般使用正投影
GLFrustum::SetOrthographic(GLfloat xMin, GLfloat xMax, GLfloat yMin, GLfloat yMax, GLfloat zMin, GLfloat zMax,); // 正投影设置获取投影矩阵,设置x、y、z轴上的最小和最大值,是一个区间,一般设置的区间没有太大调整
摄像机坐标系
- 观察者的位置,一般在原点(0,0,0),
标准化设备坐标系NDC范围是[-1, 1]
世界坐标系:描述世界空间,在大的环境里的位置,表示系统绝对坐标系
物体坐标系:局部空间坐标系、物体空间坐标系,描述局部空间、物体本身,物体所在的位置坐标系
摄像机坐标系:观察空间,描述观察坐标系的空间,观察者角度的坐标系
惯性坐标系:物体坐标系的旋转,是世界坐标系平移后的坐标系
裁剪空间:超过范围后需要裁剪
屏幕空间:描述屏幕显示坐标系
MVP矩阵:描述物体发生的变化,model、view、projection(投影)
图形从坐标顶点到渲染到屏幕需要经历的坐标系转换:
物体坐标(object space)->模型变换(modeling transformation)->世界坐标(world space)
->视变换(viewing transformation)(观察者)->观察者坐标(eye space)
->投影变换(projection transformation)->裁剪坐标(clip space)
->透视除法(perspective divide)->规范化设备坐标(NDC space)
->视口变换(ViewPort mapping)->屏幕坐标(screen space)
三、OpenGL环境搭建与测试
-
准备资料
-
创建MacOS工程,工程名字自己起,使用Objective-C
-
在General最底部Linked Frameworks and Libraries添加两个系统库OpenGL.framework 和 GLUT.framework
-
添加准备好的资料include和libGLTools.a到工程中
在Bulid Settings找到Header Search Paths,拖入GLTools.h 和 glew.h
-
将libGLTools.a 直接拖到工程的==Frameworks== 文件夹里面,在Linked Frameworks and Libraries里将libGLTools.a删除然后重新拖过来添加上,另外删除文件:AppDelegate.h 、 AppDelegate.m 、ViewController.h 、 ViewController.m 、 main.m ; 创建 main.cpp文件
- 添加代码到main.cpp
#include "GLShaderManager.h"
#include "GLTools.h"
#include
GLBatch triangleBatch;
GLShaderManager shaderManager;
//窗口大小改变时接受新的宽度和高度,其中0,0代表窗口中视口的左下角坐标,w,h代表像素
void ChangeSize(int w,int h)
{
glViewport(0,0, w, h);
}
//为程序作一次性的设置
void SetupRC()
{
//设置背影颜色
glClearColor(0.1f,0.1f,0.1f,1.0f);
//初始化着色管理器
shaderManager.InitializeStockShaders();
//设置三角形,其中数组vVert包含所有3个顶点的x,y,笛卡尔坐标对。
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();
}
//开始渲染
void RenderScene(void)
{
//清除一个或一组特定的缓冲区glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
//设置一组浮点数来表示红色
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
//传递到存储着色器,即GLT_SHADER_IDENTITY着色器,这个着色器只是使用指定颜色以默认笛卡尔坐标第在屏幕上渲染几何图形
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
//提交着色器
triangleBatch.Draw();
//将在后台缓冲区进行渲染,然后在结束时交换到前台
glutSwapBuffers();
}
int main(int argc,char* argv[])
{
//设置当前工作目录,针对MAC OS X
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");
//注册回调函数
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
//驱动程序的初始化中没有出现任何问题。
GLenum err = glewInit();
if(GLEW_OK != err) {
fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
return 1;
}
//调用SetupRC
SetupRC();
glutMainLoop();
return 0;
}
- 将GLBatch.h、GLShaderManager.h、GLTools.h、GLTriangleBatch.h中报错的头文件引入由<>改为"",编译时由<>系统引入,改为""普通引入,点击运行窗口出现一个三角形