Opengl 入门教程的翻译

来源:互联网 发布:java实现加法 编辑:程序博客网 时间:2024/05/17 07:41

Opengl 入门教程的翻译

今天我们学习opengl 2.0 的入门课程.用到的APIs被苹果打包叫做GLKit,主要包括4大部分.

·      GLKView/GLKViewController. 集成了搭建基础的OpenGL    ES项目所需要的大多数的重复性的代码.

·      GLKEffects.集成了和1.0通用的渲染行为,使得程序员可以平滑的过多到2.0,也是实现一般的光照和纹理效果的方便做法.

·      GLMath.IOS5之前大多数的游戏需要自己的数学函数库,里面做通用的向量运算和矩阵运算.现在好了,GLMath帮我们集成了这些数学函数库.

·      GLKTextureLoader.该类是我们方便的将图片加载成为纹理,以便在OpenGl中使用.加载纹理现在只是一个简单的函数调用,以前??以前你需要些负责的方法处理成千上万的图片文件格式.

该教程的目的是在GLKit的基础上快速的搭建一个OpenGl应用.你不需要任何的基础知识.我们将在教程中绘制一个立方体,并且使其转动起来.

在这个过程中,你讲学习GLKit中新API的基本用法.无论你以前使用过OpenGl,还是你是一个完全的新鸟,该教程都能向你好好的介绍一个GLKit.

如果教程中涉及到的一些章节你已经知道,请无情的略过.

1.02.0的区别

该教程把焦点放在2.0,不是1.0.如果你不知道,我可以大概罗列一下1.0和2.0的主要区别:

·      1.0使用固定流水线,也就是说你只能只用内建的函数去设置光线,顶点,颜色,摄像机等元素.

·      2.0使用可编程的流水线,也就是说内建的函数都没了,你要自己实现那些光线,顶点,颜色等元素的设置.

额滴神?那我还为毛还非要使用2.0呀,既然2.0要干额外的火.虽然2.0要干额外的活,但是好多1.0做不到的效果2.0能实现,就像下面的这个卡通着色效果.

或者向下面这样很酷的光照和阴影效果.

很酷吧?

2.0 在主要的苹果设备上都可以用.2.0比1.0更难入门.但是用GLKit的帮助,我们学习起来会轻松一些.应为GLKEffects和GLKMath使我们能够很容易的实现1.0里面的效果.

如果你以前没有学过OpenGL,那么直接上2.0.

开始干活

Xcode新建一个工程,选择的工程模板是iOS\Application\Empty Application Template.我们选的是空模板,所以我们有机会平地起高楼,全面的了解里面到底干了什么.工程名字叫做HelloGLKit,选择IPhone,只选择ARC,其他的选项不要选.下一步,确定一个工程的目录,创建完成.

这是一个空工程就可以跑起来了.

我们没有写任何的代码程序就跑起来了,里面干了什么呢?我们快速的看一下:在main.m文件件中,可以看到入口函数main:

 

int main(int argc,char *argv[]){    

@autoreleasepool {   

return UIApplicationMain(argc, argv,nil, NSStringFromClass([AppDelegate class]));    

}

}

函数调用的最有一个参数是应用要创建哪个对象的实例,该对象的实例被用作代理,在这个代码中就是要创建一个AppDelegate类的实例.跳到AppDelegate.m文件,看看他的实现:

 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    self.window = [[UIWindowalloc] initWithFrame:[[UIScreenmainScreen] bounds]];

    // Override point for customization after application launch.

    self.window.backgroundColor = [UIColorwhiteColor];

    [self.windowmakeKeyAndVisible];

    return YES;

}

就是用代码新建一个主窗口,然后让窗口课件.没有一个Xcode工程模板比这个内容更少了.

了解一下GLKView

为了开始2.0,首先我们要再window的里面增加一个子视图,这个子视图用OpenGL来挥之.如果你以前用过2.0编程,你就知道这里面是一堆程式化 的代码-----例如创建渲染缓冲区和帧缓冲区等东西.但是现在有了GLKView类,就简单多了.当我们想用OpenGL渲染一个视图的时候,只要新建一个GLKView类型的视图,然后设置一些属性就好了.然后把一个类作为GLKView的代理,当GLKView需要绘制的时候会调用代理类上的方法.你只要在这个方法里面添加你想要执行的OpenGL指令.

具体怎么干?首先----为工程添加GLKit需要使用的一些框架.选中HelloGLKit工程,选中HelloGLKit的target,选择BuildPhases,展开LinkBinary With Libraries段,点击+号来增加如下的框架.

·      QuartzCore.framework

·      OpenGLES.framework

·      GLKit.framework

AppDelegate.h, 文件中增加如下代码

#import <GLKit/GLKit.h>

AppDelegate.m, 文件中修改这个方法application:didFinishLaunchingWithOptions,为主窗口增加一个GLKView类型的子视图.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    self.window = [[UIWindowalloc] initWithFrame:[[UIScreenmainScreen] bounds]];

    // Override point for customization after application launch.

   

   

     EAGLContext * context = [[EAGLContextalloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];// 1

    GLKView *view = [[GLKViewalloc] initWithFrame:[[UIScreenmainScreen] bounds]];// 2

    view.context = context; // 3

    view.delegate = self; // 4,Application类作为了GLKView的代理,所以他需要实现一个接口

    [self.windowaddSubview:view]; // 5   

   

    self.window.backgroundColor = [UIColorwhiteColor];

    [self.windowmakeKeyAndVisible];

    return YES;

}}

新加入的行都加了编号.

1. Create a OpenGL context. To do anything with OpenGL, you need to create aEAGLContext.

An EAGLContext manages all of the information iOS needsto draw with OpenGL. It’s similar to how you need a Core Graphics context to doanything with Core Graphics.

When you create a context, you specify what version ofthe API you want to use. Here, you specify that you want to use OpenGL ES 2.0.If it is not available (such as if the program were run on an iPhone 3G), theapp would terminate.

2. Create a GLKView. This creates a new instance of a GLKView, and makes it as large as the entire window.

3. Set the GLKView’s context. When you create a GLKView, you need to tell it theOpenGL context to use, so we specify the one we already created.

4. Set the GLKView’s delegate. This sets the current class (AppDelegate) as theGLKView’s delegate. This means whenever the view needs to be redrawn, it willcall a method named glkView:drawInRect on whatever class you specify here. Wewill implement this inside the App Delegate shortly to contain some basicOpenGL commands to paint the screen red.

5. Add the GLKView as a subview. This line adds the GLKView as a subview of the mainwindow.

Since we marked the App Delegate as GLKView’d delegate,we need to mark it as implementing the GLKViewDelegate protocol. So switch toAppDelegate.h and modify the @interface line as follows:

@interface AppDelegate : UIResponder <UIApplicationDelegate, GLKViewDelegate>

One step left! Switch back to AppDelegate.m, add thefollowing code right before the @end:

 

#pragma mark - GLKViewDelegate  

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect

{

    glClearColor(1.0,0.0, 0.0, 1.0); // 设置清屏色

    glClear(GL_COLOR_BUFFER_BIT); // 执行清屏动作

}

The first line calls glClearColor to specify the RGB and alpha (transparency) valuesto use when clearing the screen. We set it to red here.

The second line calls glClear to actually perform the clearing. Remember that there can bedifferent types of buffers, such as the render/color buffer we’re displaying,and others we’re not using yet such as depth or stencil buffers. Here we usethe GL_COLOR_BUFFER_BIT to specify what exactly to clear – in this case, thecurrent render/color buffer.

有各种类型的缓冲区,渲染缓冲区,颜色缓冲区,还有其他的深度缓冲区,蒙版缓冲区.这里的我们用GL_COLOR_BUFFER_BIT参数支出到你要清哪个缓冲区------- GL_COLOR_BUFFER_BIT指的就是当前的渲染颜色缓冲区.

That’s it! Compile and run, and with just 7 lines of codewe have OpenGL rendering to the screen!

Those of you who are completely new to OpenGL might notbe very impressed, but those of you who have done this before will be veryhappy with the convenience here :]

GLKView的属性和方法

我们设置了GLKView的两个属性---context和代理,他还有其他的属性和方法.

context and delegate

已经介绍过了,不再赘述.

drawableColorFormat

在OpenGL的上下文中有个用于保存颜色值的缓冲区.你可以用这个属性设置颜色的格式. 默认的颜色格式GLKViewDrawableColorFormatRGBA8888代表是是每一个颜色成分(r g b a)占用8个位,所以每个像素占用4个字节.这个格式很好给了我们最大范围的颜色范围,应用做出来会很靓. 如果你的应用不需要那么多的颜色,你可以考虑GLKViewDrawableColorFormatRGB565,这样应用会消耗更少的资源(内存和处理时间).

drawableDepthFormat

在OpenGL的上下文中还有一个可配置的缓冲区---深度缓冲区.深度缓冲区保证离观察者更近的物体”盖住”远的物体.

OpenGL默认的工作方式是对每个像素点都保存一个更近的物体是谁,保存的这些值放在深度缓冲区离.当OpenGl绘制每一个像素的时候都检测深度缓冲区,判断一下OpenGL是不是已经绘制过一个离观察者更近的物体,如果确实绘制过,OpenGl就会丢弃这个点,不然OpenGl就会把这个像素添加到深度缓冲区和颜色缓冲区.

你可以设置drawableDepthFormat属性来选择深度缓冲区的格式.默认值是GLKViewDrawableDepthFormatNone----代表不开启深度缓冲区.

如果想要开启这个特性,可以选择GLKViewDrawableDepthFormat16或者GLKViewDrawableDepthFormat24. GLKViewDrawableDepthFormat16消耗的资源少,但是当物体非常接近的时候,深度缓冲区的绘制可能会有一些不准确.

drawableStencilFormat

另外一个可以配置的缓冲区是蒙版缓冲区.蒙版缓冲区是限制我们只能在屏幕的特定部分绘制图形.可以用于影子的绘制,如果墙只能容纳你影子的一半,另外的一半就需要用蒙版技术去掉.默认值是GLKViewDrawableStencilFormatNone,可以设置属性为GLKViewDrawableStencilFormat8打开模板缓冲区.

drawableMultisample

GLKView还有一个属性是多次采样缓冲区的设置.如果你使用OpenGL划线,发现线上有锯齿,多次采样可以解决这个问题.基本的做法是这样:不再是对于每个像素调用一次frag渲染器,而是把一个像素分成更小的单位多次调用frag渲染器.然后融合多次渲染得到的颜色值,这样最终的结果就会更加平滑.打开这个属性要小型,因为他将消耗更多的资源.默认值是GLKViewDrawableMultisampleNone,可以设置为GLKViewDrawableMultisample4X打开这个选项.

drawableHeight/drawableWidth

These are read-only properties that indicate the integerheight and width of your various buffers. These are based on the bounds andcontentSize of the view – the buffers are automatically resized when thesechange.

snapshot

This is a handy way to get a UIImage of the view’scurrent context.

bindDrawable

OpenGL has yet another buffer called a frame buffer,which is basically a collection of all the other buffers we talked about (colorbuffer, depth buffer, stencil buffer etc).

OpenGL还有另外一个缓冲区,叫做帧缓冲区,它是其他缓冲区的集合.在我们调用glkView:drawInRect之前,GLKit将会在场景后面绑定到它自己建立起来的帧缓冲区.但是如果你的游戏需要渠道另外的帧缓冲区去执行一些其他类型的渲染,你可以使用bindDrawable方法,让GLKit重新回去绑定刚才的帧缓冲区.

deleteDrawable

GLKView and OpenGL take a substantial amount of memoryfor all of these buffers. If your GLKView isn’t visible, you might find ituseful to deallocate this memory temporarily until it becomes visible again. Ifyou want to do this, just use this method!

Next time the view is drawn, GLKView will automaticallyre-allocate the memory behind the scenes. Quite handy, eh?

enableSetNeedsDisplay and display

I don’t want to spoil the surprise – we’ll explain thesein the next section! :]

Updating the GLKView

Let’s try to update our GLKView periodically, like wewould in a game. How about we make the screen pulse from red to black, kind oflike a “Red Alert” effect!

Go to the top of AppDelegate.m and modify the@implementation line to add two private variables as follows:

@implementation AppDelegate

{

    float _curRed;

    BOOL _increasing;

}

And initialize these inapplication:didFinishLaunchingWithOptions:

_increasing = YES;

_curRed = 0.0;

Then go to the glkView:drawInRect method and update it tothe following:

#pragma mark - GLKViewDelegate  

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect

{

    if (_increasing) {

        _curRed += 0.01;

    } else {

        _curRed -= 0.01;

    }

    if (_curRed >=1.0) {

        _curRed = 1.0;

        _increasing = NO;

    }

    if (_curRed <=0.0) {

        _curRed = 0.0;

        _increasing = YES;

    }

    glClearColor(_curRed,0.0, 0.0, 1.0);

    glClear(GL_COLOR_BUFFER_BIT);

}

Every time drawInRect is called, it updates the _curRedvalue a little bit based on whether it’s increasing or decreasing. Note thatthis code isn’t perfect, because it doesn’t take into effect how long it takesbetween calls to drawInRect. This means that the animation might be faster orslower based on how quickly drawInRect is called. We’ll discuss a way to fixthis later in the tutorial.

Compile and run and… wait a minute, nothing’s happening!

By default, the GLKView only updates itself on anas-needed basis – i.e. when views are first shown, the size changes, or thelike. However for game programming, you often need to redraw every frame!

默认情况下,GLKView只有在需要的情况下才会重绘,例如理一次显示出来,大小改变的时候.但是游戏编程中经常需要每一帧都重绘.

我们可以修改不重绘的行为, enableSetNeedsDisplay设为false就好了.

We can disable this default behavior of GLKView bysetting enableSetNeedsDisplay to false. Then, we can control when the redrawingoccurs by calling the display method on GLKView whenever we want to update thescreen.

Ideally we would like to synchronize the time we renderwith OpenGL to the rate at which the screen refreshes.

Luckily, Apple provides an easy way for us to do thiswith CADisplayLink! It’s really easy to use so let’s just dive in. First addthis import to the top of AppDelegate.m:

#import <QuartzCore/QuartzCore.h>

Then add these lines toapplication:didFinishLaunchingWithOptions:

view.enableSetNeedsDisplay = NO; // 调用display就会重绘

   

    CADisplayLink* displayLink = [CADisplayLinkdisplayLinkWithTarget:selfselector:@selector(render:)];// 注册周期性回调函数

    [displayLink addToRunLoop:[NSRunLoopcurrentRunLoop] forMode:NSDefaultRunLoopMode];

Then add a new render function as follows:

- (void)render:(CADisplayLink*)displayLink

{

    GLKView * view = [self.window.subviewsobjectAtIndex:0];

    [view display];

}

Compile and run, and you should now see a cool pulsating“red alert” effect!

Introducing GLKViewController

You know that code we just wrote in that last section?Well you can just forget about it, because there’s a much easier way to do soby using GLKViewController :]

The reason I showed you how to do it with plain GLKViewfirst was so you understand the point behind using GLKViewController – it savesyou from writing that code, plus adds some extra neat features that you wouldhave had to code yourself.

So try out GLKViewController. Modify yourapplication:didFinishLaunchingWithOptions to look like this:

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {     self.window= [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];       EAGLContext * context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];     GLKView*view = [[GLKView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];     view.context= context;     view.delegate = self;     //[self.window addSubview:view];       _increasing= YES;     _curRed= 0.0;      //view.enableSetNeedsDisplay = NO;     //CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];    //[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];       GLKViewController * viewController= [[GLKViewController alloc] initWithNibName:nil bundle:nil];// 1     viewController.view = view; // 2     viewController.delegate= self; // 3     viewController.preferredFramesPerSecond= 60; // 4     self.window.rootViewController= viewController; // 5       self.window.backgroundColor= [UIColor whiteColor];    [self.window makeKeyAndVisible];    return YES;}

Feel free to delete the commented lines – I justcommented them out so it is easy to see what’s no longer needed. There are alsofour new lines (marked with comments):

1. Create a GLKViewController. This creates a new instance of a GLKViewControllerprogramatically. In this case, it has no XIB associated.

2. Set the GLKViewController’s view. The root view of a GLKViewController should be aGLKView, so we set it to the one we already created.

3. Set the GLKViewController’s delegate. We set the current class (AppDelegate) as thedelegate of the GLKViewController. This means that the GLKViewController willnotify us each frame so we can run game logic, or when the game pauses (a nicebuilt-in feature of GLKViewController we’ll demonstrate later).

4. Set the preferred FPS. The GLKViewController will call your draw method acertain number of times per second. This number gives a hint to theGLKViewController how often you’d like to be called. Of course, if your gametakes a long time to render frames, the actual number may be lower than this.

The default value is 30 FPS. Apple’s guidelines are toset this to whatever your app can reliably support to the frame rate isconsistent and doesn’t seem to stutter. This app is very simple so can easilyrun at 60 FPS, so we set it to that.

Also as an FYI, if you want to see the actual number oftimes the OS will attempt to call your update/draw methods, check the read-onlyframesPerSecond property.

5. Set the rootViewController. We want this view controller to be the first thingthat shows up, so we add it as the rootViewController of the window. Note thatwe no longer need to add the view as a subview of the window manually, becauseit’s the root view of the GLKViewController.

Notice that we no longer need the code to run the renderloop and tell the GLView to refresh each frame – GLKViewController does thatfor us in the background! So go ahead and comment out the render method aswell.

Also remember that we set the GLKViewController’sdelegate to the current class (AppDelegate), so let’s mark it as implementingGLKViewControllerDelegate. Switch to AppDelegate.h and replace the@implementation with the following line:

@interface AppDelegate : UIResponder <UIApplicationDelegate, GLKViewDelegate, GLKViewControllerDelegate>

The final step is to update the glkView:drawInRectmethod, and add the implementation for GLKViewController’sglkViewControllerUpdate callback:

#pragma mark - GLKViewDelegate  

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {

    glClearColor(_curRed,0.0, 0.0, 1.0);

    glClear(GL_COLOR_BUFFER_BIT);

}

 

#pragma mark - GLKViewControllerDelegate 

- (void)glkViewControllerUpdate:(GLKViewController *)controller {

    if (_increasing) {

        _curRed += 1.0 * controller.timeSinceLastUpdate;

    } else {

        _curRed -= 1.0 * controller.timeSinceLastUpdate;

    }

   

    if (_curRed >=1.0) {

        _curRed = 1.0;

        _increasing = NO;

    }

    if (_curRed <=0.0) {

        _curRed = 0.0;

        _increasing = YES;

    }

}

Note that we moved the code to change the current colorfrom the draw method (where it didn’t really belong) to the update method(intended for game/app logic).

Also notice that we changed the amount the red colorincrements from a hardcoded value to a calculated value, based on the amount oftime since the last update. This is nice because it guarantees the animationwill always proceed at the same speed, regardless of the frame rate.

This is another of those convenient thingsGLKViewController does for you! We didn’t have to write special code to storethe time since the last update – it did it for us! There are some othertime-based properties, but we’ll discuss those later.

 

 

 

////  AppDelegate.h//  HelloOpenGL////  Created by stephen.xing on 12/6/14.//  Copyright (c) 2014 IDREAMSKEY. All rights reserved.//#import <UIKit/UIKit.h>#import <GLKit/GLKit.h>@interface AppDelegate : UIResponder <UIApplicationDelegate, GLKViewDelegate, GLKViewControllerDelegate>@property (strong, nonatomic) UIWindow *window;@end

////  AppDelegate.m//  HelloOpenGL////  Created by stephen.xing on 12/6/14.//  Copyright (c) 2014 IDREAMSKEY. All rights reserved.//#import "AppDelegate.h"#import <QuartzCore/QuartzCore.h>@implementation AppDelegate{    float _curRed;    BOOL _increasing;}- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];        EAGLContext * context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];        GLKView *view = [[GLKView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];    view.context = context;    view.delegate = self; // 因为设置了GLKView的代理,所以 需要实现(void)glkView:(GLKView *)view drawInRect:(CGRect)rect 方法    //[self.window addSubview:view];        _increasing = YES;    _curRed = 0.0;    //view.enableSetNeedsDisplay = NO;    //CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];    //[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];        GLKViewController * viewController = [[GLKViewController alloc] initWithNibName:nil bundle:nil]; // 1 新建    viewController.view = view; // 2 绑定c v    viewController.delegate = self;    // 3 因为设置了GLKviewController的代理,所以需要实现 (void)glkViewControllerUpdate:(GLKViewController *)controller方法    viewController.preferredFramesPerSecond = 60; // 4    self.window.rootViewController = viewController; // 5 希望第一个被显示,所以将他作为window的rootviewcontroller    self.window.backgroundColor = [UIColor whiteColor];    [self.window makeKeyAndVisible];    return YES;}//- (void)render:(CADisplayLink*)displayLink//{//    GLKView * view = [self.window.subviews objectAtIndex:0];//    [view display];//}- (void)applicationWillResignActive:(UIApplication *)application{    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.}- (void)applicationDidEnterBackground:(UIApplication *)application{    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.     // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.}- (void)applicationWillEnterForeground:(UIApplication *)application{    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.}- (void)applicationDidBecomeActive:(UIApplication *)application{    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.}- (void)applicationWillTerminate:(UIApplication *)application{    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.}#pragma mark - GLKViewDelegate   - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {    glClearColor(_curRed, 0.0, 0.0, 1.0);    glClear(GL_COLOR_BUFFER_BIT);}#pragma mark - GLKViewControllerDelegate  - (void)glkViewControllerUpdate:(GLKViewController *)controller {    if (_increasing) {        _curRed += 1.0 * controller.timeSinceLastUpdate;    } else {        _curRed -= 1.0 * controller.timeSinceLastUpdate;    }        if (_curRed >= 1.0) {        _curRed = 1.0;        _increasing = NO;    }    if (_curRed <= 0.0) {        _curRed = 0.0;        _increasing = YES;    }}@end



 

0 0
原创粉丝点击