阅读 574

iOS视觉 OpenGL初探

一、图形API

1. 图形API简介

  • OpenGL是一个跨编程语言、跨平台的编程图形程序接口
  • OpenGL ESOpenGL三维图形API的自己,针对 手机、PDA和游戏主机等嵌入式设备而设计的,去除了很多不必要觉和性能较低的API接口
  • DirectX是由很多API组成的, DirectX并不是一个单纯的图形API。最重要的是 DirectX是属于Windows上一个多媒体处理框架,并不支持Windows以外的平台
  • Metal是Apple为游戏开发者退出的新的平台技术,该技术能为3D图像提高10倍的渲染性能

2. 图形API作用

简单来说就是 实现图形的底层渲染

  • 比如在游戏开发中对于游戏场景/游戏人物的渲染
  • 比如在音视频开发中对于视频解码后的数据渲染
  • 比如在地图引擎对于地图上的数据渲染
  • 比如在动画中实现动画的绘制
  • 比如在视频处理中对于视频加上滤镜效果

OpenGL/OpenGL ES/Metal 在任何项目中解决问题的本质——利用 GPU芯片来高效渲染图形图像

图形API是iOS开发者唯一接近GPU的方式

二、OpenGL下专业名词

  • OpenGL上下文 Context
    • 上下文是一个非常庞大的状态机,保存了OpenGL中的各种状态
    • 不管在哪个语言中,都是类似C语言一样面向过程的函数
  • OpenGL状态机描述了一个对象的生命周期经历各种状态发生转变时的动因、条件及转变中所执行的活动
    • 有记忆功能,能记住其当前的状态(如当前所使用的颜色、是否开始了混合功能等)
      • glClearColor(1,1,1,1)设置颜色
      • glEable(GL_DEPTH_TEST)开启深度测试
      • glEable(GL_BLEND)开启混合
    • 可以接收输入,根据输入的内容和自己的原先状态,修改自己当前状态,并且可以有对应输出
    • 当进入特殊状态(停机状态)时便不再接收输入,停止工作
  • 渲染:将图形/图像数据转换成3D空间图像的操作叫做渲染(Rendering)即 数据->可视化界面的过程,也就是我们口中所说的 绘制
  • 顶点数组(VertexArray)和 顶点缓冲区(VertexBuffer):
    • 顶点数据是由GPU处理的
    • 顶点数组是存在内存中,GPU通过操作内存来处理顶点数据
    • 顶点缓冲区存在显卡显存中,使得GPU的操作更为简单
    • 在调用绘制方法的时候,直接有内存传入顶点数据,也就是这部分数据之前是存储在内存中的,被称为顶点数组;性能更高的做法是,提前分配一块显存,将顶点数据预先传入到显存当中,这块显存就是顶点缓冲区
  • 管线:可以理解为流水线。在OpenGL下渲染图形,就会经历一个一个节点,这样的操作可以理解为管线。之所以称为管线是因为显卡在处理数据的时候是按照一个固定的顺序来的
  • 固定管线/存储着色器:在早期OpenGL版本中,开发者只需要传入相应的参数,就能快速完成图形的渲染。开发者只需要调用API使用封装好的固定shader程序段,并不需要关注底层实现原理
  • 着色器程序Shader是一段程序代码,是用来操作GPU进行计算的,主要的着色器有:
    • 顶点着色器(VertexShader)
    • 片元着色器(Metal叫片元函数)/片段着色器(FragmentShader)/像素着色器(PixelShader)
    • 几何着色器(GeometryShader)
    • 曲面细分着色器(TessellationShader)
    • 在绘制的时候首先由 顶点着色器对传入的顶点数据进行运算,将顶点转换为图元;然后进行 光栅化转化为栅格化数据;最后传入 片元着色器进行运算
  • 顶点着色器(VertexShader)
    • 用来处理图形每个顶点变换——旋转/平移/投影
    • 每一个顶点都会执行一次
  • 片元着色器(FragmentShader)
    • 用来处理图形中每个像素点的颜色计算和填充
    • 每个像素都会执行一次片元着色器(并行执行)
  • GLSL(OpenGL Shading Language)
    • 是用来在OpenGL中着色编程的语言,即开发人员写的短小的自定义程序
    • 代替了固定渲染管线,使渲染管线中不同层次具有可编程性,比如:视图转换、投影转换等
    • 用来操作 顶点着色器片元着色器
  • 光栅化(Rasterization)
    • 是把顶点数据转换成片元的过程,具有将图转化为一个个栅格组成的图像的作用
    • 其实就是将 几何图元变为二维图像的过程。该过程包含了两部分工作:决定窗口坐标中的那些整形栅格区域被基本图元占用;分配一个颜色值和一个深度值到各个区域
    • 把物体的数学描述以及与物体相关的颜色信息转换为屏幕上用于对应位置的像素及用于填充像素的颜色,这个过程称为光栅化
  • 纹理:即图片(位图)
  • 混合:两种颜色的视图叠在一起后的颜色就叫混合
  • 变换矩阵(Transformation):图形发生平移、缩放、旋转变换
  • 投影矩阵(Projection):将3D坐标转换为二维屏幕坐标
  • 渲染上屏/交换缓冲区(SwapBuffer)
    • 常规的OpenGL程序至少都会有两个缓冲区,显示在屏幕上的成为屏幕缓冲区,没有显示的成为离屏缓冲区。在一个缓冲区渲染完成之后,通过将屏幕缓冲区和离屏缓冲区交换,实现图像在屏幕上的显示
    • 为了防止交换缓冲区的时候屏幕上下区域的图像分属于两个不同的帧,因此交换一般会等待显示器刷新完成的信号,在显示器两次刷新的间各种进行交换,这个信号就成为 垂直同步信号,这个技术成为 垂直同步

三、OpenGL坐标系统解析

  • 视口:显示的窗口区域 ,OpenGL使用glViewPort来设置视口
  • 投影方式
    • 正投影:用来渲染平面图形(远近物体大小一样)
    • 透视投影:用来渲染立体图形(远小近大)

MVP矩阵:Model、View、Projection

  • 2D笛卡尔坐标系:拥有x轴、y轴的平面坐标系(用来描述平面图形)
  • 3D笛卡尔坐标系:拥有x轴、y轴、z轴(z轴表示深度)的立体坐标系(用来描述立体图形)
  • 摄像机坐标系/照相机坐标系/观察者坐标系:自身为原点,向上为+y,向前为+z,向右为+x
  • 标准化设备坐标系(NDC):x轴、y轴的范围都是从-1到1的坐标系
  • 左手坐标系、右手坐标系:拇指方向为+x,食指方向为+y,手心方向为+z
    • 规范化设备坐标是左手坐标系
    • 物体/世界/照相机坐标系都是右手系坐标


常用坐标系的比较

  • 世界坐标系:系统的、绝对的坐标系
  • 物体空间坐标系:针对于物体个体的坐标系
  • 摄像机坐标系:观察的一个角度
  • 惯性坐标系:是 物体坐标系 和 世界坐标系 转换过程中的一个中转站,为了方便物体坐标系转换到世界坐标系


坐标系对应的空间系

  • 局部空间(物体空间坐标系) :在描述这个物体本身
  • 世界空间:在大环境中的位置
  • 观察空间:观察坐标系空间
  • 裁剪空间:超过部分要裁剪
  • 屏幕空间:设备

四、理解图片从文件渲染屏幕的过程

1. 渲染过程中的坐标变换

image.png
物体坐标/对象坐标->(模型变换)->世界坐标->(视觉变换)->观察者坐标/摄像机坐标->(投影变换)->裁剪坐标->(透视除法)->规范化设备坐标->(视口变化)->屏幕坐标
这个过程中以下过程是可以开发者定义的:

  • 顶点信息(Attribute)
  • 模型变换(Vertex)
  • 模型空间(Model Space)
  • 世界空间(World Space)
  • 观察空间(Camera Space)
  • 裁剪空间(Clip Space)

image.png

2. 着色器的渲染流程image.png

  • 顶点着色器处理顶点数据
  • 细分着色器描述物体的形状(OpenGL3.0已经不开放细分着色器的API)
  • 几何着色器 决定输出的图元类型和个数(不需要关心细节)
  • 图元设置明确图元的类型,是三条线还是三角形(三个顶点并一定是三角形)
  • 光栅化几何图元转为二维图像(像素点)
  • 片元着色器填充像素点的颜色、深度值等

3. 图片渲染流程

具体请见iOS中图片的解压缩到渲染过程

  • 图片从文件到屏幕过程image.png
    • CPU计算视图frame、图片解码,然后通过数据总线交给GPU
    • GPU负责纹理混合、顶点变换与计算、像素点的填充计算,渲染到帧缓冲区
    • 时钟信号:垂直同步信号V-Sync/水平同步信号H-Sync
    • iOS设备双缓冲机制:显示系统通常会引入两个帧缓冲区
  • 图片加载的工作流程
    • 使用 +imageWithContentOfFile:从磁盘中加载一张图片并不会解压缩图片
    • 显示图片时CPU才会去解码图片
    • 隐式的 CATransaction捕获到了 UIImageView图层数的变化
    • 在主线程的下一个 Runloop到来时, Core Animation提交了这个隐式的 transaction
    • 渲染流程:即4.2流程
  • 为什么解压缩图片
    • 位图就是一个像素数组,数组中的每个像素就代表着图片中的一个点(jpeg和png都是位图)
    • 在将磁盘中的图片渲染到屏幕之前,必须先要得到图片的原始像素数据,才能执行后续的绘制操作
  • 解压缩原理
    • 使用CGBitmapContextCreate 对图片进行重新绘制,得到一张新的解压缩后的位图