2012年3月30日 星期五

OpenGL ES 入門: 二. 簡單繪圖之一

since: 2012/03/30
update: 2012/03/30

reference:
1. 原文:
iPhone Development: OpenGL ES From the Ground Up, Part 2: A Look at Simple Drawing

2. 翻譯:
從零開始學習OpenGL ES之二 – 簡單繪圖概述


3. Developer's Note: Protocol & Delegate

簡單繪圖之一: 繪圖架構

A. 說明
      1. 為了儘量接近原始文章的架構, 因此需要使用到 Delegate (委任) 的方式.

      2. 先不使用 OpenGL ES 2, 而使用 OpenGL ES 1.

      3. 目前主要有三個類別檔案: GLView, ViewControllerAppDelegate,
           功能調整如下:

            a. GLView:
                (1). 將定義一個 protocol: <GLViewDelegate>, 並新增一個將實作此 protocol
                       的變數 delegate

                (2). <GLViewDelegate> protocol 宣告了二個需要被實作的方法:
                       // 用來設置 view
                       - (void)setupView:(GLView *)view;

                       // 這是最主要繪圖的地方
                       - (void)drawView:(GLView *)view;

                (3). GLView 類別新增了以下的方法:
                       // 於繪圖前, 清除緩衝區內容
                       - (void)destroyFrameBuffer;

                       // 建立緩衝區內容, 並呼叫 delegate 的 setupView:
                       - (BOOL)createFrameBuffer;

                       // 呼叫 delegate 的 drawView: , 並實際將內容畫到螢幕上
                       - (void)drawView;

                       說明: 以上三個方法將藉由覆寫本身(GLView)的 layoutSubviews方法,
                                  而依序自動被呼叫.

            b. ViewController:
                (1). 宣告 ViewController 類別, 必須遵循 <GLViewDelegate> protocol 的規範.

                (2). ViewController 必須實作 <GLViewDelegate> protocol 所宣告的二個方法:
                       // 用來設置 view
                       - (void)setupView:(GLView *)view;   

                       // 這是最主要繪圖的地方
                       - (void)drawView:(GLView *)view;

           c. AppDelegate:
               在 application:didFinishLaunchingWithOptions: 方法內, 將 GLView 實體
               的 delegate 指向 ViewController 實體.

               self.glView.delegate = self.viewController;


      4. 整個繪圖架構流程大致如下:
        
         步驟 01. AppDelegate => didFinishLaunchingWithOptions

         步驟 02. GLView => layoutSubviews

                步驟 03. GLView => destroyFrameBuffer

                步驟 04. GLView => createFrameBuffer

                        步驟 05. ViewController => setupView:

                步驟 06. GLView => drawView

                        步驟 07. ViewController => drawView:

-----------------------------------------------------------------------------------------------

B. 開啓 GLView.h 檔案, 修改如下:
#import <UIKit/UIKit.h>
//@add
#import <OpenGLES/EAGL.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#import "OpenGLESCommon.h"
#import "ConstantsAndMacros.h"

//@add for protocol
@protocol GLViewDelegate;

@interface GLView : UIView
{
    //@add
    EAGLContext *glContext;
   
    //@add
    GLint backingWidth;
    GLint backingHeight;
    GLuint viewRenderBuffer;
    GLuint viewFrameBuffer;
    GLuint depthRenderBuffer;
   
    id<GLViewDelegate> delegate;
}

//@add
@property (nonatomic, strong) EAGLContext *glContext;
@property (assign) /* weak ref */ id <GLViewDelegate> delegate;

//@add
- (void)destroyFrameBuffer;
- (BOOL)createFrameBuffer;
- (void)drawView;

@end

//@add for protocol
@protocol GLViewDelegate

- (void)setupView:(GLView *)view;
- (void)drawView:(GLView *)view;

@end

-----------------------------------------------------------------------------------------------

C. 開啓 GLView.m 檔案, 修改如下:
#import "GLView.h"

@implementation GLView

//@add
@synthesize glContext = _glContext;
@synthesize delegate = _delegate;

//@add
- (EAGLContext *)glContext
{
    if (_glContext == nil) {
        //_glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

        //@update
        _glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
    }
   
    return _glContext;
}
....
//@update: comment it
/*
- (id)initWithFrame:(CGRect)frame
{
....
}
*/

....
//@add: overide
- (void)layoutSubviews
{
    NSLog(@"step 02. GLView => layoutSubviews");
   
    [EAGLContext setCurrentContext:self.glContext];
   
    [self destroyFrameBuffer];
   
    [self createFrameBuffer];
   
    [self drawView];
}

//@add
- (void)destroyFrameBuffer
{
    NSLog(@"step 03. GLView => destroyFrameBuffer");
   
    glDeleteFramebuffersOES(1, &viewFrameBuffer);
    viewFrameBuffer = 0;
   
    glDeleteRenderbuffersOES(1, &viewRenderBuffer);
    viewRenderBuffer = 0;
   
    if(depthRenderBuffer)
    {
        glDeleteRenderbuffersOES(1, &depthRenderBuffer);
        depthRenderBuffer = 0;
    }
}

//@add
- (BOOL)createFrameBuffer
{
    NSLog(@"step 04. GLView => createFrameBuffer");

    // 從 UIView 取得 layer (CALayer), 向下轉型成 CAEAGLLayer.
    // 在此為安全的, 因為 layerClass 方法已被覆寫)
    CAEAGLLayer *eaglLayer = (CAEAGLLayer*) super.layer;
   
    // 直接使用 OpenGL 設定不透明度
    eaglLayer.opaque = YES;
   
    // 定義: 渲染(render)與幅(frame)緩衝區(buffer)
    glGenFramebuffersOES(1, &viewFrameBuffer);
    glGenRenderbuffersOES(1, &viewRenderBuffer);
   
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFrameBuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderBuffer);
   
    [self.glContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer];
   
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderBuffer);
   
    /**** 以下此段非必要 ****/
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);

    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
   
    if (USE_DEPTH_BUFFER)
    {
        glGenRenderbuffersOES(1, &depthRenderBuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderBuffer);

        glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);

        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderBuffer);

    }
   
    if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
    {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));

        return NO;
    }
    /**** 以上此段非必要 ****/
   
    // GO: step 05. ViewController => setupView:
    [self.delegate setupView:self];
   
    return YES;
}

//@add
- (void)drawView
{
    NSLog(@"step 06. GLView => drawView");
       
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFrameBuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderBuffer);
   
    // GO: step 07. ViewController => drawView:
    [self.delegate drawView:self];
   
    [self.glContext presentRenderbuffer:GL_RENDERBUFFER_OES];
}
....

-----------------------------------------------------------------------------------------------

D. 開啓 ViewController.h 檔案, 修改如下:
#import <UIKit/UIKit.h>
//@add
#import "GLView.h"

//@interface ViewController : UIViewController
//@update
@interface ViewController : UIViewController <GLViewDelegate>
{
}
@end

-----------------------------------------------------------------------------------------------

E. 開啓 ViewController.m 檔案, 修改如下:
....
//@add for <GLViewDelegate> method
-(void)setupView:(GLView *)view
{
    NSLog(@"step 05. ViewController => setupView:");
   
    const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0;
    GLfloat size;
    glEnable(GL_DEPTH_TEST);
    glMatrixMode(GL_PROJECTION);
    size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
   
    CGRect rect = view.bounds;
     glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size /
               (rect.size.width / rect.size.height), zNear, zFar);
   
    // 建構一個對應的座標系統
    glViewport(0, 0, rect.size.width, rect.size.height); 
   
    glMatrixMode(GL_MODELVIEW);
   
    glLoadIdentity();
}

//@add for <GLViewDelegate> method
- (void)drawView:(GLView *)view
{
    NSLog(@"step 07. ViewController => drawView:");

    // Draw code here
    glClearColor(0.5f, 0.5f, 0.5f, 1); // 將顏色定義為灰色
    glClear(GL_COLOR_BUFFER_BIT); // 執行清除操作
}
....
//@update: comment it
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}
*/
....

-----------------------------------------------------------------------------------------------

F. 開啓 AppDelegate.m 檔案, 修改如下:
....
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    /*
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    */
    //@update
   
    NSLog(@"step 01. AppDelegate => didFinishLaunchingWithOptions");
   
    CGRect screenBounds = [[UIScreen mainScreen] bounds];
   
    self.window = [[UIWindow alloc] initWithFrame:screenBounds];
    self.viewController = [[ViewController alloc] init];
    self.glView = [[GLView alloc] initWithFrame:screenBounds];
   
    self.window.rootViewController = viewController;
    self.viewController.view = self.glView;
   
    //@add for delegate
    self.glView.delegate = self.viewController;
   
    [self.window addSubview:self.viewController.view];
    [self.window makeKeyAndVisible];

    return YES;
}
....

-----------------------------------------------------------------------------------------------

G. 編譯並執行

OpenGLESBegin[3779:fb03]
step 01. AppDelegate => didFinishLaunchingWithOptions
OpenGLESBegin[3779:fb03] step 02. GLView => layoutSubviews
OpenGLESBegin[3779:fb03] step 03. GLView => destroyFrameBuffer
OpenGLESBegin[3779:fb03] step 04. GLView => createFrameBuffer
OpenGLESBegin[3779:fb03] step 05. ViewController => setupView:
OpenGLESBegin[3779:fb03] step 06. GLView => drawView
OpenGLESBegin[3779:fb03] step 07. ViewController => drawView:

-----------------------------------------------------------------------------------------------

H. 備註
      1. 很多 code 原本應該是由 template 產生的, 但是找不到原始可用的 template,
         所以變成自行 coding, 因此缺少了許多的說明; 目前先將重心放在畫圖本身.

      2. 以後畫圖的 code, 幾乎都是寫在 ViewController.m 裡的 drawView: 內.
//@add for <GLViewDelegate> method
- (void)drawView:(GLView *)view
{
    NSLog(@"step 07. ViewController => drawView:");
   
    // Draw code here
    ....
}

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。