将 CIFilter 应用于 OpenGL 渲染到纹理

Posted

技术标签:

【中文标题】将 CIFilter 应用于 OpenGL 渲染到纹理【英文标题】:Applying CIFilter to OpenGL render-to-texture 【发布时间】:2014-01-07 15:25:26 【问题描述】:

我正在尝试在我的全屏渲染输出上应用 CoreImage 过滤器,但看起来我遗漏了一些东西,因为我得到了黑屏作为输出。

首先我将整个场景绘制到纹理上。然后我从那个纹理中创建 CoreImage,我最终绘制并呈现它。但我得到的只是黑屏。我遵循 Apple 关于绘制纹理并将 CoreImage 与 OpenGLES 集成的指南:WWDC2012 511 和 https://developer.apple.com/library/ios/documentation/3ddrawing/conceptual/opengles_programmingguide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html

相关代码如下:

渲染器:

@interface Renderer () 
  EAGLContext* _context;
  GLuint _defaultFramebuffer, _drawFramebuffer, _depthRenderbuffer, _colorRenderbuffer, _drawTexture;
  GLint _backingWidth, _backingHeight;
  CIImage *_coreImage;
  CIFilter *_coreFilter;
  CIContext *_coreContext;

初始化方法:

- (BOOL)initOpenGL

  _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
  if (!_context) return NO;

  [EAGLContext setCurrentContext:_context];

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

  glGenRenderbuffers(1, &_colorRenderbuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);

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

  glGenTextures(1, &_drawTexture);
  glBindTexture(GL_TEXTURE_2D, _drawTexture);
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _drawTexture, 0);

  glGenRenderbuffers(1, &_depthRenderbuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderbuffer);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer);

  _coreFilter = [CIFilter filterWithName:@"CIColorInvert"];
  [_coreFilter setDefaults];

  NSDictionary *opts = @ kCIContextWorkingColorSpace : [NSNull null] ;
  _coreContext = [CIContext contextWithEAGLContext:_context options:opts];

  return YES;

每当层大小改变时分配内存(在初始化和方向改变时):

- (void)resizeFromLayer:(CAEAGLLayer *)layer

  layer.contentsScale = 1;

  glBindFramebuffer(GL_FRAMEBUFFER, _defaultFramebuffer);

  glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
  [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];

  glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);
  glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);

  // glCheckFramebufferStatus ... SUCCESS

  glBindFramebuffer(GL_FRAMEBUFFER, _drawFramebuffer);

  glBindTexture(GL_TEXTURE_2D, _drawTexture);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _backingWidth, _backingHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

  glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderbuffer);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _backingWidth, _backingHeight);

  // glCheckFramebufferStatus ... SUCCESS

绘制方法:

- (void)render:(Scene *)scene

  [EAGLContext setCurrentContext:_context];

  glBindFramebuffer(GL_FRAMEBUFFER, _drawFramebuffer);

  // Draw using GLKit, custom shaders, drawArrays, drawElements
  // Now rendered scene is in _drawTexture

  glBindFramebuffer(GL_FRAMEBUFFER, _defaultFramebuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);

  // Create CIImage with our render-to-texture texture
  _coreImage = [CIImage imageWithTexture:_drawTexture size:CGSizeMake(_backingWidth, _backingHeight) flipped:NO colorSpace:nil];

  // Ignore filtering for now; Draw CIImage to current render buffer 
  [_coreContext drawImage:_coreImage inRect:CGRectMake(0, 0, _backingWidth, _backingHeight) fromRect:CGRectMake(0, 0, _backingWidth, _backingHeight)];

  // Present
  [_context presentRenderbuffer:GL_RENDERBUFFER];

注意,绘制场景后,_drawTexture 包含渲染场景。我使用 Xcode 调试工具(捕获 OpenGL ES 帧)检查了这一点。

编辑:如果我尝试使用其他纹理创建 CIImage,然后是 _drawTexture,它会正确显示它。我怀疑 _drawTexture 可能没有准备好,或者当 CIContext 尝试通过 CIImage 渲染它时被锁定。

EDIT2:我还尝试用视口清除替换所有绘图代码:

  glViewport(0, 0, _backingWidth, _backingHeight);
  glClearColor(0, 0.8, 0, 1);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

结果仍然是黑色的。它表明问题可能与绘制纹理或帧缓冲区有关。

【问题讨论】:

为什么这会被否决? 这很有趣。通常,我会说这是因为 Core Image 上下文不是使用与您的渲染相同的 OpenGL ES 上下文创建的,但在这里看起来设置正确。如果您使用直通着色器并使用渲染纹理将四边形绘制到屏幕上,您能否验证场景是否正确渲染到纹理?最后,如果你没有嫁给 Core Image,我这里有一个小项目:github.com/BradLarson/GPUImage,它也可以做这种风格的 GPU 端后处理。请参阅其中的 CubeExample 示例应用程序,它可以满足您的需求。 好的,我使用 _drawTexture 渲染了一个四边形,它是黑色的。所以看起来该纹理或在其上的渲染有问题。也许我错过了渲染到纹理的东西。差异只是我将纹理附加为 GL_COLOR_ATTACHEMNT0 而不是渲染缓冲区。 哦,感谢您提出 GPUImage。我实际上对你的项目很熟悉,我可能会回到它,甚至与 CoreImage 一起支持它。 【参考方案1】:

我终于找到了问题所在。 iOS 上的非幂 2 纹理必须具有线性过滤和钳制到边缘环绕:

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

我的纹理与屏幕大小相同,但我没有设置这四个参数。

对于后代:上面的代码是 OpenGL ES 和 CoreImage 互连的完全有效的示例。只需确保正确初始化纹理!

【讨论】:

以上是关于将 CIFilter 应用于 OpenGL 渲染到纹理的主要内容,如果未能解决你的问题,请参考以下文章

将 OpenGL 渲染保存到图像文件

金属版 CIFilter 是不是可用于 A7 处理器

渲染到纹理 OpenGL ES 2.0

应用 CIFilter 后图像旋转

OpenGL:如何设置 OpenGL 只渲染到 FBO,不输出到屏幕/窗口/控件?

应用 CIFilter 后,无论我做啥,图像都会变大