@qidiandasheng
2020-07-13T06:46:55.000000Z
字数 6562
阅读 2233
音视频
OpenGL ES的FrameBuffer是渲染发生的地方,普通的2D图形的渲染默认发生在屏幕上;而三维的图形渲染则除了包括像素点的颜色,还有Depth Buffer,Stencil Buffer等其他空间。因此,FrameBuffer就是一个这些Buffer的一个集合。
默认情况下,FrameBuffer存在于现存中,但是当需要进行多次渲染或者离屏渲染的时候,可以通过创建一个离屏的FrameBuffer进行渲染。当需要渲染3D效果时,除了创建frameBuffer以外,还需要创建相应的Render Buffer, Depth Buffer, Stencil Buffer,并且将它们attach到frameBuffer上;而当只需要渲染2D图形时,可以直接生成一个Texture,并且将FrameBuffer渲染的结果放入Texture中。
生成一个FrameBuffer的代码:
glGenFramebuffers(1, &framebuffer);glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
由于存在离屏渲染的情况,实际渲染开始之前,需要先激活目标FrameBuffer,而在渲染完成之后,需要将FrameBuffer绑定为0,回到默认的FrameBuffer,才能够显示在屏幕上:
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);//Rendering CodeglBindFramebuffer(GL_FRAMEBUFFER, 0);
如果要渲染一个2D图像到一个Texture上,则还需要先生成一个Texture,并且将Texture绑定到FrameBuffer上。当使用FrameBuffer进行流的传递的时候,则可以使用这个Texture:
glActiveTexture(GL_TEXTURE1);glGenTextures(1, &_texture);glBindTexture(GL_TEXTURE_2D, _texture);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// This is necessary for non-power-of-two texturesglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glBindTexture(GL_TEXTURE_2D, _texture);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)_size.width, (int)_size.height, 0,GL_RGBA, GL_UNSIGNED_BYTE, 0);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
GPUImageFrameBuffer不只是简单的对OpenGL ES的FrameBuffer的对象化封装,而是一个对渲染对象的对象化封装。它生成的可以是一个带有Texture作为Attachment的FrameBuffer,也可以仅仅生成一个Texture作为渲染对象。它的主要功能有:
- (id)initWithSize:(CGSize)framebufferSize;- (id)initWithSize:(CGSize)framebufferSize textureOptions:(GPUTextureOptions)fboTextureOptions onlyTexture:(BOOL)onlyGenerateTexture;- (id)initWithSize:(CGSize)framebufferSize overriddenTexture:(GLuint)inputTexture;
- (void)activateFramebuffer;
- (void)lock;- (void)unlock;- (void)clearAllLocks;- (void)disableReferenceCounting;- (void)enableReferenceCounting;
- (CGImageRef)newCGImageFromFramebufferContents;- (GLubyte *)byteBuffer;
可以通过onlyGenerateTexture来设置生成的渲染对象,YES表示生成的只是纹理缓冲对象,NO为生成帧缓冲对象。
以下代码为创建帧缓冲:
- (void)generateFramebuffer;{runSynchronouslyOnVideoProcessingQueue(^{[GPUImageContext useImageProcessingContext];glGenFramebuffers(1, &framebuffer);glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);// By default, all framebuffers on iOS 5.0+ devices are backed by texture caches, using one shared cacheif ([GPUImageContext supportsFastTextureUpload]){#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE// 从当前上下文获取CoreVideo中的纹理缓存CVOpenGLESTextureCacheRef coreVideoTextureCache = [[GPUImageContext sharedImageProcessingContext] coreVideoTextureCache];// Code originally sourced from http://allmybrain.com/2011/12/08/rendering-to-a-texture-with-ios-5-texture-cache-api/CFDictionaryRef empty; // empty value for attr value.CFMutableDictionaryRef attrs;empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // our empty IOSurface properties dictionaryattrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);CFDictionarySetValue(attrs, kCVPixelBufferIOSurfacePropertiesKey, empty);CVReturn err = CVPixelBufferCreate(kCFAllocatorDefault, (int)_size.width, (int)_size.height, kCVPixelFormatType_32BGRA, attrs, &renderTarget);if (err){NSLog(@"FBO size: %f, %f", _size.width, _size.height);NSAssert(NO, @"Error at CVPixelBufferCreate %d", err);}// 根据纹理缓存获取到CV纹理renderTextureerr = CVOpenGLESTextureCacheCreateTextureFromImage (kCFAllocatorDefault, coreVideoTextureCache, renderTarget,NULL, // texture attributesGL_TEXTURE_2D,_textureOptions.internalFormat, // opengl format(int)_size.width,(int)_size.height,_textureOptions.format, // native iOS format_textureOptions.type,0,&renderTexture);if (err){NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);}CFRelease(attrs);CFRelease(empty);// 根据CVOpenGLESTextureGetTarget和CVOpenGLESTextureGetName获取纹理的target和标识符glBindTexture(CVOpenGLESTextureGetTarget(renderTexture), CVOpenGLESTextureGetName(renderTexture));_texture = CVOpenGLESTextureGetName(renderTexture);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _textureOptions.wrapS);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _textureOptions.wrapT);/*把纹理附加到帧缓存上target:我们所创建的帧缓冲类型的目标(绘制、读取或两者都有)。attachment:我们所附加的附件的类型。现在我们附加的是一个颜色附件。需要注意,最后的那个0是暗示我们可以附加1个以上颜色的附件。textarget:你希望附加的纹理类型。texture:附加的实际纹理。level:Mipmap level。我们设置为0。*/glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture), 0);#endif}else{[self generateTexture];glBindTexture(GL_TEXTURE_2D, _texture);glTexImage2D(GL_TEXTURE_2D, 0, _textureOptions.internalFormat, (int)_size.width, (int)_size.height, 0, _textureOptions.format, _textureOptions.type, 0);/*把纹理附加到帧缓存上target:我们所创建的帧缓冲类型的目标(绘制、读取或两者都有)。attachment:我们所附加的附件的类型。现在我们附加的是一个颜色附件。需要注意,最后的那个0是暗示我们可以附加1个以上颜色的附件。textarget:你希望附加的纹理类型。texture:附加的实际纹理。level:Mipmap level。我们设置为0。*/glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);}#ifndef NS_BLOCK_ASSERTIONSGLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @"Incomplete filter FBO: %d", status);#endifglBindTexture(GL_TEXTURE_2D, 0);});}
由于GPUImageFrameBuffer需要重用,因此,当FrameBuffer的引用为0的时候,这个FrameBuffer就会被放到Cache中,来给其他的需要的地方进行使用。因此作者给GPUImageFrameBuffer引入了引用计数的概念。
引用计数的概念其实和OC自带的MRC有点类似,每当一个地方需要使用这个FrameBuffer的时候,手动调用lock方法,使引用计数+1;当渲染完毕或者这个FrameBuffer使用完毕的时候,手动调用unlock方法,使引用计数-1;当引用计数为0的时候,FrameBuffer会被归还到Cache中。
调用增加FrameBuffer引用计数方法的地方:
调用减少FrameBuffer引用计数方法的地方:
不需要引用计数的情况: