首页 > 其他分享 >iOS OpenGL ES FBO 帧缓存区 渲染缓存区详解

iOS OpenGL ES FBO 帧缓存区 渲染缓存区详解

时间:2023-05-24 12:32:50浏览次数:49  
标签:renderbuffer 缓存 渲染 OpenGL iOS 缓冲区 GL ES



原文地址:

https://developer.apple.com/library/content/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html#//apple_ref/doc/uid/TP40008793-CH103-SW6


绘制到其他渲染目的地

Framebuffer对象是渲染命令的目标。当您创建一个framebuffer对象时,您可以精确控制其存储的颜色,深度和模板数据。您可以通过将图像附加到帧缓冲区来提供此存储,如图4-1所示。最常见的图像附件是一个renderbuffer对象。您还可以将OpenGL ES纹理附加到帧缓冲区的颜色附加点,这意味着任何绘图命令都将呈现到纹理中。之后,纹理可以作为未来渲染命令的输入。您还可以在单个渲染上下文中创建多个帧缓冲区对象。您可以这样做,以便您可以在多个帧缓冲区之间共享相同的渲染管道和OpenGL ES资源。




iOS OpenGL ES FBO 帧缓存区 渲染缓存区详解_图层



图4-1


所有这些方法都需要手动创建framebuffer和renderbuffer对象来存储来自OpenGL ES上下文的渲染结果,以及编写附加代码以将其内容显示在屏幕上,如果需要,运行动画循环

创建一个 Framebuffer Object

根据您的应用程序要执行的任务,您的应用程序会配置不同的对象以附加到framebuffer对象。在大多数情况下,配置帧缓冲区的区别在于什么对象附加到framebuffer对象的颜色附着点

  • 要使用帧缓冲区进行屏幕外图像处理,请附加一个renderbuffer。请参阅创建Offscreen Framebuffer对象。
  • 要使用帧缓冲图像作为后续渲染步骤的输入,请附加纹理。请参阅使用Framebuffer对象渲染到纹理。
  • 要在Core Animation图层组合中使用framebuffer,请使用特殊的Core Animation感知renderbuffer。请参阅渲染到核心动画层。
创建 Offscreen Framebuffer Objects

用于屏幕外渲染的帧缓冲区将其所有附件分配为OpenGL ES渲染缓冲区。以下代码分配带有颜色和深度附件的framebuffer对象。

  1. 创建帧缓冲区并绑定它。
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  1. 创建一个彩色渲染缓冲区,为其分配存储空间,并将其附加到framebuffer的颜色附着点。
GLuint colorRenderbuffer;
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
  1. 创建一个深度或深度/模板renderbuffer,为其分配存储,并将其附加到framebuffer的深度附件点。
GLuint depthRenderbuffer;
glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
  1. 测试framebuffer的完整性。只有当帧缓冲区的配置更改时,才需要执行此测试。
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
if(status != GL_FRAMEBUFFER_COMPLETE) {
 NSLog(@"failed to make complete framebuffer object %x", status);
}

绘制到屏幕外的renderbuffer后,可以将其内容返回给CPU,以便使用glReadPixels函数进一步处理

Using Framebuffer Objects to Render to a Texture 将Framebuffer对象的内存渲染到纹理中 

创建此帧缓冲区的代码与屏幕外的示例几乎相同,但是现在将分配纹理并附加到颜色附加点

  • 创建framebuffer对象(使用与创建Offscreen Framebuffer对象相同的过程)。
  • 创建目标纹理,并将其附加到framebuffer的颜色附件点。
// create the texture
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
  • 分配并附加深度缓冲区(如前所述)。
  • 测试framebuffer的完整性(如前所述)。

尽管此示例假定您要渲染为颜色纹理,但其他选项也是可能的。例如,使用OES_depth_texture扩展,您可以将纹理附加到深度附件点,以将场景中的深度信息存储到纹理中。您可以使用此深度信息来计算最终渲染场景中的阴影。

渲染到核心动画层

核心动画是iOS上图形渲染和动画的核心基础设施。您可以使用主持使用不同iOS子系统(如UIKit,Quartz 2D和OpenGL ES)呈现的内容的图层来构成应用的用户界面或其他视觉显示。 OpenGL ES通过CAEAGLLayer类连接到Core Animation,这是一种特殊类型的Core Animation层,其内容来自OpenGL ES renderbuffer。 Core Animation将renderbuffer的内容与其他图层复合,并在屏幕上显示生成的图像。对应于其他系统和此Renderbuffer相连接的FBO称为 窗口系统提供的"帧缓存区".


iOS OpenGL ES FBO 帧缓存区 渲染缓存区详解_应用程序_02



图4-2


CAEAGLLayer通过提供两个关键功能向OpenGL ES提供此支持。首先,它为renderbuffer分配共享存储。其次,它将渲染缓冲区呈现给Core Animation,将该图层的以前内容替换为renderbuffer中的数据。该模型的优点在于,只有当渲染缓冲区的内容发生变化时, Core Animation layer 才需要进行绘制,Core Animation layer核心动画层的内容不需要在每个帧中绘制。
注意:GLKView类会自动执行以下步骤,因此当您需要将OpenGL ES的内容绘制到包含layer的视图上时,您应该使用GLKView。

注解:当需要将OpenGL ES的内容绘制到iOS的UIView上时,需要使用GLKView类或CAEAGLLayer类来实现将OpenGL ES的内容绘制到iOS的UIView上。


为OpenGL ES渲染使用Core Animation层

  1. 创建CAEAGLLayer对象并配置其属性。
    为获得最佳性能,请将图层的不透明属性的值设置为YES。看到注意核心动画合成性能。可选地,通过为CAEAGLLayer对象的drawableProperties属性分配一个新的值字典来配置渲染表面的表面属性。您可以指定renderbuffer的像素格式,并指定在将它们发送到Core Animation之后,renderbuffer的内容是否被丢弃。有关允许密钥的列表,请参阅EAGLDrawable Protocol Reference。
  2. 分配OpenGL ES上下文并使其成为当前上下文。请参阅配置OpenGL ES上下文。
  3. 创建framebuffer对象(如上面的创建Offscreen Framebuffer对象)。
  4. 创建一个颜色renderbuffer,通过调用上下文的renderbufferStorage:fromDrawable:method分配其存储,并传递层对象作为参数。宽度,高度和像素格式取自层,用于为renderbuffer分配存储空间
GLuint colorRenderbuffer;
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:myEAGLLayer];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
  1. 注意:当核心动画层的界限或属性更改时,应用程序应重新分配renderbuffer的存储空间。如果不重新分配renderbuffers,renderbuffer大小将不匹配图层的大小;在这种情况下,Core Animation可以缩放图像的内容以适应图层。
  2. 检索颜色renderbuffer的高度和宽度。
GLint width;
GLint height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
  1. 分配并附加深度缓冲区(如前所述)。
  2. 测试framebuffer的完整性(如前所述)。
  3. 将CAEAGLLayer对象添加到Core Animation层次结构,将其传递给可见层的addSublayer:方法。

绘制到Framebuffer对象

现在你有一个framebuffer对象,你需要填写它。本节介绍渲染新帧并将其呈现给用户所需的步骤。渲染到纹理或屏幕外框架缓冲区的作用类似,仅在应用程序使用最终帧时有所不同。

按需渲染或动画循环

当渲染到Core Animation层时,您必须选择何时绘制OpenGL ES内容,就像使用GLKit视图和视图控制器进行绘制时一样。如果渲染到屏幕外的帧缓冲区或纹理,则绘制每当适用于使用这些帧缓冲区的情况时。

对于按需绘图,实现自己的方法来绘制和呈现您的renderbuffer,并且每当您要显示新内容时调用它。

要使用动画循环绘制,请使用CADisplayLink对象。显示链接是Core Animation提供的一种定时器,可让您将绘图同步到画面的刷新率。清单4-1显示了如何检索显示视图的屏幕,使用该屏幕创建新的显示链接对象,并将显示链接对象添加到运行循环。
注意:GLKViewController类可自动使用CADisplayLink对象来动画化GLKView内容。仅当您需要超出GLKit框架提供的行为时才直接使用CADisplayLink类。

Listing4-1

displayLink = [myView.window.screen displayLinkWithTarget:self selector:@selector(drawFrame)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

在drawFrame方法的实现之内,读取显示链接的timestamp属性以获取要渲染的下一个帧的时间戳。它可以使用该值来计算下一帧中的对象的位置。

通常,每次屏幕刷新时触发显示链接对象;该值通常为60 Hz,但可能会因不同的设备而异。大多数应用程序不需要每秒更新屏幕60次。您可以将显示链接的frameInterval属性设置为在调用方法之前执行的实际帧数。例如,如果帧间隔设置为3,则您的应用程序每三帧调用一次,或大约每秒20帧。
重要提示:为获得最佳效果,请选择应用程序可以始终如一地实现的帧率平滑,一致的帧速率产生比不规则变化的帧速率更愉快的用户体验。

渲染视频帧
图4-3显示了OpenGL ES应用程序在iOS上呈现和呈现视频帧
的步骤。这些步骤包括提高应用程序性能的许多提示


iOS OpenGL ES FBO 帧缓存区 渲染缓存区详解_图层_03



图4-3


清除缓冲区

在每帧的开始,擦除所有帧缓冲附件的内容,其中不需要前一帧的内容来绘制下一帧。调用glClear函数,将所有缓冲区的位掩码传递给清除,如清单4-2所示。
Listing4-2

glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

对OpenGL ES使用glClear“提示”,可以丢弃renderbuffer或纹理的现有内容,避免将以前的内容加载到内存中的昂贵的操作。

准备资源并执行绘图命令

这两个步骤包括您在设计应用程序架构时所做的大多数关键决策。首先,您决定要向用户显示什么,并配置相应的OpenGL ES对象(如顶点缓冲区对象,纹理,着色器程序及其输入变量)以上传到GPU。接下来,您提交绘图通知,告诉GPU如何使用这些资源来渲染帧。

OpenGL ES设计指南中更详细地介绍了渲染器设计。现在,要注意的最重要的性能优化是,只有在渲染新帧时才能更快地修改OpenGL ES对象。虽然您的应用程序可以在修改对象和提交绘图命令之间交替(如图4-3中的虚线所示),它的运行速度更快,如果它每帧只执行一次

执行绘图命令

此步骤将使用您在上一步中准备的对象,并提交绘图命令以使用它们。在OpenGL ES设计指南中详细介绍了将此部分渲染代码设计为高效运行。现在,要注意的最重要的性能优化是,如果在开始渲染新帧时仅修改OpenGL ES对象,则应用程序运行速度更快。虽然您的应用程序可以在修改对象和提交绘图命令之间交替(如虚线所示),但如果它只执行一次,则运行速度更快。

解决多重采样

如果您的应用程序使用多重采样来提高图像质量,则应用程序必须在呈现给用户之前解析像素。多采样在使用多采样来提高图像质量方面有详细的介绍。

丢弃不需要的Renderbuffers

丢弃操作是一种性能提示,它告诉OpenGL ES,不再需要一个或多个渲染缓冲区的内容。通过暗示OpenGL ES,您不需要renderbuffer的内容,缓冲区中的数据可以被丢弃,并且可以避免更新这些缓冲区内容的昂贵任务。

在渲染循环的这个阶段,您的应用程序已经提交了框架的所有绘图命令。当您的应用程序需要彩色renderbuffer显示到屏幕时,它可能不需要深度缓冲区的内容。清单4-3放弃了深度缓冲区的内容。
Listing4-3

const GLenum discards[]  = {GL_DEPTH_ATTACHMENT};
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glDiscardFramebufferEXT(GL_FRAMEBUFFER,1,discards);

注意:glDiscardFramebufferEXT函数由OpenGL ES 1.1和2.0的EXT_discard_framebuffer扩展提供。在OpenGL ES 3.0上下文中,使用glInvalidateFramebuffer函数。

将结果呈现给核心动画

在此步骤中,颜色渲染缓冲区保存完成的框架,因此您需要做的就是将其呈现给用户。清单4-4将renderbuffer绑定到上下文并呈现它。这将导致完成的框架被交给核心动画。
Listing4-4

glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];

默认情况下,您必须假定在应用程序呈现renderbuffer后,renderbuffer的内容将被丢弃。这意味着,每当您的应用程序呈现帧时,它必须在渲染新帧时完全重新创建帧的内容。由于这个原因,上面的代码总是擦除颜色缓冲区。

如果您的应用程序要保留帧之间的颜色renderbuffer的内容,请将kEAGLDrawablePropertyRetainedBacking密钥添加到CAEAGLLayer对象的drawableProperties属性中存储的字典中,并从较早的glClear函数调用中删除GL_COLOR_BUFFER_BIT常量。保留的支持可能需要iOS才能分配额外的内存来保留缓冲区的内容,这可能会降低应用程序的性能。

使用多重采样来提高图像质量

多采样是一种抗锯齿形式,可以在大多数3D应用程序中平滑锯齿状边缘并提高图像质量。 OpenGL ES 3.0包括多采样作为核心规范的一部分,iOS通过APPLE_framebuffer_multisample扩展在OpenGL ES 1.1和2.0中提供。多采样使用更多的内存和片段处理时间来渲染图像,但它可以以比使用其他方法更低的性能成本来提高图像质量。

图4-4显示了多采样如何工作。而不是创建一个framebuffer对象,您的应用程序创建两个。多重采样缓冲区包含渲染内容所需的所有附件(通常为彩色和深度缓冲区)。解析缓冲区仅包含向用户显示渲染图像所必需的附件(通常为彩色渲染缓冲区,但可能是纹理),使用“创建帧缓冲区对象”中的相应过程创建。多重采样渲染缓冲区使用与解析帧缓冲区相同的维度进行分配,但每个包含一个附加参数,该参数指定为每个像素存储的采样数。您的应用程序将其所有渲染执行到多重采样缓冲区,然后通过将这些样本解析为解析缓冲区来生成最终的抗锯齿图像。


iOS OpenGL ES FBO 帧缓存区 渲染缓存区详解_Core_04



图4-4


清单4-5显示了创建多采样缓冲区的代码。此代码使用先前创建的缓冲区的宽度和高度。它调用glRenderbufferStorageMultisampleAPPLE函数为renderbuffer创建多采样存储。
Listing4-5

glGenFramebuffers(1, &sampleFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer);

glGenRenderbuffers(1, &sampleColorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, sampleColorRenderbuffer);
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_RGBA8_OES, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sampleColorRenderbuffer);

glGenRenderbuffers(1, &sampleDepthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, sampleDepthRenderbuffer);
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, sampleDepthRenderbuffer);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));

以下是修改渲染代码以支持多采样的步骤:

  1. 在清除缓冲区步骤中,清除多重采样帧缓冲区的内容。
glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer);
glViewport(0, 0, framebufferWidth, framebufferHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  1. 提交绘图命令后,将内容从多重采样缓冲区解析为解析缓冲区。为每个像素存储的样本被合并到解析缓冲区中的单个样本中。
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, resolveFrameBuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, sampleFramebuffer);
glResolveMultisampleFramebufferAPPLE();
  1. 在“丢弃”步骤中,可以丢弃附加到多重采样帧缓冲区的两个renderbuffer。这是因为您计划呈现的内容存储在解析帧缓冲区中。
const GLenum discards[]  = {GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT};
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE,2,discards);
  1. 在当前结果步骤中,您将呈现附加到解析帧缓冲区的颜色renderbuffer。
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];

多次采样不是免费的;需要额外的内存来存储附加样本,并将样本解析为解析帧缓冲区需要时间。如果您向应用程序添加多重采样,请始终测试应用程序的性能,以确保其仍然可以接受。
注意:上述代码假定为OpenGL ES 1.1或2.0上下文。多采样是OpenGL ES 3.0 API核心的一部分,但功能不同。详见规范。


标签:renderbuffer,缓存,渲染,OpenGL,iOS,缓冲区,GL,ES
From: https://blog.51cto.com/u_16124099/6338519

相关文章

  • http缓存相关文章推荐
    HTTP缓存MemoryCache与DiskCache介绍......
  • 分页列表缓存,你真的会吗
    开源中国的红薯哥写了很多关于缓存的文章,其中多级缓存思路,分页列表缓存这些知识点给了我很大的启发性。写这篇文章,我们聊聊分页列表缓存,希望能帮助大家提升缓存技术认知。1直接缓存分页列表结果显而易见,这是最简单易懂的方式。我们按照不同的分页条件来缓存分页结果,伪代码......
  • iOS mask 层 UIBezierPath path 放大
    iOSmask层UIBezierPathpath放大////ViewController.m//test_shapeLayer_02////Createdbyadminon3/4/16.//Copyright©2016jeffasd.Allrightsreserved.//#import"ViewController.h"@interfaceViewController()@property(nonatomic......
  • iOS 获取图像的方式与坑点
    请求图像(这里有巨坑)-requestImageForAsset:targetSize:contentMode:options:resultHandler:你不应该生成该类的实例,而应该使用该类的提供的单例对象。该方法提供指定的尺寸的图像,与ALAssetsLibrary库相比,没有了方便的缩略图提供。不过要吐槽的是,需要注意的是,该方法在默认情况下是......
  • iOS UIView如何管理它的子视图
    didMoveToWindow 通知接收者它一斤给添加到窗口中 -(void)didMoveToWindow 讨论 默认实现不做任何事情;子类可以重写这个方法来做特殊的实现 窗口的属性有可能是nil当这个方法调用的时候,这表明接收者并不属于当然任何一个窗口。这个只发生在接收者从它的父视图上移......
  • iOS8 Self Sizing UITableView Cells iOS8Tableview Cells 自适应高度
    UITableViewUITableViewTheoldwayUITableView inheritsfrom UIScrollView).Iftherowswere allequalthiswas justasimpleoperation.Butiftheywere different,ithad toknow theheightsofalltherowsandsumthem.Itaskedusfortheheightofeve......
  • vs git 分支缓存问题
    我们项目不停的开发,就会产生很多本地分支,但实际上git服务器上早就合并了,没有这么多分支,但VisualStudioGit分支本地一大堆,手动一个个删除太费时间。使用如下两条命令可以切换VisualStudioGit分支以git服务器上的分支为主,本地不做缓存。cmd或者gitbash直接执行gitconfig--g......
  • redis专题六:redis 删除策略、淘汰策略、数据库与缓存数据一致性、事物、发布订阅
    文章目录一、删除策略二、淘汰策略三、数据库与缓存数据一致性四、redis事务五、redis发布订阅一、删除策略redis使用:惰性删除+定期删除1、定时删除–>以CPU内存换redis内存定时删除过期的缓存值2、惰性删除–>以redis内存换CPU内存查询到该key时如果过期,删除该过期的缓存值......
  • redis,缓存雪崩,缓存穿透,缓存更新,缓存降级,缓存预热等问题
    一、缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成......
  • iOS MachineLearning 系列(17)—— 几个常用的对象识别 CoreML 模型
    iOSMachineLearning系列(17)——几个常用的对象识别CoreML模型上一篇文章中,我们介绍了几个官方的图片分类的模型,图片分类模型的应用场景在于将图片中最主要的事物进行识别,在已有的词库中找到最可能得事物。而对象识别则要更高级一些。再之前的文章,我们介绍过可以使用官方提供的......