OS X中如何使用OpenGL 3.2 Core Profile
从OS X Lion(10.7)开始,Apple支持了对OpenGL 3.2 Core Profile的支持。不过Core Profile与Compatible相比有比较大幅度的改变。从主机端的API到OpenGL接口,再到GLSL(OpenGL Shading Language),这些方面都有些变化。
在主机端接口方面,首先,必须用<OpenGL/gl3.h>来代替原来的<OpenGL/gl.h>,这点很重要!
其次,在NSOpenGLPixelFormatAttribute对象中必须增加NSOpenGLPFAOpenGLProfile以及NSOpenGLProfileVersion3_2Core这两个属性。
在最后完成绘制后,需要调用CGL库的CGLFlushDrawable接口将所渲染的图形结果显示到视图或窗口上。
在OpenGL接口使用方面,必须使用VAO和VBO来存放顶点数据。然后,所有与固定功能流水线相关的接口都被移出,无法使用。因此这些被移除的功能只能通过Shader来实现。
在GLSL方面,必须显式地加上GLSL的版本号,并且至少为140(表示1.40),在基于Sandy Bridge的Intel HD Graphics 3000上默认为150(1.50)。然后,in、out代替了原来的attribute和varying关键字。在Fragment shader中,gl_FragColor内建变量被取消,你可以自己定义一个out变量作为最后像素的输出。
下面给出一个比较基本、简洁的代码样例:
// // MyGLView.h // OpenGLShaderBasic // // Created by Zenny Chen on 10/4/10. // Copyright 2010 GreenGames Studio. All rights reserved. // #import <Cocoa/Cocoa.h> @interface MyGLView : NSOpenGLView { GLuint program; GLuint vbo, vao; } - (void)render; @end
// // MyGLView.m // OpenGLShaderBasic // // Created by Zenny Chen on 10/4/10. // Copyright 2010 GreenGames Studio. All rights reserved. // #import "MyGLView.h" // 这里必须注意!<gl3.h>头文件必须被包含并取代<gl.h>,否则VAO接口会调用不正常,从而无法正确显示图形! #import <OpenGL/gl3.h> #define MY_PIXEL_WIDTH 128 #define MY_PIXEL_HEIGHT 128 @implementation MyGLView // attribute index enum { ATTRIB_VERTEX, 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(); // bind attribute locations // this needs to be done prior to linking glBindAttribLocation(program, ATTRIB_VERTEX, "inPos"); // 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); //glBindFragDataLocationEXT(program, 0, "myOutput"); // link program if (![self linkProgram:program]) { NSLog(@"Failed to link program: %d", program); return FALSE; } // release vertex and fragment shaders if (vertShader) glDeleteShader(vertShader); if (fragShader) glDeleteShader(fragShader); return TRUE; } - (id)initWithFrame:(NSRect)frameRect { self = [super initWithFrame:frameRect]; NSOpenGLPixelFormatAttribute attrs[] = { NSOpenGLPFADoubleBuffer, // 可选地,可以使用双缓冲 NSOpenGLPFAOpenGLProfile, // Must specify the 3.2 Core Profile to use OpenGL 3.2 NSOpenGLProfileVersion3_2Core, 0 }; NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; if (!pf) { NSLog(@"No OpenGL pixel format"); } NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil]; [self setPixelFormat:pf]; [pf release]; [self setOpenGLContext:context]; [context release]; return self; } - (void)dealloc { if(vbo != 0) glDeleteFramebuffers(1, &vbo); if(vao != 0) glDeleteRenderbuffers(1, &vao); [super dealloc]; } - (void)prepareOpenGL { [super prepareOpenGL]; [[self openGLContext] makeCurrentContext]; // Synchronize buffer swaps with vertical refresh rate GLint swapInt = 1; [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; // Drawing code here. static const GLfloat squareVertices[] = { -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f }; // In OpenGL 3.2 core profile, vertex buffer object and vertex array object must be used! glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(squareVertices), squareVertices, GL_STATIC_DRAW); // Update attribute values glEnableVertexAttribArray(ATTRIB_VERTEX); glVertexAttribPointer(ATTRIB_VERTEX, 4, GL_FLOAT, 0, 0, (const GLvoid*)0); // Load shaders and build the program if(![self loadShaders]) return; // Use shader program glUseProgram(program); glViewport(0, 0, MY_PIXEL_WIDTH, MY_PIXEL_HEIGHT); glClearColor(0.5f, 0.5f, 0.5f, 1.0f); } - (void)render { // render glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glFlush(); CGLFlushDrawable([[self openGLContext] CGLContextObj]); } @end
以上两个文件是主要的使用OpenGL接口的样例头文件和源文件。在这个例子中,OpenGL的上下文基于一个视图(NSOpenGLView的子类)。
下面将给出Shader源代码:
// // Shader.fsh // GLSLTest // // Created by Zenny Chen on 4/11/10. // Copyright GreenGames Studio 2010. All rights reserved. // // 在OpenGL3.2 Core Profile中,版本号必须显式地给出 #version 150 out vec4 myOutput; void main() { //gl_FragColor = vec4(0.1, 0.8, 0.5, 1.0); myOutput = vec4(0.1, 0.8, 0.5, 1.0); }
// // Shader.vsh // GLSLTest // // Created by Zenny Chen on 4/11/10. // Copyright GreenGames Studio 2010. All rights reserved. // // 在OpenGL3.2 Core Profile中,版本号必须显式地给出 #version 150 in vec4 inPos; void main() { gl_Position = inPos; }
下面给出AppDelegate中对MyGLView对象的具体调用。
// // 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, 60.0f, 30.0f)]; [button setButtonType:NSMomentaryPushInButton]; [button setBezelStyle:NSRoundedBezelStyle]; [button setTitle:@"Show"]; [button setTarget:self]; [button setAction:@selector(computeButtonTouched:)]; [baseView addSubview:button]; [button release]; NSLog(@"The view is: %@", [self.window.contentView class]); } - (void)computeButtonTouched:(id)sender { if(glComputeObj == nil) { NSView *baseView = self.window.contentView; CGSize viewSize = baseView.frame.size; glComputeObj = [[MyGLView alloc] initWithFrame:CGRectMake((viewSize.width - 128.0f) * 0.5f, (viewSize.height - 128.0f) * 0.5f, 128.0f, 128.0f)]; [baseView addSubview:glComputeObj]; [glComputeObj release]; } [glComputeObj performSelector:@selector(render) withObject:nil afterDelay:0.1]; } @end
工程代码可以在以下地址获取:
http://www.cocoachina.com/bbs/read.php?tid=35895&page=1&toread=1#tpc
OpenGL3_2.zip