OS X下使用OpenGL做离屏渲染
本文為轉(zhuǎn)載內(nèi)容,原地址
有時,我們想通過GPU做一些視頻、圖像處理,而處理的結(jié)果不需要顯示在顯示器上,而是直接交給主存,這時候我們可以通過OpenGL的離屏渲染來實(shí)現(xiàn)。
由于我們不需要將渲染好的像素顯示到屏幕上,因此我們可以使用framebuffer object,將像素放到fbo上,然后通過glReadPixels來最終獲取渲染好的像素。
在Mac OS X Lion中做離屏渲染非常簡單,基本步驟如下:
1、創(chuàng)建OpenGL繪制上下文,可使用離屏渲染屬性,然后可以設(shè)置上其它屬性。
2、創(chuàng)建framebuffer object
3、綁定創(chuàng)建好的fbo
4、編譯、連接、加載著色器并初始化一些基本的OpenGL的上下文
5、生成紋理,并將它綁定到framebuffer中
6、取消framebuffer的綁定,然后就可以做渲染了。
在使用glDrawArrays等繪制接口之后,可以調(diào)用glFlush來確保像素都已經(jīng)到了指定的framebuffer,然后通過調(diào)用glReadPixels將像素讀取出來。
下面將給出具體的代碼:
| // //? MyGLView.h //? OpenGLShaderBasic // //? Created by Zenny Chen on 10/4/10. //? Copyright 2010 GreenGames Studio. All rights reserved. // #import <Cocoa/Cocoa.h> @class?NSOpenGLContext; @interface MyGLView : NSObject { ????GLuint program; ????GLuint framebufferID; ????GLuint texName; ????? ????NSOpenGLContext *glContext; } - (void)prepareOpenGL; - (void)compute; @end |
這里盡管給出的Objective-C類叫MyGLView,但它其實(shí)不是一個UIView的子類。由于我們不需要將結(jié)果顯示到屏幕上,因此這里也就不需要用UIView作為父類。
| // //? MyGLView.m //? OpenGLShaderBasic // //? Created by Zenny Chen on 10/4/10. //? Copyright 2010 GreenGames Studio. All rights reserved. // #import "MyGLView.h" #include <OpenGL/gl.h> #include <OpenGL/OpenGL.h> #include <OpenGL/CGLRenderers.h> #define MY_PIXEL_WIDTH????? 128 #define MY_PIXEL_HEIGHT???? 128 @implementation MyGLView // uniform index enum { ????UNIFORM_SAMPLER, ????? ????NUM_UNIFORMS }; static?GLint uniforms[NUM_UNIFORMS]; // attribute index enum?{ ????ATTRIB_VERTEX, ????ATTRIB_TEXCOORD, ????? ????NUM_ATTRIBUTES }; - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file { ????GLint status; ????const?GLchar *source; ????? ????source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String]; ????if?(!source) ????{ ????????NSLog(@"Failed to load vertex shader"); ????????return?FALSE; ????} ????? ????*shader = glCreateShader(type); ????glShaderSource(*shader, 1, &source, NULL); ????glCompileShader(*shader); ????? ????GLint logLength; ????glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); ????if?(logLength > 0) ????{ ????????GLchar *log?= (GLchar *)malloc(logLength); ????????glGetShaderInfoLog(*shader, logLength, &logLength, log); ????????NSLog(@"Shader compile log:\n%s", log); ????????free(log); ????} ????? ????glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); ????if?(status == 0) ????{ ????????glDeleteShader(*shader); ????????return?FALSE; ????} ????? ????return?TRUE; } - (BOOL)linkProgram:(GLuint)prog { ????GLint status; ????? ????glLinkProgram(prog); ????? ????GLint logLength; ????glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); ????if?(logLength > 0) ????{ ????????GLchar *log?= (GLchar *)malloc(logLength); ????????glGetProgramInfoLog(prog, logLength, &logLength, log); ????????NSLog(@"Program link log:\n%s", log); ????????free(log); ????} ????? ????glGetProgramiv(prog, GL_LINK_STATUS, &status); ????if?(status == 0) ????????return?FALSE; ????? ????return?TRUE; } - (BOOL)validateProgram:(GLuint)prog { ????GLint logLength, status; ????? ????glValidateProgram(prog); ????glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); ????if?(logLength > 0) ????{ ????????GLchar *log?= (GLchar *)malloc(logLength); ????????glGetProgramInfoLog(prog, logLength, &logLength, log); ????????NSLog(@"Program validate log:\n%s", log); ????????free(log); ????} ????? ????glGetProgramiv(prog, GL_VALIDATE_STATUS, &status); ????if?(status == 0) ????????return?FALSE; ????? ????return?TRUE; } - (BOOL)loadShaders { ????GLuint vertShader, fragShader; ????NSString *vertShaderPathname, *fragShaderPathname; ????? ????// create shader program ????program = glCreateProgram(); ????? ????// create and compile vertex shader ????vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader"?ofType:@"vsh"]; ????if?(![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) ????{ ????????NSLog(@"Failed to compile vertex shader"); ????????return?FALSE; ????} ????? ????// create and compile fragment shader ????fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader"?ofType:@"fsh"]; ????if?(![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) ????{ ????????NSLog(@"Failed to compile fragment shader"); ????????return?FALSE; ????} ????? ????// attach vertex shader to program ????glAttachShader(program, vertShader); ????? ????// attach fragment shader to program ????glAttachShader(program, fragShader); ????? ????// bind attribute locations ????// this needs to be done prior to linking ????glBindAttribLocation(program, ATTRIB_VERTEX, "position"); ????glBindAttribLocation(program, ATTRIB_TEXCOORD, "texCoords"); ????//glBindFragDataLocationEXT(program, 0, "myFragColor"); ????? ????// link program ????if?(![self linkProgram:program]) ????{ ????????NSLog(@"Failed to link program: %d", program); ????????return?FALSE; ????} ????? ????// get uniform locations ????uniforms[UNIFORM_SAMPLER] = glGetUniformLocation(program, "sampler"); ????? ????// release vertex and fragment shaders ????if?(vertShader) ????????glDeleteShader(vertShader); ????if?(fragShader) ????????glDeleteShader(fragShader); ????? ????return?TRUE; } - (void)dealloc { ????if(texName != 0) ????????glDeleteTextures(1, &texName); ????? ????if(framebufferID != 0) ????????glDeleteFramebuffers(1, &framebufferID); ????? ????if(glContext != nil) ????{ ????????[glContext release]; ????????glContext = nil; ????} ????? ????[super dealloc]; } static?GLfloat __attribute__((aligned(16))) my_buffer[MY_PIXEL_WIDTH * MY_PIXEL_HEIGHT * 4]; static?GLfloat __attribute__((aligned(16))) checkImage[MY_PIXEL_WIDTH * MY_PIXEL_HEIGHT * 4]; static?void?initCheckImage(void) { ????for(int?row = 0; row < MY_PIXEL_HEIGHT; row++) ????{??????? ????????for(int?col = 0; col < MY_PIXEL_WIDTH; col++) ????????{ ????????????checkImage[row * MY_PIXEL_WIDTH * 4 + col * 4 + 0] = 0.1f; ????????????checkImage[row * MY_PIXEL_WIDTH * 4 + col * 4 + 1] = 0.2f; ????????????checkImage[row * MY_PIXEL_WIDTH * 4 + col * 4 + 2] = 0.3f; ????????????checkImage[row * MY_PIXEL_WIDTH * 4 + col * 4 + 3] = 0.4f; ????????} ????} } - (void)prepareOpenGL { ????if(glContext != nil) ????????return; ????? #if 0 ????CGLPixelFormatObj pixObj = NULL; ????GLint nPix = 0; ????CGLChoosePixelFormat((const?CGLPixelFormatAttribute[]){kCGLPFAOpenGLProfile, kCGLOGLPVersion_3_2_Core, 0}, &pixObj, &nPix); ????NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithCGLPixelFormatObj:pixObj]; ????CGLReleasePixelFormat(pixObj); ????? #else ????? ????// 創(chuàng)建pixel format,設(shè)置離屏渲染 ????NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:(const?NSOpenGLPixelFormatAttribute[]){NSOpenGLPFAAllRenderers, NSOpenGLPFAOffScreen, NSOpenGLPFAAllowOfflineRenderers, 0}]; #endif ????? ????// 創(chuàng)建OpenGL上下文 ????glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; ????[pixelFormat release];<br> ????// 將此上下文作為當(dāng)前上下文 ????[glContext makeCurrentContext]; ????? ????if(![self loadShaders]) ????????return; ????? ????glGenFramebuffers(1, &framebufferID); ????glBindFramebuffer(GL_FRAMEBUFFER, framebufferID); ????? ????glViewport(0, 0, MY_PIXEL_WIDTH, MY_PIXEL_HEIGHT); ????glClearColor(0.4f, 0.4f, 0.4f, 1.0f); ????? ????// Use shader program ????glUseProgram(program); ????? ????// Drawing code here. ????static?const?GLfloat squareVertices[] = { ????????-1.0f, -1.0f, 0.0f, ????????1.0f, -1.0f, 0.0f, ????????-1.0f,? 1.0f, 0.0f, ????????1.0f,? 1.0f, 0.0f ????}; ????? ????static?const?GLfloat texCoords[] = { ????????0.0f, 0.0f,???? // left lower ????????1.0f, 0.0f,???? // right lower ????????0.0f, 1.0f,???? // left upper ????????1.0f, 1.0f????? // right upper ????}; ????? ????glEnable(GL_CULL_FACE); ????? ????// 初始化紋理數(shù)據(jù) ????initCheckImage(); ????? ????glClampColorARB(GL_RGBA_FLOAT_MODE_ARB, GL_TRUE); ????glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE); ????glClampColorARB(GL_CLAMP_READ_COLOR_ARB, GL_FALSE); ????? ????// 初始化紋理 ????glPixelStorei(GL_UNPACK_ALIGNMENT, 8); ????glActiveTexture(GL_TEXTURE0); ????glGenTextures(1, &texName); ????glBindTexture(GL_TEXTURE_2D, texName); ????? ????glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); ????glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); ????glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ????glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ????? ????glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MY_PIXEL_WIDTH, MY_PIXEL_HEIGHT, 0, GL_RGBA, GL_FLOAT, checkImage); ????? ????// 將紋理綁定到幀緩存 ????glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texName, 0); ????glUniform1i(uniforms[UNIFORM_SAMPLER], 0);????? // Set the sampler value ????? ????// Update attribute values ????glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, 0, 0, squareVertices); ????glEnableVertexAttribArray(ATTRIB_VERTEX); ????glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, 0, 0, texCoords); ????glEnableVertexAttribArray(ATTRIB_TEXCOORD); } - (void)compute { ????glClear(GL_COLOR_BUFFER_BIT); ????? ????// Draw ????glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); ????? ????glFlush(); ????? ????glReadPixels(0, 0, MY_PIXEL_WIDTH, MY_PIXEL_HEIGHT, GL_RGBA, GL_FLOAT, my_buffer); ????NSLog(@"R:%f, G:%f, B:%f, A:%f", my_buffer[0], my_buffer[1], my_buffer[2], my_buffer[3]); } @end |
下面給出Vertex shader和Fragment shader,這兩個Shader的代碼很簡單:
| // //? Shader.vsh //? GLSLTest // //? Created by Zenny Chen on 4/11/10. //? Copyright GreenGames Studio 2010. All rights reserved. // #version 120 #extension GL_EXT_gpu_shader4 : enable attribute vec4 position; attribute vec4 texCoords; uniform mat4 translate; uniform mat4 projection; void?main() {??? ????gl_Position = position; ????gl_TexCoord[0] = texCoords; } |
| // //? Shader.fsh //? GLSLTest // //? Created by Zenny Chen on 4/11/10. //? Copyright GreenGames Studio 2010. All rights reserved. // #version 120 uniform sampler2D sampler; void?main() { ????// gl_FragColor ????gl_FragColor = texture2D(sampler, gl_TexCoord[0].st) + vec4(0.4, 0.3, 0.2, 1.1); } |
最后給出OS X的AppDelegate中的實(shí)現(xiàn)。各位可以拿這代碼自己嘗試一下,看看輸出結(jié)果~
| // //? AppDelegate.h //? OpenGL4Compute // //? Created by zenny_chen on 12-12-9. //? Copyright (c) 2012年 zenny_chen. All rights reserved. // #import <Cocoa/Cocoa.h> @class?MyGLView; @interface AppDelegate : NSObject <NSApplicationDelegate> { ????MyGLView *glComputeObj; } @property (assign) IBOutlet NSWindow *window; @end |
| // //? AppDelegate.m //? OpenGL4Compute // //? Created by zenny_chen on 12-12-9. //? Copyright (c) 2012年 zenny_chen. All rights reserved. // #import "AppDelegate.h" #import "MyGLView.h" @implementation AppDelegate - (void)dealloc { ????[super dealloc]; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { ????// Insert code here to initialize your application ????? ????NSView *baseView = self.window.contentView; ????CGSize viewSize = baseView.frame.size; ????? ????NSButton *button = [[NSButton alloc] initWithFrame:CGRectMake(20.0f, viewSize.height - 60.0f, 100.0f, 30.0f)]; ????[button setButtonType:NSMomentaryPushInButton]; ????[button setBezelStyle:NSRoundedBezelStyle]; ????[button setTitle:@"Compute"]; ????[button setTarget:self]; ????[button setAction:@selector(computeButtonTouched:)]; ????[baseView addSubview:button]; ????[button release]; } - (void)computeButtonTouched:(id)sender { ????if(glComputeObj == nil) ????{ ????????glComputeObj = [[MyGLView alloc] init]; ????????[glComputeObj prepareOpenGL]; ????} ????[glComputeObj compute]; } @end |
總結(jié)
以上是生活随笔為你收集整理的OS X下使用OpenGL做离屏渲染的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图形的变换方式有哪些
- 下一篇: Leetcode 14.最长公共前缀