Применение CIFilter для рендеринга OpenGL-текстуры

Я пытаюсь применить фильтр CoreImage на выходе полноэкранного рендеринга, но похоже, что я чего-то не хватает, потому что получаю черный экран в качестве вывода.

Сначала я рисую целую сцену на текстуру. Затем я создаю CoreImage из этой текстуры, которую я наконец рисую и представляю. Но все, что я получаю, это черный экран. Я следил за планами Apple по рисованию текстур и интеграции CoreImage с OpenGLES: WWDC2012 511 и https://developer.apple.com/library/ios/documentation/3ddrawing/conceptual/opengles_programmingguide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html

Вот соответствующий код:

Renderer:

@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;
}

Память Alloc всякий раз, когда изменяется размер слоя (при инициализации и изменении ориентации):

- (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 (Capture OpenGL ES frame).

EDIT: Если я попытаюсь создать 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);

Результат по-прежнему черный. Это говорит о том, что проблема может быть чем-то вроде текстуры рисования или фреймбуфера.

Ответ 1

Наконец я нашел, что неправильно. Отсутствие двух текстур на iOS должно иметь линейную фильтрацию и привязку к обрезке кромок:

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. Просто убедитесь, что вы правильно инициализировали свою текстуру!