OpenGL ES 2.0 - iOS - 使用 FBO 渲染到纹理
Posted
技术标签:
【中文标题】OpenGL ES 2.0 - iOS - 使用 FBO 渲染到纹理【英文标题】:OpenGL ES 2.0 - iOS - Render to texture using FBO 【发布时间】:2015-07-30 20:10:58 【问题描述】:根据教程:
“http://www.raywenderlich.com/4404/opengl-es-2-0-for-iphone-tutorial-part-2-textures”
还有《Open GL ES 2.0 Programming Guide》一书
我正在尝试将场景渲染为 FBO 纹理,然后在显示窗口上渲染该 FBO 纹理。
我不知道为什么,但我有一个白屏..
编辑 1:白屏是由于在渲染循环中绑定了深度缓冲区。现在的问题是FBO纹理中渲染的立方体没有渲染到屏幕上..
编辑 2:更新代码并添加着色器文件
这是我的代码:
OpenGLView.h:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
@interface OpenGLView : UIView
CAEAGLLayer* _eaglLayer;
EAGLContext* _context;
GLuint _viewFrameBuffer;
GLuint _colorRenderBuffer;
GLuint _depthRenderBuffer;
float _cubeCurrentRotation;
GLuint _cubeTexture;
GLuint _cubeVertexBuffer;
GLuint _cubeIndexBuffer;
GLuint _viewportSizedQuadVertexBuffer;
GLuint _viewportSizedQuadIndexBuffer;
GLuint _FBO;
GLuint _FBOTexture;
GLuint _FBODepthBuffer;
// shaders
// simple shader
GLuint _simpleShaderProgram;
GLuint _simpleVertexShader;
GLuint _simpleFragmentShader;
GLuint _positionSlot;
GLuint _colorSlot;
GLuint _projectionUniform;
GLuint _modelViewUniform;
GLuint _texCoordSlot;
GLuint _textureUniform;
// texture shader
GLuint _textureShaderProgram;
GLuint _textureVertexShader;
GLuint _texturePositionSlot;
GLuint _textureColorSlot;
GLuint _textureTexCoordSlot;
GLuint _textureTextureUniform;
@end
OpenGLView.m:
//
// OpenGLView.m
// HelloOpenGL
//
// Created by Ray Wenderlich on 5/24/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "OpenGLView.h"
#import "CC3GLMatrix.h"
@implementation OpenGLView
typedef struct
float Position[3];
float Color[4];
float TexCoord[2];
Vertex;
#define TEX_COORD_MAX 1
const Vertex cubeVertices[] =
// Front
1, -1, 0, 1, 0, 0, 1, TEX_COORD_MAX, 0,
1, 1, 0, 0, 1, 0, 1, TEX_COORD_MAX, TEX_COORD_MAX,
-1, 1, 0, 0, 0, 1, 1, 0, TEX_COORD_MAX,
-1, -1, 0, 0, 0, 0, 1, 0, 0,
// Back
1, 1, -2, 1, 0, 0, 1, TEX_COORD_MAX, 0,
-1, -1, -2, 0, 1, 0, 1, TEX_COORD_MAX, TEX_COORD_MAX,
1, -1, -2, 0, 0, 1, 1, 0, TEX_COORD_MAX,
-1, 1, -2, 0, 0, 0, 1, 0, 0,
// Left
-1, -1, 0, 1, 0, 0, 1, TEX_COORD_MAX, 0,
-1, 1, 0, 0, 1, 0, 1, TEX_COORD_MAX, TEX_COORD_MAX,
-1, 1, -2, 0, 0, 1, 1, 0, TEX_COORD_MAX,
-1, -1, -2, 0, 0, 0, 1, 0, 0,
// Right
1, -1, -2, 1, 0, 0, 1, TEX_COORD_MAX, 0,
1, 1, -2, 0, 1, 0, 1, TEX_COORD_MAX, TEX_COORD_MAX,
1, 1, 0, 0, 0, 1, 1, 0, TEX_COORD_MAX,
1, -1, 0, 0, 0, 0, 1, 0, 0,
// Top
1, 1, 0, 1, 0, 0, 1, TEX_COORD_MAX, 0,
1, 1, -2, 0, 1, 0, 1, TEX_COORD_MAX, TEX_COORD_MAX,
-1, 1, -2, 0, 0, 1, 1, 0, TEX_COORD_MAX,
-1, 1, 0, 0, 0, 0, 1, 0, 0,
// Bottom
1, -1, -2, 1, 0, 0, 1, TEX_COORD_MAX, 0,
1, -1, 0, 0, 1, 0, 1, TEX_COORD_MAX, TEX_COORD_MAX,
-1, -1, 0, 0, 0, 1, 1, 0, TEX_COORD_MAX,
-1, -1, -2, 0, 0, 0, 1, 0, 0
;
const GLubyte cubeIndices[] =
// Front
0, 1, 2,
2, 3, 0,
// Back
4, 5, 6,
6, 7, 4,
// Left
8, 9, 10,
10, 11, 8,
// Right
12, 13, 14,
14, 15, 12,
// Top
16, 17, 18,
18, 19, 16,
// Bottom
20, 21, 22,
22, 23, 20
;
const Vertex viewPortQuadVertices[] =
1, -1, 0, 1, 1, 1, 1, TEX_COORD_MAX, 0,
1, 1, 0, 1, 1, 1, 1, TEX_COORD_MAX, TEX_COORD_MAX,
-1, 1, 0, 1, 1, 1, 1, 0, TEX_COORD_MAX,
-1, -1, 0, 1, 1, 1, 1, 0, 0
;
const GLubyte viewPortQuadIndices[] =
0, 1, 2,
2, 3, 0,
;
+ (Class)layerClass
return [CAEAGLLayer class];
- (void)dealloc
[_context release];
_context = nil;
[super dealloc];
- (id)initWithFrame:(CGRect)frame
self = [super initWithFrame:frame];
if (self)
[self setupLayer];
[self setupContext];
[self setupDepthBuffer];
[self setupRenderBuffer];
[self setupFrameBuffer];
[self setupFBOs];
[self compileShaders];
[self setupVBOs];
[self setupDisplayLink];
_cubeTexture = [self setupTexture:@"tile_floor.png"];
return self;
- (void)setupDisplayLink
CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- (void)setupLayer
_eaglLayer = (CAEAGLLayer*) self.layer;
_eaglLayer.opaque = YES;
- (void)setupContext
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
_context = [[EAGLContext alloc] initWithAPI:api];
if (!_context)
NSLog(@"Failed to initialize OpenGLES 2.0 context");
exit(1);
if (![EAGLContext setCurrentContext:_context])
NSLog(@"Failed to set current OpenGL context");
exit(1);
- (void)setupRenderBuffer
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
- (void)setupDepthBuffer
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, self.frame.size.width, self.frame.size.height);
- (void)setupFrameBuffer
glGenFramebuffers(1, &_viewFrameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _viewFrameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
-(void)setupFBOs
GLint maxRenderBufferSize;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderBufferSize);
GLuint textureWidth = self.frame.size.width;
GLuint textureHeight = self.frame.size.height;
if(maxRenderBufferSize <= textureWidth
|| maxRenderBufferSize <= textureHeight)
NSLog(@"FBO cant allocate that much space");
glGenFramebuffers(1, &_FBO);
glGenRenderbuffers(1, &_FBODepthBuffer);
glGenTextures(1, &_FBOTexture);
glBindTexture(GL_TEXTURE_2D, _FBOTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureWidth, textureHeight,
0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindRenderbuffer(GL_RENDERBUFFER, _FBODepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, textureWidth, textureHeight);
glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _FBOTexture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _FBODepthBuffer);
GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE)
NSLog(@"FBO is not complete :%u", status);
- (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType
NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:@"glsl"];
NSError* error;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error];
if (!shaderString)
NSLog(@"Error loading shader: %@", error.localizedDescription);
exit(1);
GLuint shaderHandle = glCreateShader(shaderType);
const char * shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = [shaderString length];
glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
glCompileShader(shaderHandle);
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE)
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"%@", messageString);
exit(1);
return shaderHandle;
- (void)compileShaders
_simpleVertexShader = [self compileShader:@"SimpleVertex" withType:GL_VERTEX_SHADER];
_simpleFragmentShader = [self compileShader:@"SimpleFragment" withType:GL_FRAGMENT_SHADER];
_textureVertexShader = [self compileShader:@"TextureVertex" withType:GL_VERTEX_SHADER];
_simpleShaderProgram = glCreateProgram();
glAttachShader(_simpleShaderProgram, _simpleVertexShader);
glAttachShader(_simpleShaderProgram, _simpleFragmentShader);
glLinkProgram(_simpleShaderProgram);
GLint linkSuccess;
glGetProgramiv(_simpleShaderProgram, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE)
GLchar messages[256];
glGetProgramInfoLog(_simpleShaderProgram, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"%@", messageString);
exit(1);
glUseProgram(_simpleShaderProgram);
_positionSlot = glGetAttribLocation(_simpleShaderProgram, "Position");
_colorSlot = glGetAttribLocation(_simpleShaderProgram, "SourceColor");
glEnableVertexAttribArray(_positionSlot);
glEnableVertexAttribArray(_colorSlot);
_projectionUniform = glGetUniformLocation(_simpleShaderProgram, "Projection");
_modelViewUniform = glGetUniformLocation(_simpleShaderProgram, "Modelview");
_texCoordSlot = glGetAttribLocation(_simpleShaderProgram, "TexCoordIn");
glEnableVertexAttribArray(_texCoordSlot);
_textureUniform = glGetUniformLocation(_simpleShaderProgram, "Texture");
// Texture shader
_textureShaderProgram = glCreateProgram();
glAttachShader(_textureShaderProgram, _textureVertexShader);
glAttachShader(_textureShaderProgram, _simpleFragmentShader);
glLinkProgram(_textureShaderProgram);
glGetProgramiv(_textureShaderProgram, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE)
GLchar messages[256];
glGetProgramInfoLog(_textureShaderProgram, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"%@", messageString);
exit(1);
glUseProgram(_textureShaderProgram);
_texturePositionSlot = glGetAttribLocation(_textureShaderProgram, "texPosition");
_textureColorSlot = glGetAttribLocation(_textureShaderProgram, "texSourceColor");
glEnableVertexAttribArray(_texturePositionSlot);
glEnableVertexAttribArray(_textureColorSlot);
_textureTexCoordSlot = glGetAttribLocation(_textureShaderProgram, "texTexCoordIn");
glEnableVertexAttribArray(_textureTexCoordSlot);
_textureTextureUniform = glGetUniformLocation(_textureShaderProgram, "Texture");
- (void)setupVBOs
glGenBuffers(1, &_cubeVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _cubeVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);
glGenBuffers(1, &_cubeIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _cubeIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW);
glGenBuffers(1, &_viewportSizedQuadVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _viewportSizedQuadVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(viewPortQuadVertices), viewPortQuadVertices, GL_STATIC_DRAW);
glGenBuffers(1, &_viewportSizedQuadIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _viewportSizedQuadIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(viewPortQuadIndices), viewPortQuadIndices, GL_STATIC_DRAW);
- (GLuint)setupTexture:(NSString *)fileName
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage)
NSLog(@"Failed to load image %@", fileName);
exit(1);
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
CGContextRelease(spriteContext);
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return texName;
- (void)render:(CADisplayLink*)displayLink
glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
//glBindRenderbuffer(GL_RENDERBUFFER, _FBODepthBuffer);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glClearColor(255.0/255.0, 255.0/255.0, 255.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glUseProgram(_simpleShaderProgram);
// Cube draw in FBO
CC3GLMatrix *projection = [CC3GLMatrix matrix];
float h = 4.0f * self.frame.size.height / self.frame.size.width;
[projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:4 andFar:1000];
glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
[modelView populateFromTranslation:CC3VectorMake(sin(CACurrentMediaTime()), 0, -7)];
_cubeCurrentRotation += displayLink.duration * 90;
[modelView rotateBy:CC3VectorMake(_cubeCurrentRotation, _cubeCurrentRotation, 0)];
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
glBindBuffer(GL_ARRAY_BUFFER, _cubeVertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _cubeIndexBuffer);
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _cubeTexture);
glUniform1i(_textureUniform, 0);
glDrawElements(GL_TRIANGLES, sizeof(cubeIndices)/sizeof(cubeIndices[0]), GL_UNSIGNED_BYTE, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ---------------------------------------------------------------
// FBO -> window
glBindFramebuffer(GL_FRAMEBUFFER, _viewFrameBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glClearColor(0.0/255.0, 0.0/255.0, 0.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glUseProgram(_textureShaderProgram);
glBindBuffer(GL_ARRAY_BUFFER, _viewportSizedQuadVertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _viewportSizedQuadIndexBuffer);
glVertexAttribPointer(_texturePositionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(_textureColorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glVertexAttribPointer(_textureTexCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _FBOTexture);
glUniform1i(_textureTextureUniform, 0);
glDrawElements(GL_TRIANGLES, sizeof(viewPortQuadIndices)/sizeof(viewPortQuadIndices[0]), GL_UNSIGNED_BYTE, 0);
[_context presentRenderbuffer:GL_RENDERBUFFER];
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
@end
SimpleVertex.glsl:
attribute vec4 Position;
attribute vec4 SourceColor;
varying vec4 DestinationColor;
uniform mat4 Projection;
uniform mat4 Modelview;
attribute vec2 TexCoordIn;
varying vec2 TexCoordOut;
void main(void)
DestinationColor = SourceColor;
gl_Position = Projection * Modelview * Position;
TexCoordOut = TexCoordIn;
TextureVertex.glsl:
attribute vec4 texPosition;
attribute vec4 texSourceColor;
varying vec4 DestinationColor;
attribute vec2 texTexCoordIn;
varying vec2 TexCoordOut;
void main(void)
DestinationColor = texSourceColor;
gl_Position = texPosition;
TexCoordOut = texTexCoordIn;
SimpleFragment.glsl:
varying lowp vec4 DestinationColor;
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void)
gl_FragColor = DestinationColor * texture2D(Texture, TexCoordOut);
【问题讨论】:
你尝试过什么调试?您是否尝试仅使用颜色清除缓冲区? 您可以尝试将颜色缓冲区直接传送到默认帧缓冲区,请参见此处:opengl.org/sdk/docs/man3/xhtml/glBlitFramebuffer.xml @dari :据我所知,es 2.0 中没有可用的 glBlitFramebuffer。 @FelixK。 : 谢谢,我意识到渲染过程中的这一行不是一个好主意:glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);现在我不再有黑屏了,但是没有应用 FBO 纹理,或者它是空的。请问还有其他想法吗?非常感谢您的帮助 我建议尝试逐步减少可能的错误源的数量,或者甚至更好地从一个小代码开始,并尝试仅将颜色渲染到窗口中,例如 FBO。我看到的是您还绑定了其他渲染缓冲区,例如在删除的行中,您是否删除了它们?您是否检查过用于渲染对象的代码是否正常工作?为了确定,我会在主帧缓冲区上尝试一下。 【参考方案1】:好的,感谢@FelixK,我通过更正视口而不绑定渲染循环中的深度缓冲区解决了这个问题!
【讨论】:
以上是关于OpenGL ES 2.0 - iOS - 使用 FBO 渲染到纹理的主要内容,如果未能解决你的问题,请参考以下文章
使用 OpenGL ES 2.0 和 cmake 生成 iOS 静态库
OpenGL ES 2.0 Framebuffer 渲染到纹理 iOS:没有显示