在这里记录一个在iOS上面渲染到纹理的一般步骤.文章最后附源码地址
首先,我们的视图的layer应该是CAEAGLLayer:
+(Class)layerClass{
return [CAEAGLLayer class];
}
其次,我们配置一下这个layer的绘图属性:
CAEAGLLayer *layer = (CAEAGLLayer*)self.layer;
layer.opaque = NO;
layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],
kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8,
kEAGLDrawablePropertyColorFormat,
nil];
然后我们配置一个EAGLContext为当前上下文.
context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
[EAGLContext setCurrentContext:context];
接下来,我们将要配置一个帧缓存framebuffer,一个用于渲染的renderbuffer, 以及一个depthbuffer,depthbuffer是可选的,在绘制立体图形时最好要有。
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &viewWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &viewHeight);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
//生成一个用于渲染的颜色缓冲区
glGenRenderbuffers(1, &depthbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, viewWidth, viewHeight);
glBindRenderbuffer(GL_RENDERBUFFER, 0);//生成一个深度缓冲区
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_COMPONENT, GL_RENDERBUFFER, depthbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
在这段代码中,我们将renderbuffer作为framebuffer的颜色附着点,将depthbuffer作为frame buffer的深度附着点,也就是说以后绘制到这个帧缓存的颜色数据统统都存在renderbuffer所标示的存储中,深度数据都存储在depthbuffer中,在iOS上面我们要使用iOS所特有的一种分配方式分配颜色缓冲区renderbufferStorage: fromDrawable:,这个操作将当前GL_RENDERBUFFER与layer绑定。
接下来我们加载着色器,并且准备顶点数据以及纹理数据,这其中包括一个没有数据的纹理,这个纹理将被用渲染到纹理的方式来渲染.
NSString *vShader = [[NSBundle mainBundle]pathForResource:@"TexCoords" ofType:@"vsh"];
NSString *fShader = [[NSBundle mainBundle]pathForResource:@"TexCoords" ofType:@"fsh"];
YXShaderManager *manager = [[YXShaderManager alloc]initWithVertexShader:vShader fragShader:fShader];
program = manager.program;
[manager.analyser.attributes enumerateObjectsUsingBlock:^(YXAttribute * obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.name isEqualToString:@"position"]) {
pos = obj.position;
}
if ([obj.name isEqualToString:@"texcoords"]) {
texCoords = obj.position;
}
}];
[manager.analyser.uniforms enumerateObjectsUsingBlock:^(YXUniform * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.name isEqualToString:@"tex"]) {
tx = obj.pos;
}
}];
//shader生成shader,经过测试shader是好的
//接下来开始准备数据
glGenBuffers(2, quad);
glBindBuffer(GL_ARRAY_BUFFER, quad[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(a), a, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(b), b, GL_STATIC_DRAW);
//将数据送入gpu
glGenTextures(1, &fbotex);
glBindTexture(GL_TEXTURE_2D, fbotex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, viewWidth, viewHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, 0);
//生成一个空纹理
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbotex, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//生成一个帧缓存并将生成的空纹理作为帧缓存的颜色附着点与帧绑定
UIImage *img = [UIImage imageNamed:@"1.png"];
CFDataRef rawdata = CGDataProviderCopyData(CGImageGetDataProvider(img.CGImage));
GLuint *pixels = (GLuint *)CFDataGetBytePtr(rawdata);
int width = img.size.width;
int height = img.size.height;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, 0);
在这里,我们加载了一个着色器,然后生成了两个vbo,一个是顶点数据,一个是索引数据,后面我们将用索引的方式绘图.然后我们生成了一个空纹理fbotex,我们只是简单的对这个纹理设置了一些参数,并没有往里面放入数据.紧接着,我们生成了另一个帧缓存fbo,并且我们把fbotex作为fbo的颜色附着点,这样,当绘制到这个fbo后,fbotex中就会有数据了。下面我们生成了一个实实在在的纹理texture。紧接着,我们用一个CADidplayLink来启动一个绘图循环.
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(Render)];
[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
下面是绘图代码:
glClearColor(0, 0, 0, 1);
glUseProgram(program);
glViewport(0, 0, viewWidth, viewHeight);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, quad[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad[1]);
glEnableVertexAttribArray(pos);
glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, sizeof(SceneVertex), NULL);
glEnableVertexAttribArray(texCoords);
glVertexAttribPointer(texCoords, 2, GL_FLOAT, GL_FALSE, sizeof(SceneVertex), NULL+offsetof(SceneVertex, texCoords));
glUniform1i(tx, 0);
glBindTexture(GL_TEXTURE_2D, texture);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(1, 0, 0, 1);
glUseProgram(program);
glViewport(0, 0, viewWidth, viewHeight);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, quad[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad[1]);
glEnableVertexAttribArray(pos);
glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, sizeof(SceneVertex), NULL);
glEnableVertexAttribArray(texCoords);
glVertexAttribPointer(texCoords, 2, GL_FLOAT, GL_FALSE, sizeof(SceneVertex), NULL+offsetof(SceneVertex, texCoords));
glUniform1i(tx, 0);
glBindTexture(GL_TEXTURE_2D, texture);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];
glBindRenderbuffer(GL_RENDERBUFFER, 0);
在这段代码中,我们先绑定fbo,接下来的绘制都会绘制到fbo中,那么在第一次drawelement后,fbotex中就已经有了数据,紧接着,我们切换到目标帧缓存framebuffer,再进行一轮绘制,这时我们使用fbotex这个纹理,当绘制完成后,我们调用[context presentRenderbuffer]这个方法绘制到屏幕,在绘制过程中注意,要设置glviewport,不设置的话纹理画不出来,然后还要注意要绘制到哪里就要先绑定那个对象,要绘制哪个缓存就要先绑定哪个缓存.下面是全部代码的地址:
点击打开链接