2012年3月29日 星期四

OpenGL ES 入門: 一. 基本概念

since: 2012/03/29
update: 2012/04/08

reference:
1. 原文:
    iPhone Development: OpenGL ES from the Ground Up: Table of Contents
    (iPhone Development: OpenGL ES From the Ground Up, Part 1: Basic Concepts)

2. 翻譯:
從零開始學習OpenGL ES之一 – 基本概念

3. I touchs: OpenGL ES 小試

基本概念

A. 說明
      1. 由於原文 / 譯文的 Empty OpenGL Xcode project template 已無法下載,
          因此主要參考譯文的版本來重新建立專案.

      2. 本文章著重在實作的部分, 相關的概念說明, 請參考原文 / 譯文的內容.

      3. 原本嘗試使用 OpenGL Game Project 來實作, 發現並沒有很簡潔, 因此決定
          採用 Empty Application Project.

      4. 本專案採用 Xcode 4.3.2, iOS SDK 5.1.

      5. 由於版本的差異與相關參考資料的不足, 本系列文章的實驗性質較重, 可能會
          更改較多次, 或者無法全部實作.

           (update: 2012/03/30)
      6. 有找到一個範例檔, 可以參考: Empty.OpenGL.ES.Application.zip
           (請直接點選 "聯通下載" 或點 "聯通下載右方箭頭" 出現的 "電信下載")

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

B. 相關詞彙
      1. vertex: 頂點, 代表三維空間中的一個點.
          說明: OpenGL ES 只支援三角形.

      2. winding: 卷繞, 表示頂點繪製的次序.
          說明: a. 預設情況下, OpenGL 的三角形只有一個可見面(front face: 前面), 不被
                         繪製的一面稱為 backface(背面), 確認可見面, 才能使OpenGL只做一半
                         的計算.

                     b. 預設情況下, 以反時針次序繪製頂點的構成的面是 frontface. OpenGL
                         使用 Backface Culling(隱面消除)的技術來避免繪製視窗中多邊形的
                         不可見面.

      3. render: 渲染, 描繪, 繪圖

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

C. 新增專案
       Xcode > File > New > Project... > iOS > Application >
       Empty Application > Next
       Product Name: OpenGLESBegin
       Company Identifier: com.blogspot
       Device Family: iPhone
       Use Automatic Reference Counting: checked
       > Next > Create

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

D. 新增 Framework
     新增以下的 Framework:
     OpenGLES
     
- Is used for visualizing 2D and 3D data.

     QuartzCore

     - Add 2D graphics rendering support.

     GLKit
     - P
rovides libraries of commonly needed functions and classes to reduce the effort
       needed to create a new OpenGL ES 2.0 application or the effort required to port
       an existing OpenGL ES 1.1 application to OpenGL ES 2.0.


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

E. 新增 UIViewController 子類別
   1. 說明: view controller 在此專案裡是非必要的, 只是不想在將來看到以下的訊息,
                 所以就把它加進來了.

                 Application windows are expected to have a root view controller
                 at the end of application launch.

     
   2. Xcode > File > New > File...
       > iOS > Cocoa Touch > Objective-C class > Next
       Class: ViewController
       Subclass of: UIViewController
       > Next > Create

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

F.
新增 UIView 子類別
      Xcode > File > New > File...
      > iOS > Cocoa Touch > Objective-C class > Next
      Class: GLView
      Subclass of: UIView
      > Next > Create

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

G. 開啓 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>

@interface GLView : UIView
{
    //@add
    EAGLContext *glContext;
}

//@add
@property (nonatomic, strong) EAGLContext *glContext;

//@add
- (void)drawView;

@end

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

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

@implementation GLView

//@add
@synthesize glContext = _glContext;

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

//@add: 覆寫 layerClass 方法(類似 typeof), 回傳一個 OpenGL Layer 的類別
+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        //@add
        //      從 UIView 取得 layer (CALayer), 向下轉型成 CAEAGLLayer.
        //      在此為安全的, 因為 layerClass 方法已被覆寫)
        CAEAGLLayer *eaglLayer = (CAEAGLLayer*) super.layer;
       
        // 直接使用 OpenGL 設定不透明度
        eaglLayer.opaque = YES;
       
        // 設定 Current Context
        [EAGLContext setCurrentContext:self.glContext];
       
        //@add: OpenGL 初始化
        // 定義:渲染(render)與幅(frame)緩衝區(buffer); GLuint 同義於 unsigned int
        GLuint framebuffer, renderbuffer;
       
        glGenFramebuffersOES(1, &framebuffer);
        glGenRenderbuffersOES(1, &renderbuffer);
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, renderbuffer);
       
        [self.glContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer];
       
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER_OES, renderbuffer);
       
        // 建構一個對應的座標系統
        glViewport(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
       
        [self drawView];
    }
    return self;
}

//@add
- (void)drawView
{
    glClearColor(0.5f, 0.5f, 0.5f, 1); // 將顏色定義為灰色
    glClear(GL_COLOR_BUFFER_BIT); // 執行清除操作
   
    // 先將定義好的灰色填入緩衝區, 接著發佈到螢幕上
    [self.glContext presentRenderbuffer:GL_RENDERBUFFER_OES];
}

//@add
- (void)dealloc
{
    if ([EAGLContext currentContext] == self.glContext) {
        [EAGLContext setCurrentContext:nil];
    }
}

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

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

@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
    UIWindow *window;
    //@add
    ViewController *viewController;
    GLView *glView;
}

@property (strong, nonatomic) UIWindow *window;
//@add
@property (strong, nonatomic) ViewController *viewController;
@property (strong, nonatomic) GLView *glView;

@end

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

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

@implementation AppDelegate

@synthesize window = _window;
//@add
@synthesize glView = _glView;
@synthesize viewController = _viewController;

- (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
    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;
   
    [self.window addSubview:self.viewController.view];
    [self.window makeKeyAndVisible];
   
    return YES;
}
....

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

K. 隱藏狀態欄位
      開啓 OpenGLESBegin-Info.plist 檔案, 新增一筆資料如下:
      Key: Status bar is initially hidden
      Value: YES

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

L. 編譯並執行

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

M. 新增 OpenGL ES 的通用檔案
    1. Xcode > File > New > File...
           > iOS > C and C++ > Header File > Next

          Save As: OpenGLESCommon.h         
          Targets: OpenGLESBegin (勾選)
         > Create

    2. 開啓 OpenGLESCommon.h 檔案, 修改如下:
/*
#ifndef OpenGLESBegin_OpenGLESCommon_h
#define OpenGLESBegin_OpenGLESCommon_h

#endif
*/

#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>

#pragma mark -
#pragma mark Vertex3D
#pragma mark -

// 頂點定義
typedef struct {
    GLfloat    x;
    GLfloat y;
    GLfloat z;
} Vertex3D;

// 單個頂點
static inline Vertex3D Vertex3DMake(CGFloat inX, CGFloat inY, CGFloat inZ)
{
    Vertex3D ret;
   
    ret.x = inX;
    ret.y = inY;
    ret.z = inZ;
   
    return ret;
}

// 三維空間中任何兩點間的直線距離
static inline GLfloat Vertex3DCalculateDistanceBetweenVertices(Vertex3D first, Vertex3D second)
{
    GLfloat deltaX = second.x - first.x;
    GLfloat deltaY = second.y - first.y;
    GLfloat deltaZ = second.z - first.z;
   
    return sqrtf(deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ ); // sqrtf: 開根號
}

#pragma mark -
#pragma mark Triangle3D
#pragma mark -

// 三角形定義
typedef struct {
    Vertex3D v1;
    Vertex3D v2;
    Vertex3D v3;
} Triangle3D;

// 三角形物體
static inline Triangle3D Triangle3DMake(Vertex3D inV1, Vertex3D inV2, Vertex3D inV3)
{
    Triangle3D ret;
   
    ret.v1 = inV1;
    ret.v2 = inV2;
    ret.v3 = inV3;
   
    return ret;
}

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

N. 新增常數與巨集定義檔案
      1. Xcode > File > New > File...
           > iOS > C and C++ > Header File > Next

          Save As: ConstantsAndMacros.h 
          Targets: OpenGLESBegin (勾選)
         > Create      
       
         2. 開啓 ConstantsAndMacros.h 檔案, 修改如下: (update: 2012/03/30)
/*
#ifndef OpenGLESBegin_ConstantsAndMacros_h
#define OpenGLESBegin_ConstantsAndMacros_h

#endif

*/


// How many times a second to refresh the screen

#define kRenderingFrequency             15.0

// Defines whether to setup and use a depth buffer
#define USE_DEPTH_BUFFER                1

// Macros
#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)

      2. 開啓 OpenGLESCommon.h 檔案, 修改如下: (2012/04/08 update)
....
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
//@add
#import "ConstantsAndMacros.h"
....

沒有留言:

張貼留言

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