OpenGL ES 프로그래밍 가이드

OpenGL ES 소개

중요  OpenGL ES는 iOS 12에서 더 이상 사용되지 않습니다. GPU에서 고성능 코드를 생성하려면 대신 Metal 프레임워크를 사용하십시오. 금속 을(를) 참고하십시오 .

OpenGL( Open Graphics Library ) 은 2D 및 3D 데이터를 시각화하는 데 사용됩니다. 2D 및 3D 디지털 콘텐츠 생성, 기계 및 건축 설계, 가상 프로토타이핑, 비행 시뮬레이션, 비디오 게임 등과 같은 응용 프로그램을 지원하는 다목적 개방형 표준 그래픽 라이브러리입니다. OpenGL을 사용하여 3D 그래픽 파이프라인에 데이터를 구성하고 제출합니다. 정점은 변형되고 조명되고 기본 요소로 조합된 다음 래스터화되어 2D 이미지를 생성합니다. OpenGL은 함수 호출을 기본 그래픽 하드웨어로 보낼 수 있는 그래픽 명령으로 변환하도록 설계되었습니다. 이 기본 하드웨어는 그래픽 명령 처리 전용이므로 OpenGL 그리기는 일반적으로 매우 빠릅니다.

임베디드 시스템용 OpenGL(OpenGL ES) 은 중복 기능을 제거하고 모바일 그래픽 하드웨어에서 더 배우기 쉽고 구현하기 쉬운 라이브러리를 제공하는 OpenGL의 단순화된 버전입니다.

이미지.png

첫눈에

OpenGL ES를 사용하면 응용 프로그램이 기본 그래픽 프로세서의 기능을 활용할 수 있습니다. iOS 장치의 GPU는 복잡한 2D 및 3D 그리기는 물론 최종 이미지의 모든 픽셀에 대한 복잡한 음영 계산을 수행할 수 있습니다. 응용 프로그램의 설계 요구 사항에 따라 GPU 하드웨어에 대한 가장 직접적이고 포괄적인 액세스가 필요한 경우 OpenGL ES를 사용해야 합니다. OpenGL ES의 일반적인 클라이언트에는 3D 그래픽을 렌더링하는 비디오 게임 및 시뮬레이션이 포함됩니다.

OpenGL ES는 낮은 수준의 하드웨어 중심 API입니다. 가장 강력하고 유연한 그래픽 처리 도구를 제공하지만 학습 곡선이 가파르고 애플리케이션의 전체 디자인에 상당한 영향을 미칩니다. 보다 전문적인 사용을 위해 고성능 그래픽이 필요한 응용 프로그램의 경우 iOS는 다음과 같은 몇 가지 상위 수준 프레임워크를 제공합니다.

  • Sprite Kit 프레임워크는 2D 게임 제작에 최적화된 하드웨어 가속 애니메이션 시스템을 제공합니다. (참고 * SpriteKit 프로그래밍 가이드 *.)
  • Core Image 프레임워크는 스틸 및 비디오 이미지에 대한 실시간 필터링 및 분석을 제공합니다. (참고 * 코어 이미지 프로그래밍 가이드 *.)
  • Core Animation은 모든 iOS 애플리케이션을 위한 하드웨어 가속 그래픽 렌더링 및 애니메이션 인프라와 복잡한 사용자 인터페이스 애니메이션을 간단하게 구현할 수 있도록 하는 간단한 선언적 프로그래밍 모델을 제공합니다. (참고 * 코어 애니메이션 프로그래밍 가이드 *.)
  • UIKit 프레임워크의 기능을 사용하여 애니메이션, 물리 기반 역학 및 기타 특수 효과를 Cocoa Touch 사용자 인터페이스에 추가할 수 있습니다.

OpenGL ES는 iOS에서 구현된 플랫폼 중립 API입니다.

OpenGL ES는 C 기반 API이기 때문에 이식성이 매우 뛰어나고 널리 지원됩니다. C API로서 Objective-C Cocoa Touch 애플리케이션과 원활하게 통합됩니다. OpenGL ES 사양은 창 계층을 정의하지 않으며, 대신 호스트 운영 체제 는 명령을 받아들이 는 OpenGL ES 렌더링 컨텍스트 와 그리기 명령의 결과가 기록되는 프레임 버퍼 를 생성하는 기능을 제공해야 합니다. iOS에서 OpenGL ES를 사용하려면 iOS 클래스를 사용하여 그리기 표면을 설정 및 렌더링하고 플랫폼 중립 API를 사용하여 콘텐츠를 렌더링해야 합니다.

GLKit은 그리기 표면 및 애니메이션 지원을 제공합니다.

由 UIKit 框架定义的视图和视图控制器控制 iOS 上视觉内容的呈现。GLKit 框架提供了这些类的 OpenGL ES 感知版本。当您开发 OpenGL ES 应用程序时,您使用一个GLKView对象来渲染您的 OpenGL ES 内容。您还可以使用GLKViewController对象来管理视图并支持对其内容进行动画处理。

iOS 支持替代渲染目标

除了绘制内容以填充整个屏幕或视图层次结构的一部分之外,您还可以将 OpenGL ES 帧缓冲区对象用于其他渲染策略。iOS 实现了标准的 OpenGL ES 帧缓冲区对象,您可以使用这些对象渲染到屏幕外缓冲区或纹理,以便在 OpenGL ES 场景的其他地方使用。此外,iOS 上的 OpenGL ES 支持渲染到 Core Animation 层(CAEAGLLayer类),然后您可以将其与其他层组合以构建应用程序的用户界面或其他视觉显示。

应用程序需要额外的性能调整

图形处理器是针对图形操作优化的并行设备。为了在您的应用程序中获得出色的性能,您必须仔细设计您的应用程序以向 OpenGL ES 提供数据和命令,以便图形硬件与您的应用程序并行运行。调整不佳的应用程序会强制 CPU 或 GPU 等待对方完成处理命令。

您应该设计您的应用程序以有效地使用 OpenGL ES API。完成构建应用程序后,使用 Instruments 微调应用程序的性能。如果您的应用程序在 OpenGL ES 中遇到瓶颈,请使用本指南中提供的信息来优化您的应用程序的性能。

Xcode 提供了一些工具来帮助您提高 OpenGL ES 应用程序的性能。

OpenGL ES 可能无法在后台应用程序中使用

在后台运行的应用程序可能不会调用 OpenGL ES 函数。如果您的应用程序在后台访问图形处理器,它会被 iOS 自动终止。为避免这种情况,您的应用应在移入后台之前刷新之前提交给 OpenGL ES 的所有待处理命令,并避免调用 OpenGL ES,直到将其移回前台。

OpenGL ES 对多线程应用程序施加了额外的限制

设计应用程序以利用并发性有助于提高应用程序的性能。如果您打算向 OpenGL ES 应用程序添加并发性,您必须确保它不会同时从两个不同的线程访问相同的上下文。

为 iOS 构建 OpenGL ES 应用程序的清单

OpenGL ES 规范定义了一个平台中立的 API,用于使用 GPU 硬件来渲染图形。实现 OpenGL ES 的平台提供了用于执行 OpenGL ES 命令的渲染上下文、用于保存渲染结果的帧缓冲区以及一个或多个呈现帧缓冲区内容以供显示的渲染目标。在 iOS 中,EAGLContext该类实现了一个渲染上下文。iOS 仅提供一种类型的帧缓冲区,即 OpenGL ES 帧缓冲区对象,并且GLKViewCAEAGLLayer类实现渲染目标。

在 iOS 中构建 OpenGL ES 应用程序需要考虑几个因素,其中一些是 OpenGL ES 编程通用的,而一些是特定于 iOS 的。请按照此清单和以下详细部分开始:

  1. 确定哪些 OpenGL ES 版本具有适合您的应用程序的功能集,并创建 OpenGL ES 上下文。
  2. 在运行时验证设备是否支持您要使用的 OpenGL ES 功能。
  3. 选择渲染 OpenGL ES 内容的位置。
  4. 确保您的应用在 iOS 中正确运行。
  5. 实现你的渲染引擎。
  6. 使用 Xcode 和 Instruments 调试您的 OpenGL ES 应用程序并调整它以获得最佳性能。

选择要支持的 OpenGL ES 版本

确定您的应用程序是否应支持 OpenGL ES 3.0、OpenGL ES 2.0、OpenGL ES 1.1 或多个版本。

  • OpenGL ES 3.0 是 iOS 7 中的新功能。它添加了许多新功能,可实现更高的性能、通用 GPU 计算技术以及更复杂的视觉效果,而这些视觉效果以前只能在桌面级硬件和游戏控制台上实现。
  • OpenGL ES 2.0 是 iOS 设备的基准配置文件,具有基于可编程着色器的可配置图形管道。
  • OpenGL ES 1.1 仅提供基本的固定功能图形管道,并且在 iOS 中可用主要是为了向后兼容。

您应该针对支持与您的应用程序最相关的功能和设备的一个或多个 OpenGL ES 版本。要了解有关 iOS 设备的 OpenGL ES 功能的更多信息,请阅读*iOS 设备兼容性参考*。

验证 OpenGL ES 功能

*iOS 设备兼容性参考*总结了发货 iOS 设备上可用的功能和扩展。但是,为了让您的应用程序能够在尽可能多的设备和 iOS 版本上运行,您的应用程序应始终在运行时查询 OpenGL ES 实现的功能。

要确定实现特定的限制,例如最大纹理大小或最大顶点属性数,请使用其数据类型的适当函数查找相应标记的值(例如,MAX_TEXTURE_SIZEMAX_VERTEX_ATTRIBS在标头中找到)。gl.h``glGet

要检查 OpenGL ES 3.0 扩展,请使用glGetIntegervglGetStringi函数,如以下代码示例所示:

BOOL CheckForExtension(NSString *searchName)
{
    // Create a set containing all extension names.
    // (For better performance, create the set only once and cache it for future use.)
    int max = 0;
    glGetIntegerv(GL_NUM_EXTENSIONS, &max);
    NSMutableSet *extensions = [NSMutableSet set];
    for (int i = 0; i < max; i++) {
        [extensions addObject: @( (char *)glGetStringi(GL_EXTENSIONS, i) )];
    }
    return [extensions containsObject: searchName];
}

要检查 OpenGL ES 1.1 和 2.0 扩展,请调用glGetString(GL_EXTENSIONS)以获取所有扩展名称的空格分隔列表。

选择渲染目标

在 iOS 中,帧缓冲区对象存储绘图命令的结果。(iOS 不实现窗口系统提供的帧缓冲区。)您可以通过多种方式使用帧缓冲区对象的内容:

  • GLKit 框架提供了一个绘制 OpenGL ES 内容并管理其自己的帧缓冲区对象的视图,以及一个支持动画 OpenGL ES 内容的视图控制器。使用这些类来创建全屏视图或将 OpenGL ES 内容适合 UIKit 视图层次结构。
  • 该类CAEAGLLayer提供了一种将 OpenGL ES 内容作为核心动画层组合的一部分进行绘制的方法。使用此类时,您必须创建自己的帧缓冲区对象。
  • 与任何 OpenGL ES 实现一样,您还可以使用帧缓冲区进行离屏图形处理或渲染到纹理以在图形管道的其他地方使用。使用 OpenGL ES 3.0,屏幕外缓冲区可用于使用多个渲染目标的渲染算法。

与 iOS 集成

iOS 应用程序默认支持多任务处理,但在 OpenGL ES 应用程序中正确处理此功能需要额外考虑。OpenGL ES 使用不当可能会导致您的应用在后台被系统杀死。

许多 iOS 设备都包含高分辨率显示器,因此您的应用应支持多种显示器尺寸和分辨率。

实现渲染引擎

设计您的 OpenGL ES 绘图代码有许多可能的策略,其全部细节超出了本文档的范围。渲染引擎设计的许多方面对于 OpenGL 和 OpenGL ES 的所有实现都是通用的。

调试和分析

Xcode 和 Instruments 提供了许多工具来跟踪渲染问题和分析应用程序中的 OpenGL ES 性能。

配置 OpenGL ES 上下文

OpenGL ES 的每个实现都提供了一种创建渲染上下文的方法,以管理 OpenGL ES 规范所需的状态。通过将此状态置于上下文中,多个应用程序可以轻松共享图形硬件,而不会干扰对方的状态。

本章详细介绍了如何在 iOS 上创建和配置上下文。

EAGL 是 OpenGL ES 渲染上下文的 iOS 实现

在您的应用程序可以调用任何 OpenGL ES 函数之前,它必须初始化一个EAGLContext对象。该类EAGLContext还提供了用于将 OpenGL ES 内容与 Core Animation 集成的方法。

当前上下文是 OpenGL ES 函数调用的目标

iOS 应用程序中的每个线程都有一个当前上下文;当您调用 OpenGL ES 函数时,这是调用更改其状态的上下文。

要设置线程的当前上下文,请在该线程上执行时调用EAGLContext类方法。setCurrentContext:

[EAGLContext setCurrentContext: myContext];

调用EAGLContext类方法currentContext来检索线程的当前上下文。

注意: 如果您的应用在同一线程上的两个或多个上下文之间主动切换,请glFlush在将新上下文设置为当前上下文之前调用该函数。这可确保将先前提交的命令及时传送到图形硬件。

OpenGL ES 持有EAGLContext与当前上下文对应的对象的强引用。(如果您使用手动引用计数,OpenGL ES 会保留此对象。)当您调用该setCurrentContext:方法更改当前上下文时,OpenGL ES 不再引用先前的上下文。(如果您使用手动引用计数,OpenGL ES 会释放该EAGLContext对象。)为了防止EAGLContext对象在不是当前上下文时被释放,您的应用程序必须保持对(或保留)这些对象的强引用。

每个上下文都针对特定版本的 OpenGL ES

一个EAGLContext对象只支持一个版本的 OpenGL ES。例如,为 OpenGL ES 1.1 编写的代码与 OpenGL ES 2.0 或 3.0 上下文不兼容。使用核心 OpenGL ES 2.0 功能的代码与 OpenGL ES 3.0 上下文兼容,为 OpenGL ES 2.0 扩展设计的代码通常可以在 OpenGL ES 3.0 上下文中使用,只需稍作更改。许多新的 OpenGL ES 3.0 特性和增强的硬件功能都需要 OpenGL ES 3.0 上下文。

您的应用在创建初始化EAGLContext对象时决定支持哪个版本的 OpenGL ES 。如果设备不支持请求的 OpenGL ES 版本,则该initWithAPI:方法返回nil. 您的应用程序必须进行测试以确保上下文在使用之前已成功初始化。

要在您的应用程序中支持多个版本的 OpenGL ES 作为渲染选项,您应该首先尝试初始化您想要定位的最新版本的渲染上下文。如果返回的对象是nil,则改为初始化旧版本的上下文。清单 2-1演示了如何做到这一点。

清单 2-1  在同一个应用程序中支持多个版本的 OpenGL ES

EAGLContext* CreateBestEAGLContext()
{
   EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
   if (context == nil) {
      context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
   }
   return context;
}

上下文的API属性说明了上下文支持的 OpenGL ES 版本。您的应用应该测试上下文的API属性并使用它来选择正确的渲染路径。实现此行为的常见模式是为每个渲染路径创建一个类。您的应用程序会在初始化时测试上下文并创建一次渲染器。

EAGL 共享组管理上下文的 OpenGL ES 对象

尽管上下文持有 OpenGL ES 状态,但它并不直接管理 OpenGL ES 对象。相反,OpenGL ES 对象由对象创建和维护EAGLSharegroup。每个上下文都包含一个EAGLSharegroup将对象创建委托给的对象。

当两个或多个上下文引用同一个共享组时,共享组的优势就变得很明显,如图 2-1所示。当多个上下文连接到一个公共共享组时,任何上下文创建的 OpenGL ES 对象在所有上下文上都可用;如果您在创建它的上下文之外的另一个上下文中绑定到相同的对象标识符,则您引用了相同的OpenGL ES 对象。移动设备上的资源通常很稀缺;在多个上下文中创建相同内容的多个副本是一种浪费。共享公共资源可以更好地利用设备上的可用图形资源。

共享组是一个不透明的对象;它没有您的应用可以调用的方法或属性。使用 sharegroup 对象的上下文保持对它的强引用。

图 2-1  共享 OpenGL ES 对象的两个上下文

코어 애니메이션 기반 렌더 버퍼

共享组在两种特定情况下最有用:

  • 当上下文之间共享的大部分资源不变时。
  • 当您希望您的应用程序能够在渲染器的主线程以外的线程上创建新的 OpenGL ES 对象时。在这种情况下,第二个上下文在单独的线程上运行,专门用于获取数据和创建资源。加载资源后,第一个上下文可以绑定到对象并立即使用它。该类GLKTextureLoader使用此模式来提供异步纹理加载。

要创建引用同一共享组的多个上下文,第一个上下文通过调用初始化initWithAPI:;为上下文自动创建一个共享组。第二个和以后的上下文通过调用该initWithAPI:sharegroup:方法来初始化以使用第一个上下文的共享组。清单 2-2展示了它是如何工作的。第一个上下文是使用清单 2-1中定义的便利函数创建的。第二个上下文是通过从第一个上下文中提取 API 版本和共享组来创建的。

重要提示: 与同一共享组关联的所有上下文必须使用相同版本的 OpenGL ES API 作为初始上下文。

清单 2-2  使用公共共享组创建两个上下文

EAGLContext* firstContext = CreateBestEAGLContext();
EAGLContext* secondContext = [[EAGLContext alloc] initWithAPI:[firstContext API] sharegroup: [firstContext sharegroup]];

当共享组由多个上下文共享时,管理 OpenGL ES 对象的状态更改是您的应用程序的责任。以下是规则:

  • 如果对象未被修改,您的应用程序可以同时跨多个上下文访问该对象。
  • 当对象被发送到上下文的命令修改时,不得在任何其他上下文中读取或修改该对象。
  • 修改对象后,所有上下文都必须重新绑定对象才能看到更改。如果上下文在绑定它之前引用它,则对象的内容是未定义的。

以下是您的应用程序更新 OpenGL ES 对象应遵循的步骤:

  1. 调用glFlush可能正在使用该对象的每个上下文。
  2. 在要修改对象的上下文中,调用一个或多个 OpenGL ES 函数来更改对象。
  3. 调用glFlush接收到状态修改命令的上下文。
  4. 在所有其他上下文中,重新绑定对象标识符。

注意: 共享对象的另一种方法是使用单个渲染上下文,但使用多个目标帧缓冲区。在渲染时,您的应用会绑定适当的帧缓冲区并根据需要渲染其帧。因为所有 OpenGL ES 对象都是从单个上下文中引用的,所以它们会看到相同的 OpenGL ES 数据。此模式使用的资源较少,但仅对您可以仔细控制上下文状态的单线程应用程序有用。

추천

출처juejin.im/post/7118200721576558629