今天我们来介绍烘托器规划的要害概念;后边的章节经过特定的最佳实践和功用技能扩展了这些信息。
怎么可视化 OpenGL ES
可视化 OpenGL ES 规划的两个视角:作为客户端-服务器架构和作为管道。这两种观念都能够用于规划和评价运用程序的架构。
OpenGL ES 作为客户端-服务器架构
图 1将 OpenGL ES 可视化为客户端-服务器架构。您的运用程序将状况更改、纹路和极点数据以及烘托指令传达给 OpenGL ES 客户端。客户端将此数据转化为图形硬件能够理解的格式,并将它们转发给 GPU。这些进程会添加运用程序的图形功用开支。
图 1OpenGL ES 客户端-服务器架构
完结超卓的功用需求细心办理此开支。一个规划杰出的运用程序会下降它对 OpenGL ES 的调用频率,运用适合硬件的数据格式来最小化转化本钱,并细心办理它自己和 OpenGL ES 之间的数据流。
OpenGL ES 作为图形管道
图 2将 OpenGL ES 可视化为图形管道。您的运用程序装备图形管道,然后履行绘图指令将极点数据发送到管道。管道的接连阶段运转极点上色器来处理极点数据,将极点组装成图元,将图元光栅化成片段,运转片段上色器来核算每个片段的色彩和深度值,并将片段混合到帧缓冲区中以供显现。
图 2OpenGL ES 图形管线
运用管道作为心智模型来确定您的运用程序履行哪些作业来生成新框架。您的烘托器规划包含编写上色器程序来处理管线的极点和片段阶段,安排您输入这些程序的极点和纹路数据,以及装备驱动管线的固定功用阶段的 OpenGL ES 状况机。
图形管道中的各个阶段能够一起核算它们的成果——例如,您的运用程序或许准备新的图元,而图形硬件的独自部分对从前提交的几许图形履行极点和片段核算。可是,后边的阶段依赖于前面阶段的输出。假如任何流水线阶段履行太多作业或履行速度太慢,其他流水线阶段将处于闲暇状况,直到最慢的阶段完结其作业。规划杰出的运用程序会根据图形硬件功用平衡每个流水线阶段履行的作业。
OpenGL ES 版别和烘托器架构
iOS 支撑三个版别的 OpenGL ES。较新的版别提供了更大的灵敏性,允许您在不影响功用的状况下完结包含高质量视觉效果的烘托算法。
OpenGL ES 3.0
OpenGL ES 3.0 是 iOS 7 中的新功用。您的运用程序能够运用 OpenGL ES 3.0 中引进的功用来完结高档图形编程技能——以前只能在桌面级硬件和游戏操控台上运用——以取得更快的图形功用和有目共睹的视觉效果。
下面重点介绍 OpenGL ES 3.0 的一些要害特性。有关完整的概述,请参阅OpenGL ES API Registry中的OpenGL ES 3.0 标准。
OpenGL ES 上色言语版别 3.0
GLSL ES 3.0 添加了共同块、32 位整数和附加整数运算等新功用,用于在极点和片段上色器程序中履行更通用的核算任务。#version 330 es
要在上色器程序中运用新言语,您的上色器源代码必须以指令最初。OpenGL ES 3.0 上下文与为 OpenGL ES 2.0 编写的上色器坚持兼容。
多个烘托方针
经过启用多个烘托方针,您能够创立一起写入多个帧缓冲区附件的片段上色器。
此功用支撑运用高档烘托算法,例如推迟上色,您的运用首要烘托到一组纹路以存储几许数据,然后履行一个或多个从这些纹路读取的上色通道并履行光照核算以输出最终成果图片。由于这种办法预先核算了光照核算的输入,所以向场景添加大量光照的增量功用本钱要小得多。推迟上色算法需求多个烘托方针支撑,如图 3所示,以完结合理的功用。不然,烘托到多个纹路需求为每个纹路独自制作通道。
图 3片段上色器输出到多个烘托方针的示例
除了创立帧缓冲区方针中描述的进程之外,您还能够设置多个烘托方针。您无需为帧缓冲区创立单一色彩附件,而是创立多个。然后,调用该glDrawBuffers
函数来指定在烘托中运用哪些帧缓冲区附件,如清单 1所示。
清单 1设置多个烘托方针
// 将(从前创立的)纹路附加到帧缓冲区。
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _colorTexture, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _positionTexture, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, _normalTexture, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, _depthTexture, 0);
// 指定用于烘托的帧缓冲附件。
GLenum targets[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
glDrawBuffers(3, targets);
当您的运用程序宣布绘图指令时,您的片段上色器会确定为每个烘托方针中的每个像素输出什么色彩(或非色彩数据)。清单 2显现了一个根本的片段上色器,它经过分配方位与清单 1中设置的方位匹配的片段输出变量来出现给多个方针。
清单 2输出到多个烘托方针的片段上色器
#版别 300 es
uniform lowp sampler2D myTexture;
in mediump vec2 texCoord;
in mediump vec4 position;
in mediump vec3 normal;
layout(location = 0) out lowp vec4 colorData;
layout(location = 1) out mediump vec4 positionData;
layout(location = 2) out mediump vec4 normalData;
void main()
{
colorData = texture(myTexture, texCoord);
positionData = position;
normalData = vec4(normalize(normal), 1.0);
}
多个烘托方针也可用于其他高档图形技能,例如实时反射、屏幕空间环境光遮盖和体积照明。
改换反应
图形硬件运用针对矢量处理优化的高度并行化架构。您能够经过新的改换反应功用更好地利用此硬件,该功用可让您将极点上色器的输出捕获到 GPU 内存中的缓冲区方针中。您能够从一个烘托通道捕获数据以在另一个烘托通道中运用,或许禁用部分图形管道并运用改换反应进行通用核算。
一种获益于改换反应的技能是动画粒子效果。图 4说明晰烘托粒子体系的一般架构。首要,运用程序设置粒子模仿的初始状况。然后,关于烘托的每一帧,运用程序会运转一个模仿过程,更新每个模仿粒子的方位、方向和速度,然后制作表示粒子当时状况的视觉资源。
图 4粒子体系动画概览
传统上,完结粒子体系的运用程序在 CPU 上运转模仿,将模仿成果存储在极点缓冲区中以用于烘托粒子艺术。可是,将极点缓冲区的内容传输到 GPU 内存非常耗时。转化反应,经过优化现代 GPU 硬件中可用的并行架构的才能,更有效地处理了问题。
凭借改换反应,您能够规划烘托引擎来更有效地处理此问题。图 5显现了您的运用程序怎么装备 OpenGL ES 图形管道以完结粒子体系动画的概述。由于 OpenGL ES 将每个粒子及其状况表示为一个极点,所以 GPU 的极点上色器阶段能够一次运转多个粒子的模仿。由于包含粒子状况数据的极点缓冲区在帧之间重复运用,所以将数据传输到 GPU 内存的昂贵进程只在初始化时产生一次。
图 5运用改换反应的图形管线装备示例
-
在初始化时,创立一个极点缓冲区并用包含模仿中一切粒子的初始状况的数据填充它。
-
在 GLSL 极点上色器程序中完结您的粒子模仿,并经过制作包含粒子方位数据的极点缓冲区的内容来在每一帧运转它。
- 要在启用改换反应的状况下进行烘托,请调用该
glBeginTransformFeedback
函数。glEndTransformFeedback()
(在恢复正常绘图之前调用。) - 运用该
glTransformFeedbackVaryings
函数指定应经过改换反应捕获哪些上色器输出,并运用glBindBufferBase
orglBindBufferRange
函数和GL_TRANSFORM_FEEDBACK_BUFFER
缓冲区类型指定它们将被捕获到的缓冲区。 - 经过调用禁用光栅化(以及管道的后续阶段)
glEnable(GL_RASTERIZER_DISCARD)
。
- 要在启用改换反应的状况下进行烘托,请调用该
-
要烘托模仿成果以供显现,请运用包含粒子方位的极点缓冲区作为第二个绘图通道的输入,再次启用光栅化(和管道的其余部分)并运用适合烘托运用程序视觉内容的极点和片段上色器。
-
鄙人一帧中,运用上一帧的模仿过程输出的极点缓冲区作为下一模仿过程的输入。
其他能够从改换反应中获益的图形编程技能包含骨骼动画(也称为蒙皮)和光线跋涉。
OpenGL ES 2.0
OpenGL ES 2.0 提供了带有可编程上色器的灵敏图形管线,而且可用于当时一切的 iOS 设备。OpenGL ES 3.0 标准中正式引进的许多功用经过 OpenGL ES 2.0 扩展可用于 iOS 设备,因此您能够完结许多高档图形编程技能,一起坚持与大多数设备的兼容性。
OpenGL ES 1.1
OpenGL ES 1.1 只提供了一个根本的固定功用图形管线。iOS 支撑 OpenGL ES 1.1 主要是为了向后兼容。假如您正在保护 OpenGL ES 1.1 运用程序,请考虑为更新的 OpenGL ES 版别更新您的代码。
GLKit 框架能够帮助您从 OpenGL ES 1.1 固定功用管道过渡到更高版别。
规划高功用 OpenGL ES 运用程序
总而言之,一个规划杰出的 OpenGL ES 运用程序需求:
- 利用 OpenGL ES 管道中的并行性。
- 办理运用程序和图形硬件之间的数据流。
图 6建议了一个运用 OpenGL ES 向显现器履行动画的运用程序的流程。
图 6办理资源的 App 模型
当运用程序启动时,它做的榜首件事是初始化它不方案在运用程序的生命周期内更改的资源。理想状况下,运用程序将这些资源封装到 OpenGL ES 方针中。方针是创立能够在运用程序运转时(甚至是运用程序生命周期的一部分,例如游戏中的关卡的持续时刻)坚持不变的任何方针,用添加的初始化时刻交换更好的烘托功用。复杂的指令或状况更改应替换为可与单个函数调用一同运用的 OpenGL ES 方针。例如,装备固定功用管道或许需求几十个函数调用。相反,在初始化时编译图形上色器,并在运转时经过单个函数调用切换到它。
烘托循环处理您方案烘托到 OpenGL ES 上下文的一切项目,然后将成果出现给显现器。在动画场景中,每帧都会更新一些数据。在图 6所示的内部烘托循环中,运用程序在更新烘托资源(在进程中创立或修正 OpenGL ES 方针)和提交运用这些资源的绘图指令之间替换。这个内部循环的方针是平衡作业负载,使 CPU 和 GPU 并行作业,防止运用程序和 OpenGL ES 一起拜访相同的资源。在 iOS 上,假如修正不在帧的最初或结束履行,则修正 OpenGL ES 方针的本钱或许很高。
这个内部循环的一个重要方针是防止将数据从 OpenGL ES 仿制回运用程序。将成果从 GPU 仿制到 CPU 或许非常慢。假如稍后将仿制的数据用作烘托当时帧的进程的一部分,如中心烘托循环所示,您的运用程序会堵塞,直到完结一切从前提交的绘图指令。
在运用程序提交框架中所需的一切绘图指令后,它会将成果出现到屏幕上。非交互式运用程序会将最终图像仿制到运用程序内存以进跋涉一步处理。
最后,当您的运用程序准备好退出时,或许当它完结一项主要任务时,它会开释 OpenGL ES 方针,以便为自己或其他运用程序提供额定的资源。
总结一下这个规划的重要特点:
- 尽或许创立静态资源。
- 内部烘托循环在修正动态资源和提交烘托指令之间替换。尽量防止在帧的最初或结束修正动态资源。
- 防止将中心烘托成果读回您的运用程序。
防止同步和改写操作
OpenGL ES 标准不要求完结当即履行指令。一般,指令排队到指令缓冲区并在稍后由硬件履行。一般,OpenGL ES 会等到运用程序将许多指令排入行列后,才会将指令发送到硬件——批处理一般更有效。可是,某些 OpenGL ES 函数必须当即改写指令缓冲区。其他函数不只改写指令缓冲区,而且在回来对运用程序的操控之前堵塞,直到从前提交的指令完结。仅在需求该行为时才运用改写和同步指令。过度运用改写或同步指令或许会导致您的运用程序在等候硬件完结烘托时中止。
这些状况需求 OpenGL ES 将指令缓冲区提交给硬件履行。
- 该函数
glFlush
将指令缓冲区发送到图形硬件。它会堵塞直到指令提交到硬件,但不等候指令完结履行。 - 该函数
glFinish
改写指令缓冲区,然后等候一切从前提交的指令在图形硬件上完结履行。 - 检索帧缓冲区内容(例如
glReadPixels
)的函数也会等候提交的指令完结。 - 指令缓冲区已满。
有效运用 glFlush
在某些桌面 OpenGL 完结中,定时调用该glFlush
函数以有效地平衡 CPU 和 GPU 作业或许很有用,但在 iOS 中并非如此。iOS 图形硬件完结的 Tile-Based Deferred Rendering 算法依赖于一次缓冲场景中的一切极点数据,因此能够针对躲藏表面去除进行优化处理。一般,只要两种状况 OpenGL ES 运用程序应该调用glFlush
orglFinish
函数。
- 当您的运用程序移至后台时,您应该改写指令缓冲区,由于在您的运用程序处于后台时在 GPU 上履行 OpenGL ES 指令会导致 iOS 终止您的运用程序。
- 假如您的运用在多个上下文之间同享 OpenGL ES 方针(例如极点缓冲区或纹路),您应该调用该
glFlush
函数来同步对这些资源的拜访。例如,您应该glFlush
在一个上下文中加载极点数据后调用该函数,以确保其内容已准备好被另一个上下文检索。当与其他 iOS API(例如 Core Image)同享 OpenGL ES 方针时,此建议也适用。
防止查询 OpenGL ES 状况
调用glGet*()
,包含glGetError()
,或许需求 OpenGL ES 在检索任何状况变量之前履行从前的指令。这种同步迫使图形硬件与 CPU 同步运转,从而削减了并行的机会。为防止这种状况,请保护您自己需求查询的任何状况的副本,并直接拜访它,而不是调用 OpenGL ES。
当过错产生时,OpenGL ES 设置一个过错标志。这些和其他过错出现在 Xcode 中的 OpenGL ES Frame Debugger 或 Instruments 中的 OpenGL ES Analyzer 中。您应该运用这些东西而不是glGetError
函数,假如频频调用会下降功用。glCheckFramebufferStatus()
其他查询,例如glGetProgramInfoLog()
和glValidateProgram()
一般也仅在开发和调试时有用。您应该在运用的发布版别中省掉对这些函数的调用。
运用 OpenGL ES 办理您的资源
许多 OpenGL 数据能够直接存储在 OpenGL ES 烘托上下文及其关联的同享组方针中。OpenGL ES 完结能够自由地将数据转化为最适合图形硬件的格式。这能够显着提高功用,尤其是关于不常常更改的数据。您的运用程序还能够向 OpenGL ES 提供有关其方案怎么运用数据的提示。OpenGL ES 完结能够运用这些提示来更有效地处理数据。例如,静态数据能够放置在图形处理器能够轻松获取的内存中,甚至能够放置在专用图形内存中。
运用双缓冲防止资源抵触
当您的运用程序和 OpenGL ES 一起拜访 OpenGL ES 方针时,会产生资源抵触。当一个参与者企图修正另一个正在运用的 OpenGL ES 方针时,他们或许会堵塞,直到该方针不再运用。一旦他们开端修正方针,其他参与者或许在修正完结之前不能拜访该方针。或许,OpenGL ES 能够隐式仿制方针,以便两个参与者都能够持续履行指令。任何一个选项都是安全的,但每个选项都或许成为您运用程序的瓶颈。图 7显现了这个问题。在此示例中,有一个纹路方针,OpenGL ES 和您的运用程序都期望运用它。当运用程序尝试更改纹路时,它必须等到之前提交的绘图指令完结——CPU 与 GPU 同步。
图 7单缓冲纹路数据
为了处理这个问题,您的运用程序能够在更改方针和运用它绘图之间履行额定的作业。可是,假如您的运用程序没有它能够履行的额定作业,它应该显式创立两个相同大小的方针;当一个参与者读取一个方针时,另一个参与者修正另一个。图 8说明晰双缓冲办法。当 GPU 在一个纹路上运转时,CPU 会修正另一个纹路。初始启动后,CPU 或 GPU 都不会处于闲暇状况。虽然显现的是纹路,但此处理方案简直适用于任何类型的 OpenGL ES 方针。
图 8双缓冲纹路数据
关于大多数运用程序来说,双缓冲就足够了,但它要求两个参与者在大致相同的时刻内完结处理指令。为防止堵塞,能够添加更多缓冲区;这完结了传统的生产者-顾客模型。假如生产者在顾客完结指令处理之前完结,它会占用一个闲暇缓冲区并持续处理指令。在这种状况下,生产者只要在顾客严重落后时才会闲暇。
双缓冲和三缓冲权衡耗费额定的内存以防止管道中止。额定运用内存或许会对运用程序的其他部分造成压力。在 iOS 设备上,内存或许是稀缺的;您的规划或许需求平衡运用更多内存和其他运用程序优化。
留意 OpenGL ES 状况
OpenGL ES 完结保护一组复杂的状况数据,包含您运用glEnable
或glDisable
函数设置的开关、当时上色器程序及其共同变量、当时绑定的纹路单元以及当时绑定的极点缓冲区及其启用的极点特点。硬件有一个当时状况,该状况被推迟编译和缓存。切换状况的本钱很高,因此最好规划您的运用程序以尽量削减状况切换。
不要设置现已设置好的状况。启用某项功用后,无需再次启用。例如,假如您glUniform
多次调用具有相同参数的函数,OpenGL ES 或许不会查看是否现已设置了相同的共同状况。即便该值与当时值相同,它也会简单地更新状况值。
经过运用专用设置或封闭例程而不是将此类调用置于绘图循环中,防止设置不必要的状况。设置和封闭例程关于翻开和封闭完结特定视觉效果的功用也很有用——例如,在纹路多边形周围制作线框轮廓时。
用 OpenGL ES 方针封装状况
要削减状况更改,请创立将多个 OpenGL ES 状况更改收集到能够与单个函数调用绑定的方针的方针。例如,极点数组方针将多个极点特点的装备存储到单个方针中。请参阅运用极点数组方针兼并极点数组状况更改。
安排制作调用以最小化状况改变
更改 OpenGL ES 状况不会当即产生影响。相反,当您宣布绘图指令时,OpenGL ES 会履行运用一组状况值进行绘图所需的作业。您能够经过最小化状况更改来削减重新装备图形管道所花费的 CPU 时刻。例如,在您的运用程序中保留一个状况向量,而且仅当您的状况在两次制作调用之间产生改变时才设置相应的 OpenGL ES 状况。另一个有用的算法是状况排序——盯梢您需求履行的绘图操作以及每个操作所需的状况更改量,然后对它们进行排序以运用相同的状况接连履行操作。
OpenGL ES 的 iOS 完结能够缓存它需求的一些装备数据,以便在状况之间进行高效切换,但每个唯一状况集的初始装备需求更长的时刻。为了取得共同的功用,您能够“预热”您方案在设置例程中运用的每个状况集:
- 启用您方案运用的状况装备或上色器。
- 运用该状况装备制作少数极点。
- 改写 OpenGL ES 上下文,以便在预热阶段不显现绘图。