将 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 渲染到纹理的主要内容,如果未能解决你的问题,请参考以下文章