GLFun(13)~Texture2Dクラス(2)

2011. 12. 31
●Texture2D.mソースファイル

#import <OpenGLES/ES1/glext.h>

#import "Texture2D.h"

//CONSTANTS:

#define kMaxTextureSize 1024

//CLASS IMPLEMENTATIONS:

@implementation Texture2D

@synthesize contentSize=_size, pixelFormat=_format, pixelsWide=_width, pixelsHigh=_height, name=_name, maxS=_maxS, maxT=_maxT;

//------------------------------------------------------------------------------
// BEGIN CHANGES - From here to END CHANGES are not part of the original
// Apple sample code, modification made as allowed by license
// JDL - August 1, 2008 JDL
//
// This code is necessary if this class is being used in a program that has
// drawing done both with and without textures. This code needs tog get called
// once before any texture is drawn, but if you attempt to draw without a
// texture after these have been called and before any drawing with a texture
// happens, it crashes.
//------------------------------------------------------------------------------
+ (void) initialize {

    // These calls need to get called once for the class to work, but if they are called before OpenGL knows about any textures, it crashes,
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
//------------------------------------------------------------------------------
// END CHANGES
//------------------------------------------------------------------------------
- (id) initWithData:(const void*)data pixelFormat:(Texture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size
{
    GLint saveName;
    if((self = [super init])) {
        glGenTextures(1, &_name);

        glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName);
        glBindTexture(GL_TEXTURE_2D, _name);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        switch(pixelFormat) {
            case kTexture2DPixelFormat_RGBA8888:
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
                break;
            case kTexture2DPixelFormat_RGB565:
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
                break;
            case kTexture2DPixelFormat_A8:
                glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
                break;
            default:
                [NSException raise:NSInternalInconsistencyException format:@""];
        }
        glBindTexture(GL_TEXTURE_2D, saveName);

        _size = size;
        _width = width;
        _height = height;
        _format = pixelFormat;
        _maxS = size.width / (float)width;
        _maxT = size.height / (float)height;
    }
    return self;
}

- (void) dealloc
{
    if(_name)
        glDeleteTextures(1, &_name);

    [super dealloc];
}

- (NSString*) description
{
    return [NSString stringWithFormat:@"<%@ = %08X | Name = %i | Dimensions = %ix%i | Coordinates = (%.2f, %.2f)>", [self class], self, _name, _width, _height, _maxS, _maxT];
}

@end

@implementation Texture2D (Image)

- (id) initWithImage:(UIImage *)uiImage
{
    NSUInteger width,
         height,
         i;
    CGContextRef context = nil;
    void* data = nil;
    CGColorSpaceRef colorSpace;
    void* tempData;
    unsigned int* inPixel32;
    unsigned short* outPixel16;
    BOOL hasAlpha;
    CGImageAlphaInfo info;
    CGAffineTransform transform;
    CGSize imageSize;
    Texture2DPixelFormat pixelFormat;
    CGImageRef image;
    UIImageOrientation orientation;
    BOOL sizeToFit = NO;

    image = [uiImage CGImage];
    orientation = [uiImage imageOrientation];

    if(image == NULL) {
        [self release];
        NSLog(@"Image is Null");
        return nil;
    }

    info = CGImageGetAlphaInfo(image);
    hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO);
    if(CGImageGetColorSpace(image)) {
        if(hasAlpha)
            pixelFormat = kTexture2DPixelFormat_RGBA8888;
        else
            pixelFormat = kTexture2DPixelFormat_RGB565;
    } else     //NOTE: No colorspace means a mask image
        pixelFormat = kTexture2DPixelFormat_A8;

    imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
    transform = CGAffineTransformIdentity;

    width = imageSize.width;

    if((width != 1) && (width & (width - 1))) {
        i = 1;
        while((sizeToFit ? 2 * i : i) < width)
            i *= 2;
        width = i;
    }
    height = imageSize.height;
    if((height != 1) && (height & (height - 1))) {
        i = 1;
        while((sizeToFit ? 2 * i : i) < height)
            i *= 2;
        height = i;
    }
    while((width > kMaxTextureSize) || (height < kMaxTextureSize)) {
        width /= 2;
        height /= 2;
        transform = CGAffineTransformScale(transform, 0.5, 0.5);
        imageSize.width *= 0.5;
        imageSize.height *= 0.5;
    }

    switch(pixelFormat) {
        case kTexture2DPixelFormat_RGBA8888:
            colorSpace = CGColorSpaceCreateDeviceRGB();
            data = malloc(height * width * 4);
            context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
            CGColorSpaceRelease(colorSpace);
            break;
        case kTexture2DPixelFormat_RGB565:
            colorSpace = CGColorSpaceCreateDeviceRGB();
            data = malloc(height * width * 4);
            context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big);
            CGColorSpaceRelease(colorSpace);
            break;
        case kTexture2DPixelFormat_A8:
            data = malloc(height * width);
            context = CGBitmapContextCreate(data, width, height, 8, width, NULL, kCGImageAlphaOnly);
            break;
        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"];
    }

    CGContextClearRect(context, CGRectMake(0, 0, width, height));
    CGContextTranslateCTM(context, 0, height - imageSize.height);

    if(!CGAffineTransformIsIdentity(transform))
        CGContextConcatCTM(context, transform);
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
    //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
    if(pixelFormat == kTexture2DPixelFormat_RGB565) {
        tempData = malloc(height * width * 2);
        inPixel32 = (unsigned int*)data;
        outPixel16 = (unsigned short*)tempData;
        for(i = 0; i < width * height; ++i, ++inPixel32)
            *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);
        free(data);
        data = tempData;
    }
    self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:width pixelsHigh:height contentSize:imageSize];

    CGContextRelease(context);
    free(data);

    return self;
}

@end

@implementation Texture2D (Text)

- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size
{
    NSUInteger width,
        height,
        i;
    CGContextRef context;
    void* data;
    CGColorSpaceRef colorSpace;
    UIFont * font;

    font = [UIFont fontWithName:name size:size];
    width = dimensions.width;
    if((width != 1) && (width & (width - 1))) {
        i = 1;
        while(i < width)
        i *= 2;
        width = i;
    }
    height = dimensions.height;
    if((height != 1) && (height & (height - 1))) {
        i = 1;
        while(i < height)
        i *= 2;
        height = i;
    }

    colorSpace = CGColorSpaceCreateDeviceGray();
    data = calloc(height, width);
    context = CGBitmapContextCreate(data, width, height, 8, width, colorSpace, kCGImageAlphaNone);
    CGColorSpaceRelease(colorSpace);

    CGContextSetGrayFillColor(context, 1.0, 1.0);
    CGContextTranslateCTM(context, 0.0, height);
    CGContextScaleCTM(context, 1.0, -1.0);    //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
    UIGraphicsPushContext(context);
        [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:alignment];
    UIGraphicsPopContext();

    self = [self initWithData:data pixelFormat:kTexture2DPixelFormat_A8 pixelsWide:width pixelsHigh:height contentSize:dimensions];

    CGContextRelease(context);
    free(data);

    return self;
}

@end

@implementation Texture2D (Drawing)

- (void) drawAtPoint:(CGPoint)point
{
    GLfloat coordinates[] = { 0, _maxT,
        _maxS, _maxT,
        0, 0,
        _maxS, 0 };
    GLfloat width = (GLfloat)_width * _maxS,
        height = (GLfloat)_height * _maxT;
    GLfloat vertices[] = { -width / 2 + point.x, -height / 2 + point.y, 0.0,
        width / 2 + point.x, -height / 2 + point.y, 0.0,
        -width / 2 + point.x, height / 2 + point.y, 0.0,
        width / 2 + point.x, height / 2 + point.y, 0.0 };

    glBindTexture(GL_TEXTURE_2D, _name);
    glVertexPointer(3, GL_FLOAT, 0, vertices);
    glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

- (void) drawInRect:(CGRect)rect
{
    GLfloat coordinates[] = { 0, _maxT,
        _maxS, _maxT,
        0, 0,
        _maxS, 0 };
    GLfloat vertices[] = { rect.origin.x, rect.origin.y, 0.0,
        rect.origin.x + rect.size.width, rect.origin.y, 0.0,
        rect.origin.x, rect.origin.y + rect.size.height, 0.0,
        rect.origin.x + rect.size.width, rect.origin.y + rect.size.height, 0.0 };

    glBindTexture(GL_TEXTURE_2D, _name);
    glVertexPointer(3, GL_FLOAT, 0, vertices);
    glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

@end

7706


1)インポート

ソースファイルでOpenGL ES 1.1の拡張機能『ES1/glext.h』をインポートしていますが、実際には拡張機能は使用しておらず、コメントアウトしても問題無く動作します。

おそらくコードのカスタマイズ用に予めインポートしているだけと思われます。


2)定数

kMaxTextureSizeの1024は、『iOS OpenGL ESプログラミングガイド』の『PowerVR MBX上のOpenGL ES 1.1』で示されているように、PowerVR MBXを搭載している機種(iPhone、iPhone 3Gと第一、第二世代iPod touch)における、OpenGL ES 1.1の2Dテクスチャの最大サイズ1024×1024ピクセルのことを指しています。
(各機種のGPUおよび対応OpenGL ESのバージョンについては『GLFun(1)~iOSでのOpenGL ESの利用』を参照してください)


3)プロパティの実装

ヘッダファイルで宣言した7つのプロパティを実装しています。

@synthesize contentSize=_size, pixelFormat=_format, pixelsWide=_width, pixelsHigh=_height, name=_name, maxS=_maxS, maxT=_maxT;

プロパティインスタンス変数
 GLuint  name  _name 
 GLSize  contentSize  _size 
 NSUInteger  pixelWide  _width 
 pixelHigh  _height 
 Texture2DPixelFormat  pixelFormat  _format 
 GLfloat  maxS  _maxS 
 maxT  _maxT 



参考文献

iOS OpenGL ESプログラミングガイド

OpenGL ES 1.1 Reference Pages

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(12)~Texture2Dクラス(1)

2011. 12. 29
Texture2DクラスはApple社が提供している、画像やテキストからOpenGL ESの2Dテクスチャを生成するクラスです。

コードの冒頭にあるライセンス条項に付いては、壁谷武憲さんのサイト『cabi@net:work』にある左側のフレームにある『文書』のリンクから『Appleのサンプルプログラムに付いているライセンス条項(和訳)』にて翻訳されているので参照してください。


●Texture2D.hヘッダファイル

#import <UIKit/UIKit.h>
#import <OpenGLES/ES1/gl.h>

//CONSTANTS:


typedef enum {
    kTexture2DPixelFormat_Automatic = 0,
    kTexture2DPixelFormat_RGBA8888,
    kTexture2DPixelFormat_RGB565,
    kTexture2DPixelFormat_A8,
} Texture2DPixelFormat;

//CLASS INTERFACES:

/*
This class allows to easily create OpenGL 2D textures from images, text or raw data.
The created Texture2D object will always have power-of-two dimensions.
Depending on how you create the Texture2D object, the actual image area of the texture might be smaller than the texture dimensions i.e. "contentSize" != (pixelsWide, pixelsHigh) and (maxS, maxT) != (1.0, 1.0).
Be aware that the content of the generated textures will be upside-down!
*/

@interface Texture2D : NSObject
{
@private
    GLuint    _name;
    CGSize    _size;
    NSUInteger    _width,
                           _height;
    Texture2DPixelFormat    _format;
    GLfloat    _maxS,
                     _maxT;
}

- (id) initWithData:(const void*)data pixelFormat:(Texture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size;

@property(readonly) Texture2DPixelFormat pixelFormat;
@property(readonly) NSUInteger pixelsWide;
@property(readonly) NSUInteger pixelsHigh;

@property(readonly) GLuint name;

@property(readonly, nonatomic) CGSize contentSize;
@property(readonly) GLfloat maxS;
@property(readonly) GLfloat maxT;
@end

/*
Drawing extensions to make it easy to draw basic quads using a Texture2D object.
These functions require GL_TEXTURE_2D and both GL_VERTEX_ARRAY and GL_TEXTURE_COORD_ARRAY client states to be enabled.
*/

@interface Texture2D (Drawing)
- (void) drawAtPoint:(CGPoint)point;
- (void) drawInRect:(CGRect)rect;
@end

/*
Extensions to make it easy to create a Texture2D object from an image file.

Note that RGBA type textures will have their alpha premultiplied - use the blending mode (GL_ONE, GL_ONE_MINUS_SRC_ALPHA).

*/

@interface Texture2D (Image)
- (id) initWithImage:(UIImage *)uiImage;
@end

/*
Extensions to make it easy to create a Texture2D object from a string of text.
Note that the generated textures are of type A8 - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
*/

@interface Texture2D (Text)
- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:
(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size;
@end

7705


1)インポート

Texture2DクラスはNSObjectクラスをスーパークラスとした、OpenGL ESでのテクスチャを生成するクラスです。

OpenGL ESやCore Graphicsの関数で大半が占められていますが、画像からテクスチャを生成する『initWithImage:』メソッドでのUIImageクラスや、テキストからテクスチャを生成する『initWithString:dimensions:alignment:fontName:fontSize:』メソッドでのUIFontクラスなど、一部UIKitの関数も含まれるため、UIKit.hをインポートしています。

またOpenGL ESについてはES1のgl.hのみインポートしているので、このクラスはOpenGL ES 1.1のみに対応することが分かります。


2)定数

Texture2DPixelFormatは2Dテクスチャ画像のピクセルフォーマットを示す識別子で、列挙型で4つの定数を設定しています。

kTexture2DPixelFormat_Automatic未使用
kTexture2DPixelFormat_RGBA8888アルファチャンネルを含む
32ビットのピクセルフォーマット
kTexture2DPixelFormat_RGB565アルファチャンネルを含まない
16ビットのピクセルフォーマット
kTexture2DPixelFormat_A8マスク画像またはテキスト用の
8ビットのピクセルフォーマット


3)クラスのインターフェイス

このクラスは画像やテキスト、または生データからOpenGLの2Dテクスチャを簡単に生成することができます。
生成されたTexture2Dオブジェクトは、常に2の累乗の大きさになります。
Texture2Dオブジェクトの生成の仕方によって、テクスチャの実際の画像領域はテクスチャのサイズよりも小さくなるかもしれません。
すなわち、『"contentSize" != (pixelsWide, pixelsHigh)』で『(maxS, maxT) != (1.0, 1.0)』です。
生成されたテクスチャの内容は逆さまになることに注意してください。


テクスチャ画像サイズの留意点)

OpenGLでは基本的にテクスチャサイズは2の累乗(べき乗とも呼ばれます。英語ではPower Of Two、略してPOTと記述される場合もあります)にする必要があります。
(『マイナビニュース/エンラープライズ/【コラム】実践! iPhoneアプリ開発 (20) タワーディフェンスゲームの作り方 (3) - 背景の描画』参照)

拡張機能(GL_OES_texture_npotなど)で2の累乗のサイズも扱うことができるという話もあるようですが、『iOS OpenGL ESプログラミングガイド』の中でも
  • 『PowerVR SGXは、2のべき乗以外のキューブマップテクスチャやミップマップテクスチャはサポートしていません。』

  • 『PVRTexToolを使用してテクスチャを圧縮している場合は、2のべき乗の長さを持つ正方形の テクスチャを作成しなければなりません。』
と制約があるので、基本的には2の累乗サイズでテクスチャを用意するのが無難のようです。

今回のGLFunプロジェクトで使用するテクスチャ画像iphone.pngは52×100ピクセルですが、画像からテクスチャを生成する『initWithImage:』メソッドや、テキストからテクスチャを生成する『initWithString:dimensions:alignment:fontName:fontSize:』メソッド内で、任意のサイズから2の累乗のサイズへの補正を行っているからです。

実際には64×128ピクセルのテクスチャとして生成した後に、52×100ピクセルのテクスチャリングされたプリミティブとして表示し、サイズや縦横比を気にしなくて済むように処理されています。

定数)

ここで宣言されているインスタンス変数は全て@privateになっており、Texture2Dクラス内でのみアクセス可能で、サブクラスでは使用できません。

変数名内容
 GLuint _name テクスチャの名前(識別番号)
 CGSize _size テクスチャ画像の実サイズ
 NSUInteger _width テクスチャの幅(2の累乗に補正した値)
 _height テクスチャの高さ(2の累乗に補正した値)
 Texture2DPixelFormat  _format ピクセルフォーマット
 GLfloat _maxS テクスチャ座標系での幅比
(テクスチャの実幅/2の累乗に補正した幅)
 _maxT テクスチャ座標系での高さ比
(テクスチャの実高さ/2の累乗に補正した高さ) 

前述の通り、テクスチャの幅と高さは2の累乗にする必要があるため、元となる画像の実サイズ_sizeの他に2の累乗に補正した_widthと_height、更に表示する際に元の実サイズに補正するための_maxSと_maxTという変数を用意しています。

_maxSと_maxTは、横軸をS、縦軸をTとしたテクスチャ座標系の値で、詳細は後述します。

また、ここでの変数名は全て頭に『_(アンダースコア)』が付いていますが、『_』で始まる接頭語はAppleがインスタンス変数やプライベートメソッドを名付けるための予約語で、一般の開発者が使用してはいけないことになっています。
(『詳解 Objective-C 2.0』(最新版『詳解 Objective-C 2.0 第3版』)『Appendix B コーディングの指針』にある項目『接頭語』や、『ポッチンルーム/ポッチンコンバート/Cocoaのためのコーディングガイドライン』(原文:『Coding Guidelines for Cocoa』)の『命名の基本』のページにある項目『文字使いの慣習』参照)

プロパティの宣言)

上記で宣言した7つのインスタンス変数は、全てプロパティとしても宣言しています。


4)描画カテゴリ

Texture2Dクラスでは、内部で『Drawing』『Image』『Text』の3つのカテゴリを追加しています。
(カテゴリについては『Sections(2)~カテゴリ』を参照)

特に巨大なクラスというわけではありませんが、特性ごとにモジュールを明確に分けているものと思われます。

Drawingカテゴリは、Texture2Dオブジェクトを使用して基本的な四角形を簡単に描画するための拡張機能です。
これらの関数はGL_TEXTURE_2Dと、クライアント状態のGL_VERTEX_ARRAYとGL_TEXTURE_COORD_ARRAYの両方を有効にする必要があります。


drawAtPoint:メソッドは指定した座標を中心に元画像サイズのテクスチャリングした矩形を描画します。

drawInRect:メソッドは指定した矩形にテクスチャリングします。

drawAtPoint:では指定した座標を中心としますが、drawInRect:は左下角を原点として描画されるので注意してください。

また、drawAtPoint:は元画像と同じサイズの矩形を描画しますが、drawInRectは異なる大きさ・縦横比を指定しても、それに合わせてスケーリングされて描画します。


5)画像カテゴリ

画像ファイルからTexture2Dオブジェクトを簡単に生成するための拡張機能です。
RGBA型のテクスチャはブレンディングモード(GL_ONE, GL_ONE_MINUS_SEC_ALPHA)を使用して、事前にアルファを乗算することに注意してください。


initWithImage:メソッドは、画像ファイルからテクスチャオブジェクトを作るメソッドで、OpenGL ESで扱えるようにサイズを2の累乗に補正したり、最大テクスチャサイズ以下に補正するルーチンも含まれています。


6)テキストカテゴリ

テキストの文字列からTexture2Dオブジェクトを簡単に生成するための拡張機能です。
生成されるテクスチャはA8型で、ブレンディングモード(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)を使用することに注意してください。


initWithString:dimensions:alignment:fontName:fontSize:メソッドは、テキスト文字列からテクスチャを作るメソッドです。

使用する機会は少ないと思いますが、一通りの解説は行います。



参考文献

iOS OpenGL ESプログラミングガイド

OpenGL ES 1.1 Reference Pages

Khronos OpenGL ES API Registry

cabi@net:work

マイナビニュース/エンラープライズ/【コラム】実践! iPhoneアプリ開発 (20) タワーディフェンスゲームの作り方 (3) - 背景の描画

ポッチンルーム/ポッチンコンバート/Cocoaのためのコーディングガイドライン

Coding Guidelines for Cocoa

詳解 Objective-C 2.0 第3版詳解 Objective-C 2.0 第3版
(2011/12/28)
荻原 剛志

商品詳細を見る

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(11)~GLFunViewクラス(2)

2011. 12. 27
7)線の描画

セグメンテッドコントロールで選択された図形は変数shapeTypeに格納され、switch文で各図形の描画コードに選別されます。

線の描画は、リソース消費を抑えるため最初にテクスチャ画像を保持するプロパティspriteが存在するかを確認し、あれば解放しています。

次に線分の頂点配列を生成します。

2D図形の頂点配列は一つの座標につきxとyの2つの要素が必要になるので、線分の頂点配列の要素は4つになり、[始点x, 始点y, 終点x, 終点y]と指定します。

x座標はドラッグの始点と終点の座標をそのまま設定できますが、UIKitの座標系とOpenGLの座標系ではy座標が反転しているので、画面サイズの高さからの差を取って変換します。

続いて線分の線幅をglLineWidthで設定した後、glVertexPointerで頂点配列の指定を行います。

第一引数は頂点ごとの座標数で、2Dの場合は2になります。

第二引数は頂点座標のデータ型を指定します。

第三引数は頂点間のバイトオフセットで、複数のデータ型が混在している場合などに調整する値ですが、ここでは調整不要なので0になります。

第四引数は先程設定した頂点配列を指定します。

最後にglDrawArraysでプリミティブとしてレンダリングの指示を出します。

第一引数は描画するプリミティブの形式で、線分の場合はGL_LINESになります。

第二引数は頂点配列における開始インデックスで、今回のプロジェクトの場合は一度に一つの頂点配列しか生成しないので、インデックスは0になります。

第三引数はインデックス数で、線分の場合は始点と終点の座標なので2になります。

glLineWidth

void glLineWidth(GLfloat width);

void glLineWidthx(GLfixed width);

ラスタライズされるラインの幅を指定します。

glLineWidthは、エイリアスとアンチエイリアス両方のラインの、ラスタライズの幅を指定します。

1以外の線幅を使用するとアンチエイリアシングが有効かどうかに応じて異なる効果が得られます。

ラインのアンチエイリアシングを有効または無効にするには、引数GL_LINE_SMOOTHでglEnableまたはglDisableを呼び出します。

ラインのアンチエイリアシングは、初期状態で無効になっています。

ラインのアンチエイリアシングを無効にした場合、実際の幅は最も近い整数の幅に丸められることによって決定されます。
(丸められた結果の値が0の場合、線幅は1とされます。)

x| >= |Δy|の場合、丸められたwidthの値をiとすると、ラスタライズされた各列がiピクセルで埋められます。

それ以外の場合、ラスタライズされた各行がiピクセルで埋められます。

アンチエイリアシングを有効にした場合、ラインのラスタライズは、数学的な線分の中心に実際のラインの長さに等しい長さで、現在の線幅に等しい幅を持つ矩形内にある領域と交差する各ピクセル正方形のためのフラグメントを生成します。

各フラグメントの適用範囲値は、対応するピクセル正方形と矩形領域の交差するウィンドウ座標領域です。

この値は保存され、最終的なラスタライズの工程で使用されます。

ラインのアンチエイリアシングが有効になっている場合でも全ての幅がサポートされる訳ではありません。

サポートされていない幅が要求された場合、サポートされている最も近い幅が使用されます。

幅1のみサポートが保証されており、それ以外は実装に依存します。

また、エイリアシングされた線幅も同様です。

サポートされている幅の範囲を照会するには、引数GL_ALIASED_LINE_WIDTH_RANGEまたはGL_SMOOTH_LINE_WIDTH_RANGEでglGetを呼び出します。

アンチエイリアシングされていない線幅は、実装依存の最大値にクランプされる場合があります。

最大幅の決定には、GL_ALIASED_LINE_WIDTH_RANGEでglGetを呼び出してください。

widthが0以下の場合、GL_INVALID_VALUEが発生します。

width:ラスタライズされるラインの幅を指定します。
初期値は1です。

glVertexPointer

void glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid * pointer); 

glVertexPointerは、レンダリング時に使用する頂点座標の配列の位置とデータを指定します。

sizeは頂点ごとの座標数を、typeは座標のデータ型を指定します。

strideはある頂点から次に与える頂点へのバイトストライドと、単一の配列または別々の配列内に格納する属性を指定します。
(単一の配列のストレージは、いくつかの実装上でより効率的な場合があります。)

頂点配列が指定されている場合、sizetypestridepointerはクライアント側の状態として保存されます。

頂点配列が有効な場合、glDrawArraysまたはglDrawElementsが呼び出された時に使用されます。

頂点配列を有効または無効にするには、引数GL_VERTEX_ARRAYでglEnableClientStateまたはglDisableClientStateを呼び出します。

頂点配列の初期状態は無効で、glDrawArraysまたはglDrawElementsが呼び出された時にアクセスできません。

予め指定された頂点と頂点属性配列から(全て同じ型の)プリミティブのシーケンスを構築するにはglDrawArraysを使用してください。

インデックス化された頂点と頂点属性によってプリミティブのシーケンスを構築するにはglDrawElementsを使用してください。

glVertexPointerは、通常クライアント側に実装されています。

sizeが2、3、または4でない場合、GL_INVALID_VALUEが発生します。

typeが有効な値でない場合、GL_INVALID_ENUMが発生します。

strideが負の場合、GL_INVALID_VALUEが発生します。

size:頂点ごとの座標数を指定します。
2、3、または4である必要があります。
初期値は4です。

type:配列内の各頂点座標のデータ型を指定します。
シンボリック定数GL_BYTE、GL_SHORT、そしてGL_FIXEDが有効です。
ただし初期値はGL_FLOATです。
一般的なプロファイルでは、シンボリック定数GL_FLOATも有効です。

stride:連続する頂点間のバイトオフセットを指定します。
strideが0の場合、頂点は配列内に隙間無く格納されていると解釈されます。
初期値は0です。

pointer:配列内の最初の頂点の最初の座標へのポインタを指定します。
初期値は0です。

glDrawArrays

void glDrawArrays(GLenum mode, GLint first, GLsizei count);

配列データからプリミティブをレンダリングします。

glDrawArraysは、非常に少ないサブルーチンの呼び出しで、複数の幾何学的プリミティブを指定します。

事前に頂点、法線、色、そしてテクスチャ座標それぞれの配列を指定すると、単一のglDrawArraysを呼び出してそれらを使用してプリミティブのシーケンスを構築することができます。

glDrawArraysが呼び出されると、それぞれ有効な配列を要素firstから始まりcountを使用して順次要素を取り出し、幾何学的なプリミティブのシーケンスを構築します。

modeは、どのようにプリミティブは構築されているか、そしてどのように配列の要素はこれらのプリミティブを構築するかを指定します。

GL_VERTEX_ARRAYが有効でない場合、幾何学的なプリミティブは生成されません。

glDrawArraysによって変更される頂点属性はglDrawArraysが戻った後に指定されていない値を持ちます。

例えばGL_COLOR_ARRAYが有効な場合、現在の色の値はglDrawArrays実行後に未定義になります。

変更されていない属性は、定義された値を保持します。

modeが有効な値でない場合、GL_INVALID_ENUMが発生します。

countが負の場合、GL_INVALID_VALUEが発生します。

mode:どのようにプリミティブをレンダリングするかを指定します。
シンボリック定数GL_POINTS、GL_LINE_STRIP、GL_LINE_LOOP、GL_LINES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN、GL_TRIANGLESが有効です。

first:有効な配列内での開始インデックスを指定します。

count:レンダリングするインデックス数を指定します。


8)矩形の描画

矩形の描画も線と同様に、最初にテクスチャspriteがあれば解放します。

矩形の頂点は4つなので、頂点配列の要素は8になります。

OpenGL ESはOpenGLと異なり、プリミティブの形状として使用できるのは点(GL_POINTS)と線(GL_LINES、GL_LINE_STRIP、GL_LINE_LOOP)、そして三角形(GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN)のみで、四角形(GL_QUADS、GL_QUAD_STRIP)と多角形(GL_POLYGON)は利用できず、そのため四角形を描画するには三角形を組み合わせる必要があります。
(『GLUTによる「手抜き」OpenGL入門/5.二次元図形を描く』参照)

したがって矩形は三角形のGL_TRIANGLE_STRIPまたはGL_TRIANGLE_FANで作成するために、座標の指定には順序が必要になり、線分の時よりも複雑になります。

ただ台形や平行四辺形などではなく矩形なので、必要な座標値は4つで済みます。

ドラッグの始点と終点は必ずしも座標の正方向に移動するとは限らないので、(y座標は座標を反転した後で)始点と終点を比較し、xとyそれぞれの最小値と最大値を求めます。

頂点配列は下図のように、右上から左上、左下、右下の順で設定されています。

B(minX, maxY) A(maxX, maxY)
 
(2, 3) (0, 1)
   
(4, 5) (6, 7)
C(minX, minY) D(maxX, minY)

GL_TRIANGLE_FANの場合、描画の順序は

 A - B - C → A - C - D (以降、A - D - E と続く)

と、開始点の頂点を起点として連続した三角形を描画します。

一方GL_TRIANGLE_STRIPの場合の描画順序は

 A - B - C → B - C - D (以降、C - D - E と続く)

と、頂点を一つずつずらして連続した三角形を描画します。

したがってA - B - C - Dという順の頂点配列をGL_TRIANGLE_STRIPで描画すると下図のような図形になります。

7704

もしGL_TRIANGLE_STRIPで矩形を描画する場合には、A - B - D - Cの順で配列を構成してください。

glVertexPointerは線の描画と同一で、glDrawArraysは第一引数をGL_TRIANGLE_FANに、第三引数は頂点数なので4に設定します。


9)楕円の描画

楕円の描画も矩形と同様に、最初にテクスチャspriteがあれば解放し、図形は三角形で描画することになります。

具体的にはドラッグした始点と終点を対角線とする矩形に内接する楕円形を、角度1度単位の三角形(GL_TRIANGLE_FAN)を359個並べて作成します。

楕円を描画するには、x軸とy軸方向の中心点からの距離(長径と短径)が必要です。
(『Wikipedia/楕円』参照)

楕円の長径または短径は、外接する矩形の高さと幅の半分の値なので、x軸方向の半径xradiusは始点と終点の差を半分に、y軸方向の半径yradiusは座標を反転した上で始点と終点の差を半分にして求めています。

楕円の中心点を示すオフセットxOffsetとyOffsetは、始点または終点の原点に近い方に半径を加算して求めるのですが、よくよく考えれば角度に無関係な定数なのでforループ内で求める必要性がなく、無意味に359回計算することになるので、forループに入る前に計算しておく方が良いでしょう。

forループでは角度をiとして、頂点配列に角度1度ごとのxとyの座標を設定しています。

計算式はx軸方向の楕円の半径をa、y軸方向の楕円の半径をb、ラジアンでの角度をtとすると以下のようになります。

x = a * cos(t)
y = b * sin(t)

QuartzFunでは使用されていなかったConstants.hで定義されている、度からラジアンへ変換するdegreesToRadianは、ここで使用されます。

コードではこの計算式にそれぞれの軸のオフセットを加算して、図形の描画位置を補正しています。

旧版の本書ならびにiOS 3.x対応のサンプルコードでは、角度の指定が『degreesToRadian(i)』となっていますが、正しくは『degreesToRadian(i / 2)』です。
iOS 4.x対応のサンプルコードでは修正されています)

これは頂点配列が [x0, y0, x1, y1, ...] と1つおきに設定するために、forループの変数 i を2ずつインクリメントしていることを失念してしまい、角度も2ずつインクリメントしてしまってると思われます。

実際の描画では見た目はあまり変わりませんが、正しくは角度1度ずつ359個の三角形で描画するはずが、角度2度ずつの179個の同じ三角形を2回描いていることになるので修正が必要です。

glVertexPointerは線の描画と同一で、glDrawArraysは第一引数をGL_TRIANGLE_FANに、第三引数は頂点数なので360に設定します。



参考文献

iOS OpenGL ESプログラミングガイド

OpenGL ES 1.1 Reference Pages

GLUTによる「手抜き」OpenGL入門

Wikipedia/楕円

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(10)~GLFunViewクラス(1)

2011. 12. 23
GLFunViewクラスはOpenGLES2DViewクラスをサブクラス化したクラスで、OpenGL ES 1.1を利用した2Dの図形を描画するとともに、ビューへのタッチイベントの処理も行います。

前回のQuartzFunプロジェクトにおけるQuartzFunViewクラスの図形描画部分をOpenGL ES用に描画コードを置き換えたもので、それ以外はほぼ同じです。


●GLFunView.hヘッダファイルの編集

描画に必要なドラッグの始点と終点、描画色、ランダム色かどうかの判定、描画図形、画像用テクスチャのインスタンス変数をプロパティとして宣言します。
(太字が追加・修正した部分)

#import <UIKit/UIKit.h>
#import "Constants.h"
#import "Texture2D.h"
#import "OpenGLES2DView.h"


@interface GLFunView : OpenGLES2DView {
    CGPoint firstTouch;
    CGPoint lastTouch;
    UIColor *currentColor;
    BOOL useRandomColor;
    ShapeType shapeType;
    Texture2D *sprite;

}

@property CGPoint firstTouch;
@property CGPoint lastTouch;
@property (nonatomic, retain) UIColor *currentColor;
@property BOOL useRandomColor;
@property ShapeType shapeType;
@property (nonatomic, retain) Texture2D *sprite;


@end

7702


1)ヘッダファイルのインポート

色と図形を選択するセグメンテッドコントロールで選択された値を取得するためのConstants.hと、画像をテクスチャとして貼り付けるためのTexture2D.h、親クラスとなるOpenGLES2DView.hを追加でインポートします。


2)親クラスの変更

GLFunViewクラスファイルを生成する際にUIViewクラスのテンプレートを使用したため、@interfaceでの親クラスの指定がUIViewクラスになっていますので、OpenGLES2DViewに修正します。


3)インスタンス変数とプロパティの宣言

ドラッグの始点firstTouch、終点lastTouch、現在選択されている色のcurrentColor、ランダム色が選択されているかを示すuseRandomColor、現在選択されている図形のshapeType、画像をテクスチャとして保持するspriteというインスタンス変数とプロパティを宣言します。


●GLFunView.mソースファイルの編集

ソースファイルではヘッダファイルで宣言したプロパティの実装と、nibファイルからのビューの読み込んで初期化、OpenGL ESでのカスタム描画、タッチイベントの処理を行います。
(太字が追加・修正した部分)

#import "GLFunView.h"
#import "UIColor-Random.h"

@implementation GLFunView
@synthesize firstTouch;
@synthesize lastTouch;
@synthesize currentColor;
@synthesize useRandomColor;
@synthesize shapeType;
@synthesize sprite;

- (id)initWithCoder:(NSCoder *)coder {
    if (self = [super initWithCoder:coder]) {
        self.currentColor = [UIColor redColor];
        self.useRandomColor = NO;
    }
    return self;
}

- (void)draw {
    glLoadIdentity();
    glClearColor(0.78f, 0.78f, 0.78f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    CGColorRef color = currentColor.CGColor;
    const CGFloat *components = CGColorGetComponents(color);
    CGFloat red = components[0];
    CGFloat green = components[1];
    CGFloat blue = components[2];
    glColor4f(red, green, blue, 1.0);

    switch (shapeType) {
        case kLineShape: {
            if (sprite) {
                [sprite release];
                self.sprite = nil;
            }
            GLfloat vertices[4];
            // Convert coordinates
            vertices[0] = firstTouch.x;
            vertices[1] = self.frame.size.height - firstTouch.y;
            vertices[2] = lastTouch.x;
            vertices[3] = self.frame.size.height - lastTouch.y;
            glLineWidth(2.0);
            glVertexPointer(2, GL_FLOAT, 0, vertices);
            glDrawArrays(GL_LINES, 0, 2);
            break;
        }
        case kRectShape: {
            if (sprite) {
                [sprite release];
                self.sprite = nil;
            }
            // Calculate bounding rect and store in vertices
            GLfloat vertices[8];
            GLfloat minX = (firstTouch.x > lastTouch.x) ? lastTouch.x : firstTouch.x;
            GLfloat minY = (self.frame.size.height - firstTouch.y > self.frame.size.height - lastTouch.y) ?
                    self.frame.size.height - lastTouch.y : self.frame.size.height - firstTouch.y;
            GLfloat maxX = (firstTouch.x > lastTouch.x) ? firstTouch.x : lastTouch.x;
            GLfloat maxY = (self.frame.size.height - firstTouch.y > self.frame.size.height - lastTouch.y) ?
                    self.frame.size.height - firstTouch.y : self.frame.size.height - lastTouch.y;
            vertices[0] = maxX;
            vertices[1] = maxY;
            vertices[2] = minX;
            vertices[3] = maxY;
            vertices[4] = minX;
            vertices[5] = minY;
            vertices[6] = maxX;
            vertices[7] = minY;
            glVertexPointer(2, GL_FLOAT, 0, vertices);
            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
            break;
        }
        case kEllipseShape: {
            if (sprite) {
                [sprite release];
                self.sprite = nil;
            }
            GLfloat vertices[720];
            GLfloat xradius = (firstTouch.x > lastTouch.x) ?
                    (firstTouch.x - lastTouch.x)/2 : (lastTouch.x - firstTouch.x)/2;
            GLfloat yradius = (self.frame.size.height - firstTouch.y > self.frame.size.height - lastTouch.y) ?
                        ((self.frame.size.height - firstTouch.y) - (self.frame.size.height - lastTouch.y))/2 :
                        ((self.frame.size.height - lastTouch.y) - (self.frame.size.height - firstTouch.y))/2;
            for (int i = 0; i <= 720; i+=2) {
                GLfloat xOffset = (firstTouch.x > lastTouch.x) ? lastTouch.x + xradius : firstTouch.x + xradius;
                GLfloat yOffset = (self.frame.size.height - firstTouch.y > self.frame.size.height - lastTouch.y) ?
                        self.frame.size.height - lastTouch.y + yradius :
                        self.frame.size.height - firstTouch.y + yradius;
                vertices[i] = (cos(degreesToRadian(i / 2)) * xradius) + xOffset;
                vertices[i+1] = (sin(degreesToRadian(i / 2)) * yradius) + yOffset;
            }
            glVertexPointer(2, GL_FLOAT, 0, vertices);
            glDrawArrays(GL_TRIANGLE_FAN, 0, 360);
            break;
        }
        case kImageShape:
            if (sprite == nil) {
                self.sprite = [[Texture2D alloc] initWithImage:[UIImage imageNamed:@"iphone.png"]];
                glBindTexture(GL_TEXTURE_2D, sprite.name);
            }
            glEnable(GL_TEXTURE_2D);
            [sprite drawAtPoint:CGPointMake(lastTouch.x, self.frame.size.height - lastTouch.y)];
            break;
        default:
            break;
    }
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}


- (void)dealloc {
    [currentColor release];
    [sprite release];

    [super dealloc];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if (useRandomColor)
        self.currentColor = [UIColor randomColor];
    UITouch *touch = [[event touchesForView:self] anyObject];
    firstTouch = [touch locationInView:self];
    lastTouch = [touch locationInView:self];
    [self draw];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    lastTouch = [touch locationInView:self];
    [self draw];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    lastTouch = [touch locationInView:self];
    [self draw];
}


@end

7703


1)カテゴリUIColor-Random.hのインポート

ドラッグ開始時に応答するtouchesBegan:withEvent:メソッド内で、ランダム色を生成するためにカテゴリUIColor-RandomのrandomColorメソッドが呼び出されるのですが、旧版の本書ではこのカテゴリのインポートが抜けているので追加します。


2)プロパティの実装

ヘッダファイルで宣言した6つのプロパティを実装します。


3)initWithFrame:とdrawRect:メソッドの削除

ビューの初期化はnibファイルからinitWithCoder:で読み込むことになるので、initWithFrame:メソッドは削除してください。

また『iOS OpenGL ESプログラミングガイド』のp.38の『オンデマンドでのレンダリング』の注意書きで、OpenGL ES対応のビューでは、コンテンツのレンダリングにdrawRect:メソッドを実装せず、代わりに独自のメソッドを実装するようにとあるので、(元よりコメントアウトされていますが)drawRect:メソッドも削除しています。


4)initWithCoder:メソッドの追加

nibファイルからのビューの読み込みにinitWithCoder:を追加します。

これは親クラスであるOpenGLES2DViewクラスの同メソッドをオーバーライドしています。

スーパークラスでの初期化の後、プロパティcurrentColorで描画色をredColorで赤色に、プロパティuseRandomColorでランダム色をオフに初期化設定しています。


5)drawメソッドの追加

drawメソッドはOpenGL ES 1.1を利用した図形を描画するメソッドで、図形の描画色の設定と、選択された図形の頂点配列の生成と描画、レンダバッファの表示を行っています。


6)背景色と描画色の設定

最初にglLoadIdentityでモデルビュー行列を初期化した後、glClearColorで色レンダバッファのクリア色を設定し、glClearで色レンダバッファをクリア、つまり背景色のリフレッシュを行っています。

これはOpenGLES2DViewクラスのinitWithCoder:メソッドでの初期化と重複するように見えますが、アプリケーション起動直後の描画領域にタッチする前の状態ではdrawメソッドが呼び出されないため、必要になります。

仮にOpenGLES2DViewクラスのinitWithCode:内のglClearColorとglClearを削除した場合、presentRenderbuffer:によって描画領域が黒で表示されてしまいます。

presentRenderbuffer:も削除すると、nibファイルのビューがそのまま表示されるのですが、drawメソッドが呼び出されてOpenGL ESで背景を書き換えた際に若干色味が異なるので、素直に残しておくのが無難です。

また、drawメソッドの先頭に記述することでドラッグ中やドラッグ終了後の再描画の度に呼び出されることになり、QuartzFunのように再描画領域を指定する手間が省けます。
(つまり毎回画面全体を再描画しています)

それと背景色が旧版の本書では『glClearColor(1.0f, 1.0f, 1.0f, 1.0f)』と白色に設定されていますが、ここではOpenGLES2DViewの設定と同じ『glClearColor(0.78f, 0.78f, 0.78f, 1.0f)』に修正しています。

続いて描画色を設定しますが、現在選択されている色currentColorはUIColorオブジェクトなので、OpenGL ESで扱える形式に変換します。

currentColorからCGColorプロパティでCGColorRef型に変換し、CGColorGetComponents関数でCGFloat型の配列にします。

取得した配列要素を元に、glColorでRGBを割り当てて設定します。

glColor

void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);

void glColor4x(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha);

void glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);

現在の色を設定します。

glColorは、新しい4つの値で現在のRGBA色を設定します。

現在の色の値は、固定小数点または浮動小数点で格納されています。

浮動小数点で格納されている場合、仮数部と指数部のサイズは不定です。

固定小数点も浮動小数点値も、現在の色が更新される以前は、範囲[0, 1]にクランプされていません。

ただし、色要素はカラーバッファに補間あるいは書き込まれる前に、この範囲にクランプされます。

glColor4ubで指定された符号無しbyteの色要素は、0は0.0(ゼロ輝度)に、255は1.0(最大輝度)として、浮動小数点値をリニアにマッピングします。

現在の色の初期値は(1, 1, 1, 1)です。

redgreenbluealpha:現在の色に新しい赤、緑、青、アルファ値を指定します。



参考文献

iOS OpenGL ESプログラミングガイド

OpenGL ES 1.1 Reference Pages

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(9)~OpenGLES2DViewクラス(5)

2011. 12. 22
頂点配列の有効化とピクセル演算)

glEnableClientStateはクライアント側の機能を有効にする関数で、引数のGL_VERTEX_ARRAYは頂点配列を表し、これによって頂点配列への書き込みが有効化され、オブジェクトを頂点配列で記述し描画することができます。

この関数はレンダバッファを表示するpresentRenderbuffer:メソッドの前後で2回呼び出されているのですが、この機能はglDisableClientStateを呼び出さない限り持続すると思われること、initWithCoder:内で頂点配列を利用していないことから、何故2回呼び出しているのかは分かりません。
(実際、どちらかをコメントアウトしても動作に問題はありません)

少なくとも図形描画に頂点配列を利用する以上、1回の呼び出しは必須となります。

glBlendFuncはピクセル演算を指定する関数で、この場合は背景に対し(GLFunViewクラスでの図形描画メソッドで)以降に描画する図形をどのように合成するかを指定しています。

ただし、ここでは合成の方法を指定しているだけで、実際に合成を行いたい場合にはサーバ側の機能をglEnableで引数GL_BLENDを渡して有効にする必要があります。
(GLFunViewの描画メソッドでは有効にしていないので、実際には合成は行われません)

glBlendFuncの第一引数は元となる色、第二引数は対象となる色を指定するのですが、今回のGLFunViewのように何も無い背景に対し図形オブジェクトを1つだけ描画する場合、元の色とは後から描画する図形を、対象となる色はそれ以前に描画されている背景色を指します。

第一引数のGL_ONEは指定された色をそのまま表示することを意味し、第二引数のGL_ONE_MINUS_SRC_ALPHAは対象となる色から元の色で指定されたアルファ値を減算した色を乗ずることを意味します。

つまり元の色(図形の色)を(Sr, Sg, Sb, Sa)、対象の色(背景色)を(Dr, Dg, Db, Da)とすると、合成された表示色は、

(Sr * 1, Sg * 1, Sb * 1, Sa * 1) + (Dr * (1 - Sa), Dg * (1 - Sa), Db * (1 - Sa), Da * (1 - Sa))
= (Sr + Dr * (1 - Sa), Sg + Dg * (1 - Sa), Sb + Db * (1 - Sa), Sa + Da * (1 - Sa))

となります。

この辺は言葉では説明し難いので、実際に試したり下記サイトを参照してください。

WisdomSoft/OpenGL入門/混合処理
OpenGL de プログラミング/ブレンディング::計算式の設定
TOBY SOFT wiki/OpenGL/glBlendFuncについて
有限会社ウエスタンビレッジ/OpenGLプログラム
テン*シー*シー/iPhoneアプリ開発、その(105) 星がいっぱいなの
Pentanium Reactor Blog/アルファブレンディング

glEnableClientState

void glEnableClientState(GLenum array);

void glDisableClientState(GLenum array);

クライアント側の機能を有効または無効にします。

glEnableClientStateとglDisableClientStateはクライアント側の機能を個々に有効または無効にします。

デフォルトでは全てのクライアント側の機能が無効になっています。

glEnableClientStateとglDisableClientStateの両方とも、以下の値のいずれか一つの配列を、単一の引数として取ることができます。

GL_COLOR_ARRAY
有効にした場合、カラー配列は書き込みが有効になり、glDrawArraysまたはglDrawElementsが呼び出された時に、レンダリングの間使用することができます。
glColorPointerを参照してください。

GL_NORMAL_ARRAY
有効にした場合、ノーマル配列は書き込みが有効になり、glDrawArraysまたはglDrawElementsが呼び出された時に、レンダリングの間使用することができます。
glNormalPointerを参照してください。

GL_POINT_SIZE_ARRAY_OES

有効にした場合、ポイントサイズ配列はポイントとポイントスプライトのレンダリングに使用するサイズを制御します。
この場合、glPointSizeによって定義されるポイントサイズは無視されます。
ポイントサイズ配列で供給されるポイントサイズは、ポイントとポイントスプライトの両方のレンダリングに使用されるサイズになります。
glPointSizeを参照してください。

GL_TEXTURE_COORD_ARRAY
有効にした場合、テクスチャ座標配列は書き込みが有効になり、glDrawArraysまたはglDrawElementsが呼び出された時に、レンダリングの間使用することができます。
glTexCoordPointerを参照してください。

GL_VERTEX_ARRAY
有効にした場合、頂点配列は書き込みが有効になり、glDrawArraysまたはglDrawElementsが呼び出された時に、レンダリングの間使用することができます。
glVertexPointerを参照してください。

GL_TEXTURE_COORD_ARRAYの有効および無効は、アクティブなクライアントのテクスチャユニットに影響を与えます。

アクティブなクライアントのテクスチャユニットは、glClientActiveTextureで制御されます。

配列が有効な値でない場合、GL_INVALID_ENUMが発生します。

array:有効または無効にする機能を指定します。
シンボリック定数は、GL_COLOR_ARRAY、GL_NORMAL_ARRAY、GL_POINT_SIZE_ARRAY_OES、GL_TEXTURE_COORD_ARRAY、そしてGL_VERTEX_ARRAYが指定できます。

glBlendFunc

void glBlendFunc(GLenum sfactor, GLenum dfactor); 

ピクセル演算を指定します。

ピクセルは、元となる値とカラーバッファ(の対象となる値)内にある既存の値をブレンドする関数を使用して描画することができます。

ブレンディングを有効/無効にするには、引数GL_BLENDでglEnableglDisableを使用してください。

ブレンディングの初期状態は無効になっています。

glBlendFuncは、有効になっている時のブレンディング操作を定義しています。

sfactorは、元となる色成分の比率を11のメソッドを使用して指定します。

dfactorは、対象となる色成分の比率を10のメソッドを使用して指定します。

11の有効なメソッドは以下に表で表します。

各メソッドは、赤、緑、青、そしてアルファの4つの比率要素で定義されています。

表とその後の方程式において、元と対象の色成分は(RsGsBsAs)と(RdGdBdAd)として表されています。

これらは0から(kRkGkBkA)間の整数値を持つとすると、

kc = 2mc - 1

と表され、(mRmGmBmA)は、赤、緑、青、そしてアルファのビットプレーン数を示します。

元と対象の比率要素は、(sRsGsBsA)と(dRdGdBdA)として参照されます。

比率要素は表では(fRfGfBfA)と示しており、元または対象の要素を表します。

全ての比率要素の範囲は[0, 1]です。

Parameter (fRfGfBfA)
GL_ZERO (0, 0, 0, 0)
GL_ONE (1, 1, 1, 1)
GL_SRC_COLOR (R/ kRG/ kGB/ kBA/ kA)
GL_ONE_MINUS_SRC_COLOR (1, 1, 1, 1) - (R/ kRG/ kGB/ kBA/ kA)
GL_DST_COLOR (R/ kRG/ kGB/ kBA/ kA)
GL_ONE_MINUS_DST_COLOR (1, 1, 1, 1) - (R/ kRG/ kGB/ kBA/ kA)
GL_SRC_ALPHA (A/ kAA/ kAA/ kAA/ kA)
GL_ONE_MINUS_SRC_ALPHA (1, 1, 1, 1) - (A/ kAA/ kAA/ kAA/ kA)
GL_DST_ALPHA (A/ kAA/ kAA/ kAA/ kA)
GL_ONE_MINUS_DST_ALPHA (1, 1, 1, 1) - (A/ kAA/ kAA/ kAA/ kA)
GL_SRC_ALPHA_SATURATE (iii, 1)

表において、

i = min(AskA - Ad) / kA

ピクセルのブレンド値の決定に、システムは以下の方程式を使用します。

Rd = min(kRRssR + RddR)

Gd = min(kGGssG + GddG)

Bd = min(kBBssB + BddB)

Ad = min(kAAssA + AddA)

ブレンディングは不正確な整数のカラー値で計算するため、上記の方程式の精密な見た目にも関わらず、ブレンディングの計算は正確ではありません。

しかし1に等しくなる必要があるブレンド要素は、その被乗数を変更しないことが保証されており、0に等しいブレンド要素は被乗数を0へ減少させます。

例えばsfactorがGL_SRC_ALPHAで、dfactorがGL_ONE_MINUS_SRC_ALPHA、AskAが等しい場合、方程式は単純なものに置換できます。

Rd = Rs

Gd = Gs

Bd = Bs

Ad = As

全てのピクセル書き込み操作上でのglBlendFuncの操作は、ポイント、ライン、そしてポリゴンの走査変換を含みます。

glBlendFuncはglClearの影響を受けません。

一例として、透明度は最奥から最前にソートされたプリミティブを、glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)を使用して、最も頻繁に実装されます。

この透明度の計算は、カラーバッファのアルファビットプレーンの存在を必要としないことに注意してください。

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)はまた、アンチエイリアシングをしたポイントとラインのレンダリングに便利です。

sfactorまたはdfactorが有効な値でない場合、GL_INVALID_ENUMが発生します。

sfactor:元となる赤、緑、青、そしてアルファのブレンディング要素をどのように計算するかを指定します。
GL_ZERO、GL_ONE、GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR、GL_SRC_ALPHA、GL_ONE_MINUS_SRC_ALPHA、GL_DST_ALPHA、GL_ONE_MINUS_DST_ALPHA、そしてGL_SRC_ALPHA_SATURATEのシンボリック定数を指定できます。
初期値はGL_ONEです。

dfactor:対象となる赤、緑、青、そしてアルファのブレンディング要素をどのように計算するかを指定します。
GL_ZERO、GL_ONE、GL_SRC_COLOR、GL_ONE_MINUS_SRC_COLOR、GL_SRC_ALPHA、GL_ONE_MINUS_SRC_ALPHA、GL_DST_ALPHA、そしてGL_ONE_MINUS_DST_ALPHAの8つのシンボリック定数を指定できます。
初期値はGL_ZEROです。



参考文献

iOS OpenGL ESプログラミングガイド

OpenGL ES 1.1 Reference Pages

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(8)~OpenGLES2DViewクラス(4)

2011. 12. 21
ビューポート変換、投影変換、モデルビュー変換)

続いて、2Dグラフィックス用の3つの座標変換(ビューポート変換と投影変換、そしてモデルビュー変換)を設定します。
(『GLFun(4)~OpenGL ESへの対応と作業工程』参照)

最初にglBindFramebufferでフレームバッファをバインドします。

次にOpenGL ESのコンテンツを出力画面のどの領域に描画するかをglViewportで設定します。

第一、第二引数はビューポート矩形の原点となる左下角の座標をx、yで指定し、第三、第四引数はビューポートの幅と高さを指定します。

一般的にOpenGL ESコンテンツはビューの全画面で表示することが多いので、原点座標は(0, 0)、サイズは前述のcreateFramebufferメソッドで取得したレンダバッファの幅と高さを指定します。

投影変換の設定は、まずglMatrixModeに引数GL_PROJECTIONを渡して以降の行列操作を投影変換用にし、glLoadIdentityで初期化(単位行列にリセット)した後、glOrthoで正射影行列を適用します。

glOrthoの6つの引数は、ワールド座標系におけるビューボリュームの座標を示すもので、第一引数からleft、right、bottom、top、near、farを指定します。

glViewportではint値を使用するためglGetRenderbufferParameterivでレンダバッファから取得したint値を利用していましたが、glOrthofはfloat値で指定するため、ビューのframeプロパティでCGRect構造体内にあるCGFloat型のwidthとheightを使用していると思われます。

続いてglMatrixModeに引数GL_MODELVIEWを渡し、以降の行列操作をモデルビュー変換用にしますが、実際にオブジェクトを生成・配置する部分はOpenGLES2DViewクラスでは行っていなく、継承するクラス(GLFunプロジェクトではGLFunViewクラス)が担うことになります。

glViewport

void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);

ビューポートを設定します。

glViewportは、正規化デバイス座標系からウィンドウ座標系へと、xyのアフィン変換を指定します。

(xndynd)を正規化デバイス座標系とすると、ウィンドウ座標系(xwyw)は以下のように計算されます。

xw = (xnd + 1) * (width / 2) + x

yw = (ynd + 1) * (height / 2) + y

ビューポートの幅と高さは、実装に依存する範囲に暗黙的に固定されます。

この範囲の照会には、引数GL_MAX_VIEWPORT_DIMSでglGetを呼び出してください。

widthまたはheightが負の場合、GL_INVALID_VALUEが発生します。

xy:ビューポート矩形の左下角をピクセル単位で指定します。
初期値は(0, 0)です。

widthheight:ビューポートの幅と高さを指定します。
GLコンテキストが最初にサーフェス(例えばウィンドウ)にアタッチされている場合、widthheightはそのサーフェスの寸法に設定されます。

glMatrixMode

void glMatrixMode(GLenum mode);

現在の行列となる行列を指定します。

glMatrixModeは現在の行列モードを設定します。

modeは以下の値の内の一つを採ることができます。

GL_MODELVIEW
モデルビュー行列スタックに、後続の行列操作を適用します。

GL_PROJECTION
投影行列スタックに、後続の行列操作を適用します。

GL_TEXTURE
テクスチャ行列スタックに、後続の行列操作を適用します。

modeが有効な値でない場合、GL_INVALID_ENUMが発生します。

mode:行列スタックにその後の行列操作のためのターゲットを指定します。
GL_MODELVIEW、GL_PROJECTION、そしてGL_TEXTUREという値が使用できます。
初期値はGL_MODELVIEWです。

glLoadIdentity

void glLoadIdentity(void);

現在の行列と単位行列を置換します。

glLoadIdentityは、現在の行列と単位行列を置換します。

これは単位行列でglLoadMatrixを呼び出したのと同義ですが、場合によってはより効率的です。

7541

glOrtho

void glOrthof(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far);

void glOrthox(GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed near, GLfixed far);

正射影行列を現在の行列に乗じます。

glOrtho正射影(平行投影)を生成する変換を記述します。

これはglMultMatrixを以下の行列を引数として呼び出したものと同様に、現在の行列(glMatrixMode参照)はこの行列によって乗じられ、その結果が現在の行列に置換されます。

7542

7543

通常、行列モードはGL_PROJECTIONで、視点が(0, 0, 0)に配置されていると仮定して、ウィンドウの左下と右上角にマッピングされるように、手前のクリッピング面上の点にそれぞれ(leftbottom-near)と(righttop-near)を指定します。

奥のクリッピング面の位置は、-farを指定します。

nearfarの双方とも、正や負になる場合があります。

現在の行列スタックの保存と復元には、glPushMatrixとglPopMatrixを使用してください。

left = right、またはbottom = top、あるいはnear = farの場合はGL_INVALID_VALUEが発生します。

leftright:垂直クリッピング面の左右の座標を指定します。

bottomtop:水平クリッピング面の上下の座標を指定します。

nearfar:深度クリッピング面の手前と奥の距離を指定します。
クリッピング面がビューアより後方になる場合は、値が負になります。


背景色の設定と適用)

色レンダバッファのクリア色を指定し、背景として適用します。

クリアする色の設定はglClearColorで指定し、第一引数からred、green、blue、alphaとなっています。

その後glClearに引数GL_COLOR_BUFFER_BITを渡して呼び出すことで、色レンダバッファを指定色でクリアすることができます。

実際にはglBindRenderbufferでレンダバッファをバインドし、コンテキストに対しpresentRenderbuffer:メソッドで色レンダバッファを指定することで適用されます。

glClearColor

void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

void glClearColorx(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha);

カラーバッファのクリア値を指定します。

glClearColorは、glClearでカラーバッファをクリアする際に使用する、赤、緑、青、そしてアルファ値を指定します。

glClearColorが指定できる値は、[0, 1]の範囲に限定されています。

redgreenbluealpha:カラーバッファをクリアする時に使用する、赤、緑、青、そしてアルファ値を指定します。
初期値は全て0です。

glClear

void glClear(GLbitfield mask);

プリセット値にバッファをクリアします。

glClearは、glClearColor、glClearDepthf、そしてglClearStencilによって予め選択された値に、ウィンドウのビットプレーン空間を設定します。

ピクセル所有権テスト、シザーテスト、ディザリング、そしてバッファのホワイトマスクは、glClearの操作に影響を与えます。

シザーボックスはクリアされた領域を制限します。

アルファ関数、ブレンド関数、論理操作、ステンシル、テクスチャマッピング、そして深度バッファリングは、glClearによって無視されます。

glClearは、バッファをクリアすることを示すいくつかの値から、ORのビットマスクを1つ引数として選択します。

ビットマスクの値は以下の通りです。

GL_COLOR_BUFFER_BIT
カラーバッファを示します。

GL_DEPTH_BUFFER_BIT
深度バッファを示します。

GL_STENCIL_BUFFER_BIT
ステンシルバッファを示します。

各バッファがクリアされた時の値は、そのバッファのクリア値の設定に依存します。

バッファが存在しない場合、glClearはバッファに対して何も影響を与えません。

定義済の3つのビットマスク以外のビットマスクがmaskに設定された場合、GL_INVALID_VALUEが発生します。

mask:バッファをクリアすることを示す、ORのビットマスクを指定します。
有効なマスクはGL_COLOR_BUFFER_BIT、GL_DEPTH_BUFFER_BIT、そしてGL_STENCIL_BUFFER_BITです。

presentRenderbuffer:

- (BOOL)presentRenderbuffer:(NSUInteger)target

画面上にレンダバッファのコンテンツを表示します。

戻り値は成功した場合はYES、そうでない場合はNOになります。

レンダバッファを表示するには、renderbufferStorage:fromDrawable:メソッドを使用してストレージを割り当てておく必要があります。

厳密な意味で、いつ、どのようにレンダバッファのコンテンツを表示するかは、描画可能オブジェクトによって制御されます。

重要:レンダバッファのコンテンツは、レンダバッファが画面に表示された後で変更される場合があります。
レンダバッファを表示した後、アプリケーションは再度表示する前にレンダバッファのコンテンツを完全に再描画する必要があります。
レンダバッファのコンテンツを保持するには、drawableProperties辞書のkEAGLDrawablePropertyRetainedBackingキーをYESに設定することができます。
キーをYESに設定すると、グラフィックスのパフォーマンスが低下し、使用メモリが増加するため、レンダバッファのコンテンツを変えずに保持する時のみに行ってください。

target:現在バインドされているレンダバッファ用の、OpenGL ESのバインディングポイントを指定します。
OpenGL ES 1.0 APIを使用するコンテキストの場合は、GL_RENDERBUFFER_OESにする必要があります。
OpenGL ES 2.0 APIを使用するコンテキストの場合は、OES接尾辞を削除する必要があります。



参考文献

iOS OpenGL ESプログラミングガイド

UIView Class Reference

EAGLContext Class Reference

OpenGL ES 1.1 Reference Pages

OpenGL ES 2.0 Reference Pages

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(7)~OpenGLES2DViewクラス(3)

2011. 12. 20
色レンダバッファの高さと幅を取得)

レンダバッファからビューの高さと幅を取得し、後述するinitWithCoder:でビューポートのサイズ設定に使用します。

レンダバッファのパラメータの取得にはglGetRenderbufferParameterivを使用します。

第一引数のターゲットは必ずGL_RENDERBUFFERになります。

第二引数にはレンダバッファから取得する情報を示す定数で、幅はGL_RENDERBUFFER_WIDTH、高さはGL_RENDERBUFFER_HEIGHTを指定します。

第三引数には取得した情報を格納する変数を指定します。

glGetRenderbufferParameteriv

void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint * params);

レンダバッファオブジェクトのパラメータを返します。

glGetRenderbufferParameterivは、現在バインドされているレンダバッファオブジェクトのparamsで選択されたパラメータを返します。

pname名はレンダバッファオブジェクトのパラメータを指定するもので以下のようになっています。

GL_RENDERBUFFER_WIDTH
paramsは現在バインドされているレンダバッファの画像の幅をピクセル単位で返します。
初期値は0です。

GL_RENDERBUFFER_HEIGHT
paramsは現在バインドされているレンダバッファの画像の高さをピクセル単位で返します。
初期値は0です。

GL_RENDERBUFFER_INTERNAL_FORMAT
paramsは現在バインドされているレンダバッファの画像の内部フォーマットを返します。
初期値はGL_RGBA4です。

GL_RENDERBUFFER_RED_SIZE
paramsは現在バインドされているレンダバッファの画像の赤成分のビットの解像度を返します。
初期値は0です。

GL_RENDERBUFFER_GREEN_SIZE
paramsは現在バインドされているレンダバッファの画像の緑成分のビットの解像度を返します。
初期値は0です。

GL_RENDERBUFFER_BLUE_SIZE
paramsは現在バインドされているレンダバッファの画像の青成分のビットの解像度を返します。
初期値は0です。

GL_RENDERBUFFER_ALPHA_SIZE

paramsは現在バインドされているレンダバッファの画像のアルファ成分のビットの解像度を返します。
初期値は0です。

GL_RENDERBUFFER_DEPTH_SIZE
paramsは現在バインドされているレンダバッファの画像の深度成分のビットの解像度を返します。
初期値は0です。

GL_RENDERBUFFER_STENCIL_SIZE
paramsは現在バインドされているレンダバッファの画像のステンシル成分のビットの解像度を返します。
初期値は0です。

glGetRenderbufferParameterivによって報告される成分の解像度は、格納されている成分の実際の解像度で、glRenderbufferStorageのinternalformatパラメータが要求するものと異なる場合があります。

エラーが発生した場合、paramsの内容への変更は行われません。

targetがGL_RENDERBUFFERでない場合、GL_INVALID_ENUMが発生します。

pnameがGL_RENDERBUFFER_WIDTH、GL_RENDERBUFFER_HEIGHT、GL_RENDERBUFFER_INTERNAL_FORMAT、GL_RENDERBUFFER_RED_SIZE、GL_RENDERBUFFER_GREEN_SIZE、GL_RENDERBUFFER_BLUE_SIZE、GL_RENDERBUFFER_ALPHA_SIZE、GL_RENDERBUFFER_DEPTH_SIZE、またはGL_RENDERBUFFER_STENCIL_SIZEでない場合、GL_INVALID_ENUMが発生します。

予約済のレンダバッファオブジェクト名0がバインドされていた場合、GL_INVALID_OPERATIONが発生します。

target:ターゲットのレンダバッファオブジェクトを指定します。
シンボリック定数はGL_RENDERBUFFERである必要があります。

pname:レンダバッファオブジェクトのパラメータのシンボリック名を指定します。
指定可能な値は、GL_RENDERBUFFER_WIDTH、GL_RENDERBUFFER_HEIGHT、GL_RENDERBUFFER_INTERNAL_FORMAT、GL_RENDERBUFFER_RED_SIZE、GL_RENDERBUFFER_GREEN_SIZE、GL_RENDERBUFFER_BLUE_SIZE、GL_RENDERBUFFER_ALPHA_SIZE、GL_RENDERBUFFER_DEPTH_SIZE、GL_RENDERBUFFER_STENCIL_SIZEです。

params:要求されたパラメータを返します。


フレームバッファオブジェクトの完全性のテスト)

生成・バインドされたフレームバッファオブジェクトに問題が無いかどうかを確認するため、完全性のテストを行います。

完全性のテストはglCheckFramebufferStatusで行い、引数には必ずGL_FRAMEBUFFERを指定します。

問題が無い場合はGL_FRAMEBUFFER_COMPLETEが返され、問題が発生した場合はその問題に応じた定数が返され、NSLogに出力されます。

glCheckFramebufferStatus

GLenum glCheckFramebufferStatus(GLenum target);

フレームバッファオブジェクトのフレームバッファ完全性ステータスを返します。

glCheckFramebufferStatusは、現在バインドされているフレームバッファが完全であるかどうかを識別するシンボリック定数を返し、そうでない場合はフレームバッファの完全性の規則に違反していることになります。

フレームバッファが完全である場合は、GL_FRAMEBUFFER_COMPLETEが返されます。

フレームバッファが完全でない場合は、以下の値が返されます。

GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
全てのフレームバッファのアタッチメントポイントが、フレームバッファのアタッチメントとして完全ではありません。
これは、アタッチされたレンダバッファやテクスチャの少なくとも一つのアタッチメントポイントで、アタッチされたオブジェクトがもはや存在していない、あるいはゼロの幅または高さのアタッチされた画像を持つ、あるいはカラーアタッチメントポイントがカラーレンダリング不可能な画像にアタッチされている、あるいは深度アタッチメントポイントが深度レンダリング不可能な画像にアタッチされている、あるいはステンシルアタッチメントポイントがステンシルレンダリング不可能な画像にアタッチされていることを意味します。
カラーレンダリング可能なフォーマットには、GL_RGBA4、GL_RGB5_A1、そしてGL_RGB565が含まれています。
深度レンダリング可能なフォーマットはGL_DEPTH_COMPONENT16のみです。
ステンシルレンダリング可能なフォーマットはGL_STENCIL_INDEX8のみです。

GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
全てのアタッチされた画像が、同じ幅と高さを持っていません。

GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
フレームバッファにアタッチされた画像がありません。

GL_FRAMEBUFFER_UNSUPPORTED
アタッチされた画像の内部フォーマットの組み合わせが、制約の実装依存の設定に違反しています。

現在バインドされているフレームバッファが完全なフレームバッファでない場合、読み書きにフレームバッファを使用しようと試みるとエラーになります。

これはフレームバッファが完全なフレームバッファでない時に呼び出された場合に、レンダリングコマンド(glDrawArraysとglDrawElements)がフレームバッファの読み込みコマンド(glReadPixels、glCopyTexImage2D、そしてglCopyTexSubImage2D)と同様に、GL_INVALID_FRAMEBUFFER_OPERATIONエラーを発生させることを意味します。

必須ではありませんが、アプリケーションがglCheckFramebufferStatusを呼び出して、レンダリングする前にフレームバッファが完全かどうかを確認することを強く推奨します。

なぜなら、いくつかの実装は内部フォーマットの特定の組み合わせへのレンダリングがサポートされていない可能性があるためです。

この場合、GL_FRAMEBUFFER_UNSUPPORTEDが返されます。

デフォルトのウィンドウシステムが提供するフレームバッファは常に完全なフレームバッファであるため、GL_FRAMEBUFFER_BINDINGが0の時にGL_FRAMEBUFFER_COMPLETEが返されます。

エラーが発生した場合、paramsの内容は変更されません。

targetがGL_FRAMEBUFFERでない場合、GL_INVALID_ENUMが発生します。

target:ターゲットのフレームバッファオブジェクトを指定します。
シンボリック定数はGL_FRAMEBUFFERを指定する必要があります。

createFramebufferメソッドの戻り値)

createFramebufferメソッドは、ビューの初期化を行うinitWithCoder:メソッドから呼び出され、フレームバッファが正常に生成されたかどうかをブール値で返します。

initWithCoder:側では生成の可否を受けて、ビューの初期化を中断するかどうかを判別します。


3)initWithCoder:メソッド

initWithCoder:メソッドでは、nibファイル(GLFunプロジェクトではGLFunViewController.xibファイル)からビューのインスタンスを読み込み、追加の初期化を行います。

まず、スーパークラスによる初期化後、ビューの(CAEAGLLayer)レイヤを読み込んでプロパティの設定を行います。

そしてOpenGL ES 1.1のレンダリングコンテキストを生成し、現在のコンテキストに設定、createFramebufferメソッドを呼び出してフレームバッファと色レンダバッファの生成を行います。

その後、ビューポート変換、投影変換、モデルビュー変換の設定を行い、背景色を設定して表示します。
(『iOS OpenGL ESプログラミングガイド』のp.35『Core Animationレイヤへのレンダリング』の『4』を参照)


レイヤの読み込みと初期化)

OpenGL ESコンテンツを表示するCAEAGLLayerクラスのレイヤを読み込み、プロパティの設定を行います。

最初にlayerプロパティでレイヤを取得し、キャストしてCAEAGLLayerオブジェクトのインスタンスを作ります。

パフォーマンスを最適化するため、opaqueプロパティをYESにしてレイヤを不透明にします。

それとdrawablePropertiesプロパティでレンダリングサーフェスのサーフェスプロパティ(レンダリング時におけるコンテンツの保持とカラーフォーマット)を設定します。

drawablePropertiesは辞書形式なので、dictionaryWithObjectsAndKeys:メソッドでキーと値を設定します。

キーには表示後にコンテンツを保持するかを示すkEAGLDrawablePropertyRetainedBackingと、カラーフォーマットを示すkEAGLDrawablePropertyColorFormatの2つがあり、コンテンツは非保持、カラーフォーマットはkEAGLColorFormatRGB565(16ビットRGBフォーマット)に指定しています。

opaque

@property(getter=isOpaque) BOOL opaque

このプロパティは、レイヤが生成するコンテンツに直接影響を与えます。

デフォルトはNOです。

このプロパティは、開発者によって提供された画像に対して直接効果を与えるものではないことに注意してください。

このプロパティは、setNeedsDisplayが呼び出された時にのみ発動して影響を与えるもので、その後displayはdrawInContext:メソッドが描画するためのビットマップを生成します。

その場合、生成されたビットマップがアルファチャンネルを持っているかどうかは、このプロパティの値によって定義されます。

この値はcontentsプロパティでデベロッパによって提供された画像に対して、直接効果は与えません。

drawableProperties

@property(copy) NSDictionary *drawableProperties

描画可能なサーフェスの所望する特性を指定する、値の辞書です。(必須)

drawablePropertiesの辞書は、OpenGL ESのレンダバッファにアタッチされている時に、このオブジェクトによって使用されるプロパティを指定します。

アプリケーションは、EAGLContextメソッドのrenderbufferStorage:fromDrawable:にこのオブジェクトを渡す前に、これらのプロパティを設定する必要があります。

drawablePropertiesの辞書を変更する場合、アプリケーションは新しい値を有効にするために、再度renderbufferStorage:fromDrawable:を呼び出す必要があります。

・描画可能プロパティのキー

EAGL_EXTERN NSString * const kEAGLDrawablePropertyColorFormat;
EAGL_EXTERN NSString * const kEAGLDrawablePropertyRetainedBacking;

drawablePropertiesの辞書内で指定するキーです。

kEAGLDrawablePropertyColorFormat
描画可能サーフェスのための内部カラーバッファフォーマットを指定するキーです。
このキーの値は、特定のカラーバッファフォーマットを指定するNSStringオブジェクトです。
このカラーバッファフォーマットは、レンダバッファ用のストレージの生成に、EAGLContextオブジェクトによって使用されます。
デフォルト値はkEAGLColorFormatRGBA8です。

kEAGLDrawablePropertyRetainedBacking
描画可能サーフェスを表示した後にコンテンツを保持するかを指定するキーです。
このキーの値は、BOOLデータ型を含むNSNumberオブジェクトです。
NOの場合は、コンテンツが表示された後と同じコンテンツであることに依存しません。
YESの場合は、表示後にコンテンツが変更されません。
値をYESに設定した場合、パフォーマンスの低下と追加のメモリを使用する結果を招くため、必要な時のみコンテンツを無変更にすることを推奨します。
デフォルト値はNOです。

・カラーフォーマット

EAGL_EXTERN NSString * const kEAGLColorFormatRGB565;
EAGL_EXTERN NSString * const kEAGLColorFormatRGBA8;

kEAGLColorFormatRGB565
OpenGL ESのGL_RGB565フォーマットに対応する、16ビットRGBフォーマットを指定します。

kEAGLColorFormatRGBA8
OpenGL ESのGL_RGBA8888フォーマットに対応する、32ビットRGBAフォーマットを指定します。

コンテキストの生成と現在のコンテキストへの設定)

レンダリングコンテキストの生成にはinitWithAPI:メソッドを使用します。

引数にはOpenGL ESのバージョンを示す定数を指定しますが、今回の場合はkEAGLRenderingAPIOpenGLES1を指定しているため、このコンテキストはOpenGL ES 1.1に対応することになります。

コンテキストの生成後、コンテキストが生成されたかどうか、そのコンテキストをsetCurrentContext:メソッドで現在のコンテキストに設定できたかどうか、そして前述のcreateFramebufferメソッドでフレームバッファが生成されたかどうかの3つの条件の論理和を取っています。

従って、いずれかの結果が失敗してnilが返された場合はビューを解放してnilを返すことになり、3つの条件全てが成功した場合のみ以降の処理が進むことになります。

initWithAPI:

- (id)initWithAPI:(EAGLRenderingAPI)api

指定したOpenGL ESのレンダリングAPIのバージョンで、新規に割り当てたレンダリングコンテキストを初期化して返します。

戻り値は初期化されたコンテキストオブジェクト、あるいはオブジェクトが生成できなかった場合はnilを返します。

このコンテキストにOpenGL ESのコマンドを発行するには、setCurrentContext:を呼び出すことによって、現在の描画コンテキストを最初に作成する必要があります。

initWithAPI:を呼び出すと新規にEAGLSharegroupオブジェクトを生成し、このコンテキストにアタッチします。

api:目的とするOpenGL ESのレンダリングAPIのバージョンを指定します。
有効な値については『OpenGL ESのバージョン』を参照してください。

OpenGL ESのバージョン

typedef NSUInteger EAGLRenderingAPI;
enum
{
    kEAGLRenderingAPIOpenGLES1 = 1
    kEAGLRenderingAPIOpenGLES2 = 2
};

これらの定数はレンダリングコンテキストが提供するOpenGL ESのバージョンの選択に使用します。

kEAGLRenderingAPIOpenGLES1
コンテキストはOpenGL ES 1.xレンダリングAPIをサポートします。

kEAGLRenderingAPIOpenGLES2
コンテキストはOpenGL ES 2.xレンダリングAPIをサポートします。

setCurrentContext:

+ (BOOL)setCurrentContext:(EAGLContext *)context

指定したコンテキストを呼び出したスレッドの現在のレンダリングコンテキストとして作成します。

戻り値は成功した場合はYES、それ以外の場合はNOを返します。

エラーが発生した場合、現在のスレッドのレンダリングコンテキストは変更されません。

全てのOpenGL ESの呼び出しは、特記の無い限り、現在のコンテキストに完全に呼び出した順を考慮して発行されます。

EAGLは現在の作成されたコンテキストを保持し、以前のコンテキストは解放します。

nilパラメータでこのメソッドを呼び出すと現在のコンテキストは解放し、全ての描画コンテキストにOpenGL ESのアンバインドをさせます。

複数のスレッド上に現在の同じコンテキストを作成することは避けてください。

OpenGL ESはスレッドの安全性を提供しませんが、複数のスレッド上で同じコンテキストを使用する場合は、複数のスレッドから同じコンテキストへの同時アクセスを防ぐために、スレッドを同期させるいくつかの技術を採用する必要があります。

context:現在作成しようとしているレンダリングコンテキストを指定します。



参考文献

iOS OpenGL ESプログラミングガイド

UIView Class Reference

CALayer Class Reference

EAGLDrawable Protocol Reference

EAGLContext Class Reference

OpenGL ES 2.0 Reference Pages

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(6)~OpenGLES2DViewクラス(2)

2011. 12. 17
2)createFramebufferメソッド

createFramebufferメソッドは、フレームバッファオブジェクトとレンダバッファの生成とコンテキストへのバインド、フレームバッファオブジェクトの完全性のテストを行っています。
(『iOS OpenGL ESプログラミングガイド』のp.34『オフスクリーンフレームバッファオブジェクトの作成』と、p.35『Core Animationレイヤへのレンダリング』の『7』と『8』を参照)

ここで使用されているOpenGL ESの関数には全て『OES』という接尾辞が付いています。
OESは『OpenGL extension specification』の略でKhronosによって拡張された機能を意味します。
(『OpenGL ESを使用したiOS用3Dグラフィックス(1)』参照)

OpenGL ES 1.1の拡張機能は『OpenGL ES 1.1 Reference Pages』に載っていないため、以降の関数の説明では接尾辞の無い(『OpenGL ES 2.0 Reference Pages』に載っている)OpenGL ES 2.0の関数で説明します。

したがって、文中では関数や定数の説明の際に接尾辞OESを省略することになりますので、留意してください。


フレームバッファの作成とバインド)

フレームバッファを生成し、レンダリングコンテキストに接続します。

最初にglGenFramebuffersでフレームバッファの生成を行います。

第一引数は要求する個数を、第二引数はフレームバッファを識別する名前を指定します。
(『テン*シー*シー/iPhoneアプリ開発、その(100) ようやく終息OpenGL ESセットアップ』参照)

(実装しているコードでは次の行ですが)その後、後述するinitWithCoder:メソッドで初期化するレンダリングコンテキストに対してglBindFramebufferで名付けたフレームバッファをバインドします。

第一引数のターゲットは必ずGL_FRAMEBUFFERであり、第二引数は直前に生成したフレームバッファの識別名を指定します。

glGenFramebuffers

void glGenFramebuffers(GLsizei n, GLuint * framebuffers);

フレームバッファオブジェクト名を生成します。

glGenFramebuffersは、framebuffersで名付けたn個のフレームバッファオブジェクトを返します。

名前の形式は連続した整数のセットを保証するものではありませんが、返される名前のどれもglGenFramebuffersの呼び出しの直前に使用されていなかったことを保証します。

glGenFramebuffersの呼び出しによって返されるフレームバッファオブジェクト名は、glDeleteFramebuffersで最初に削除されない限り、後続の呼び出しによって返されません。

glBindFramebufferを呼び出すことによって最初にバインドされるまで、返されたフレームバッファオブジェクト名はフレームバッファオブジェクトに関連付けられません。

nが負の場合、GL_INVALID_VALUEが発生します。

n:生成されるフレームバッファオブジェクト名の数を指定します。

framebuffers:生成されたフレームバッファオブジェクト名が格納される配列を指定します。

glBindFramebuffer

void glBindFramebuffer(GLenum target, GLuint framebuffer);

名付けられたフレームバッファオブジェクトをバインドします。

glBindFramebufferは名付けられたフレームバッファオブジェクトを生成または使用することができます。

targetにGL_FRAMEBUFFERを、framebufferに新しいフレームバッファオブジェクトの名前を設定してglBindFramebufferを呼び出すと、フレームバッファオブジェクト名にバインドします。

フレームバッファオブジェクトがバインドされている時、以前のバインドは自動的に破棄されます。

フレームバッファオブジェクト名は符号無し整数です。

値ゼロは、ウィンドウシステムによって提供されるデフォルトのフレームバッファを表すために予約されています。

フレームバッファオブジェクト名と対応するフレームバッファオブジェクトのコンテンツは、現在のGLレンダリングコンテキストの共有オブジェクト空間に対してローカルです。

新しいフレームバッファオブジェクト名の設定には、glGenFramebuffersを使用することができます。

最初にバインドした直後のフレームバッファオブジェクトの状態には、オブジェクト型としてそれぞれGL_NONEの3つのアタッチメントポイント(GL_COLOR_ATTACHMENT0、GL_DEPTH_ATTACHMENT、そしてGL_STENCIL_ATTACHMENT)があります。

非ゼロのフレームバッファオブジェクト名がバインドされている間、ターゲットGL_FRAMEBUFFER上のGL操作はバインドされているフレームバッファオブジェクトに影響を及ぼし、ターゲットGL_FRAMEBUFFERまたはフレームバッファのクエリは、バインドされたフレームバッファオブジェクトからGL_DEPTH_BITSなどの状態を返します。

ゼロのフレームバッファオブジェクト名がバインドされている間は、初期状態におけるように、ターゲットGL_FRAMEBUFFER上で状態を変更または照会を試みると、GL_INVALID_OPERATIONエラーが発生します。

非ゼロのフレームバッファオブジェクト名がバインドされている間、全ての(glDrawArraysやglDrawElementsによる)フレームバッファへのレンダリングと、(glReadPixels、glCopyTexImage2D、またはglCopyTexSubImage2Dによる)フレームバッファからの読み込みには、デフォルトのウィンドウシステムが提供したフレームバッファではなく、アプリケーションが生成したフレームバッファオブジェクトにアタッチされた画像を使用してください。

アプリケーションで生成されるフレームバッファオブジェクト(即ち非ゼロの名前)は、デフォルトのウィンドウシステムが提供するフレームバッファとはいくつか異なる点が有ります。

第一に、フレームバッファでアタッチ可能な画像をアタッチやデタッチするため、カラーバッファや深度バッファ、ステンシルバッファに対して変更可能なアタッチメントポイントを持っています。

第二に、アタッチされた画像のサイズやフォーマットは完全にGLの制御されており、ピクセルフォーマットの選択やウィンドウのリサイズ、ディスプレイモードの変更といったウィンドウシステムのイベントによる影響を受けません。

第三に、アプリケーションが生成したフレームバッファオブジェクトへのレンダリング、あるいはオブジェクトからの読み込み時は、ピクセル所有権テストは常に成功します(即ち、オブジェクトが全てのピクセルを所有しています)。

第四に、フレームバッファオブジェクトは不可視のカラーバッファビットプレーンで、唯一の『オフスクリーン』カラーイメージアタッチメントであり、フロント/バックバッファやスワッピングの概念はありません。

最後に、フレームバッファオブジェクトにはマルチサンプルバッファが無く、実装依存の状態変数GL_SAMPLESやGL_SAMPLE_BUFFERSは、アプリケーションが生成するフレームバッファオブジェクトの場合には両方ともゼロになります。

glBindFramebufferで生成されたフレームバッファオブジェクトのバインディングは、異なるフレームバッファオブジェクト名がバインドされるまで、あるいはglDeleteFramebuffersでフレームバッファオブジェクトが削除されるまで、アクティブが持続します。

実装依存のピクセルの深さと関連する状態のクエリは、現在バインドされているフレームバッファオブジェクトから派生しています。

これらには、GL_RED_BITS、GL_GREEN_BITS、GL_BLUE_BITS、GL_ALPHA_BITS、GL_DEPTH_BITS、GL_STENCIL_BITS、GL_IMPLEMENTATION_COLOR_READ_TYPE、GL_IMPLEMENTATION_COLOR_READ_FORMAT、GL_SAMPLES、そしてGL_SAMPLE_BUFFERSが含まれています。

targetがGL_FRAMEBUFFERでない場合は、GL_INVALID_ENUMが発生します。

target:フレームバッファオブジェクトをバインドするターゲットを指定します。
シンボリック定数はGL_FRAMEBUFFERである必要があります。

framebuffer:フレームバッファオブジェクトの名前を指定します。


色レンダバッファの作成とストレージの割り当て、そしてフレームバッファの色アタッチメントポイントへのストレージのアタッチ)

レンダバッファを生成してレンダリングコンテキストに接続し、レンダバッファの内容を保持するストレージを割り当て、その内容をフレームバッファの色アタッチメントポイントに接続します。

まず、glGenRenderbuffersでレンダバッファの生成を行います。

フレームバッファと同様に第一引数は要求する個数を、第二引数はレンダバッファの識別名を指定します。

レンダリングコンテキストへのバインドはglBindRenderbufferで行います。

こちらもフレームバッファと同様で、第一引数のターゲットは必ずGL_RENDERBUFFERであり、第二引数には生成したレンダバッファの識別名を指定します。

レンダバッファへのストレージの割り当ては、オフスクリーンへのレンダリングの場合はglRenderbufferStorageを使用しますが、Core Animationレイヤ(CAEAGLLayer)へレンダリングする場合はrenderbufferStorage:fromDrawable:メソッドを使用します。

レンダリングコンテキストを対象に、第一引数はこちらも必ずGL_RENDERBUFFERにします。

第二引数はEAGLDrawableプロトコルに準拠したしたオブジェクト、つまりCAEAGLLayerオブジェクトであるビューのレイヤを渡すのですが、UIViewlayerプロパティが返すレイヤのクラスがCALayerのため、CAEAGLLayerにキャスト(型変換)する必要があります。

そしてglFramebufferRenderbufferでフレームバッファにレンダバッファをアタッチします。

第一引数は必ずGL_FRAMEBUFFERになります。

第二引数はフレームバッファの3つのアタッチメントポイントの1つを指定するのですが、必須となる色アタッチメントポイントであるGL_COLOR_ATTACHMENT0を指定します。
(深度やステンシルを利用する場合は、GL_DEPTH_ATTACHEMENTまたはGL_STENCIL_ATTACHMENTを指定することになります)

第三引数は必ずGL_RENDERBUFFERになります。

第四引数はアタッチするレンダバッファの識別名を指定します。

glGenRenderbuffers

void glGenRenderbuffers(GLsizei n, GLuint * renderbuffers); 

レンダバッファオブジェクト名を生成します。

glGenRenderbuffersは、renderbuffersで名付けたn個のレンダバッファオブジェクトを返します。

名前の形式は連続した整数のセットを保証するものではありませんが、返される名前のどれもglGenRenderbuffersの呼び出しの直前に使用されていなかったことを保証します。

glGenRenderbuffersの呼び出しによって返されるレンダバッファオブジェクト名は、glDeleteRenderbuffersで最初に削除されない限り、後続の呼び出しによって返されません。

glBindRenderbufferを呼び出すことによって最初にバインドされるまで、返されたレンダバッファオブジェクト名はレンダバッファオブジェクトに関連付けられません。

nが負の場合、GL_INVALID_VALUEが発生します。

n:生成されるレンダバッファ名の数を指定します。

renderbuffers:生成されたレンダバッファオブジェクト名が格納される配列を指定します。

glBindRenderbuffer

void glBindRenderbuffer(GLenum target, GLuint renderbuffer); 

名付けられたレンダバッファオブジェクトをバインドします。

レンダバッファは、レンダリング可能な内部フォーマットの単一の画像を含む、データストレージオブジェクトです。

レンダバッファの画像は、レンダリングの出力先や読み込みのためのソースとして、フレームバッファオブジェクトへのアタッチに使用することができます。

glBindRenderbufferは名付けられたレンダバッファオブジェクトを生成または使用することができます。

targetにGL_RENDERBUFFERを、renderbufferに新しいレンダバッファオブジェクトの名前を設定してglBindRenderbufferを呼び出すと、レンダバッファオブジェクト名にバインドします。

レンダバッファオブジェクトがバインドされている時、以前のバインドは自動的に破棄されます。

レンダバッファオブジェクト名は符号無し整数です。

値ゼロは予約されていますが、デフォルトのレンダバッファは存在しません。

代わりに、renderbufferをゼロに設定すると、以前にバインドされていたレンダバッファオブジェクトは事実上アンバインドされます。

レンダバッファオブジェクト名と対応するレンダバッファオブジェクトのコンテンツは、現在のGLレンダリングコンテキストの共有オブジェクト空間に対してローカルです。

新しいレンダバッファオブジェクト名の設定には、glGenRenderbuffersを使用することができます。

最初にバインドした直後のレンダバッファオブジェクトの状態は、フォーマットGL_RGBA4で、ゼロサイズのred、green、blue、alpha、depth、そしてステンシルピクセル深度のゼロサイズのメモリバッファです。

非ゼロのレンダバッファオブジェクト名がバインドされている間、ターゲットGL_RENDERBUFFER上のGL操作はバインドされているレンダバッファオブジェクトに影響を及ぼし、ターゲットGL_RENDERBUFFERのクエリは、バインドされたレンダバッファオブジェクトから状態を返します。

ゼロのレンダバッファオブジェクト名がバインドされている間は、初期状態におけるように、ターゲットGL_RENDERBUFFER上で状態を変更または照会を試みると、GL_INVALID_OPERATIONエラーが発生します。

glBindRenderbufferで生成されたフレームバッファオブジェクトのバインディングは、異なるレンダバッファオブジェクト名がバインドされるまで、あるいはglDeleteRenderbuffersでレンダバッファオブジェクトが削除されるまで、アクティブが持続します。

targetがGL_RENDERBUFFERでない場合は、GL_INVALID_ENUMが発生します。

target:レンダバッファオブジェクトをバインドするターゲットを指定します。
シンボリック定数はGL_RENDERBUFFERである必要があります。

renderbuffer:レンダバッファオブジェクトの名前を指定します。

renderbufferStorage:fromDrawable:

- (BOOL)renderbufferStorage:(NSUInteger)target fromDrawable:(id<eagldrawable>)drawable

OpenGL ESのレンダバッファオブジェクトに、描画可能なオブジェクトのストレージをバインドします。

戻り値は成功した場合はYES、それ以外の場合はNOになります。

画面に表示することができるレンダバッファを生成するには、レンダバッファをバインドし、このメソッドを呼び出すことによって共有ストレージを割り当てます。

このメソッドの呼び出しは、通常glRenderbufferStorageへの呼び出しを置換します。

レンダバッファは、このメソッドでストレージを割り当てた後で、presentRenderbuffer:を呼び出して表示することができます。

幅、高さ、内部カラーバッファフォーマットは、描画可能なオブジェクトの特性に由来します。

このメソッドを呼び出す前に、描画可能オブジェクトのdrawableProperties辞書にkEAGLDrawablePropertyColorFormatキーを追加することによって、内部カラーバッファフォーマットをオーバーライドすることができます。

OpenGL ESのレンダバッファが描画可能オブジェクトからデタッチする必要がある場合は、drawableをnilに設定してこのメソッドを呼び出すことができます。

target:現在バインドされているレンダバッファ用の、OpenGL ESのバインディングポイントを指定します。
OpenGL ES 1.0 APIを使用するコンテキストの場合は、GL_RENDERBUFFER_OESにする必要があります。
OpenGL ES 2.0 APIを使用するコンテキストの場合は、OES接尾辞を削除する必要があります。

drawable:EAGLDrawableプロトコルに準拠したオブジェクトで、レンダバッファにバインドするストレージを指定します。
iOS 3.0の場合は常にCAEAGLLayerオブジェクトです。

layer

@property(nonatomic, readonly, retain) CALayer *layer

レンダリングに使用する、ビューのCore Animationレイヤです。(読み込み専用)

このプロパティはnilになることはありません。

オブジェクトの実際のクラスは、layerClassメソッドによって返される値によって決定されます。

ビューはレイヤのデリゲートです。

警告:ビューはレイヤのデリゲートであるため、ビューを他のCALayerオブジェクトのデリゲートにしないでください。
更に、このレイヤオブジェクトのデリゲートを変更しないでください。

glFramebufferRenderbuffer

void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);

フレームバッファオブジェクトにレンダバッファオブジェクトをアタッチします。

glFramebufferRenderbufferは、現在バインドされているフレームバッファオブジェクトの論理バッファの一つとして、renderbufferによって指定されたレンダバッファにアタッチします。

attachmentは、フレームバッファオブジェクトのカラー、深度、またはステンシルバッファのどれにアタッチするかを指定します。

レンダバッファは、デフォルトのフレームバッファオブジェクト名0にアタッチされていない可能性があります。

renderbufferが0でない場合、アタッチメントポイントに指定されたGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPEの値はGL_RENDERBUFFERに設定され、GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAMEの値はrenderbufferに設定されます。

GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVELとGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACEは、デフォルト値がそれぞれ0とGL_TEXTURE_CUBE_MAP_POSITIVE_Xに設定されています。

現在バインドされているフレームバッファオブジェクトのattachment論理バッファへの以前のアタッチメントは破棄されます。

renderbufferが0の場合、現在バインドされているフレームバッファオブジェクトattachment論理バッファへアタッチされている画像があればデタッチされます。

GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPEの値はGL_NONEに設定されています。

GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAMEの値は0に設定されています。

GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVELとGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACEは、デフォルト値がそれぞれ0とGL_TEXTURE_CUBE_MAP_POSITIVE_Xに設定されています。

現在バインドされているフレームバッファへ画像がアタッチされている間にレンダバッファオブジェクトが削除された場合、現在バインドされているフレームバッファオブジェクトにアタッチされた画像へのアタッチメントポイントは、0のrenderbufferでglFramebufferRenderbufferを呼び出したようになります。

言い換えると、レンダバッファの画像は現在バインドされているフレームバッファからデタッチされます。

レンダバッファの画像は、バインドされていないフレームバッファから明確にデタッチされないことに注意してください。

バインドされていないフレームバッファからの画像のデタッチは、アプリケーションの責任です。

targetがGL_FRAMEBUFFERでない場合、GL_INVALID_ENUMが発生します。

renderbuffertargetがGL_RENDERBUFFERではなく、renderbufferが0ではない場合、GL_INVALID_ENUMが発生します。

attachmentがアタッチメントポイントに受け付けられなかった場合GL_INVALID_ENUMが発生します。

デフォルトのフレームバッファオブジェクト名0がバインドされている場合、GL_INVALID_OPERATIONが発生します。

renderbufferが0でも現在のレンダバッファオブジェクト名でもない場合、GL_INVALID_OPERATIONが発生します。

target:フレームバッファのターゲットを指定します。
シンボリック定数はGL_FRAMEBUFFERである必要があります。

attachment:アタッチする必要があるレンダバッファのアタッチメントポイントを指定します。
シンボリック定数は、GL_COLOR_ATTACHIMENT0、GL_DEPTH_ATTACHMENT、またはGL_STENCIL_ATTACHMENTの内の一つにする必要があります。

renderbuffertarget:レンダバッファのターゲットを指定します。
シンボリック定数はGL_RENDERBUFFERである必要があります。

renderbuffer:アタッチするレンダバッファオブジェクトを指定します。



参考文献

iOS OpenGL ESプログラミングガイド

UIView Class Reference

EAGLContext Class Reference

OpenGL ES 2.0 Reference Pages

テン*シー*シー/iPhoneアプリ開発、その(100) ようやく終息OpenGL ESセットアップ

WisdomSoft/OpenGL入門/テクスチャ

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(5)~OpenGLES2DViewクラス(1)

2011. 12. 14
OpenGLES2DViewクラスは、OpenGL ESを使用した2Dグラフィックスを描画するためのテンプレートとなるクラスです。

実際にはOpenGLES2DViewクラスをサブクラス化し(今回のプロジェクトの場合はGLFunViewクラス)、そこに独自の描画コードを追加する形となります。

なお、このクラスはOpenGL ES 1.1用で、OpenGL ES 2.0には対応していません。

また『はじめてのiPhoneプログラミング』本書ではAppleによって書かれているとありますが、コードを見ると元著者であるJeff LaMarche氏の署名になっており、再利用の帰属もどうなるのか疑問が残ります。
(Texture2DクラスにはAppleの署名が入っています)


●OpenGLES2DViewクラスの概要

OpenGLES2DViewクラスでは、iOS上でOpenGL ES 1.1を使用した2Dグラフィックスを表示するビューを作成するクラスです。

具体的には以下の実装を行っています。

・CAEAGLLayerクラスのレイヤの作成と設定
・EAGLContextクラスのレンダリングコンテキストの作成
・フレームバッファオブジェクトとレンダバッファの生成と初期化
・2Dグラフィックス用のビューポート変換、投影変換、モデルビュー変換の設定
・背景となるビュー全体を指定色で初期化し表示

このクラスをオーバーライドすることで、開発者は目的とする図形の頂点配列の設定と表示のコードを記述するだけで済みます。


●OpenGLES2DView.hヘッダファイル

#import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import "Constants.h"

@interface OpenGLES2DView : UIView {

@protected
    EAGLContext *context;
    GLuint viewRenderbuffer, viewFramebuffer;
    GLint backingWidth, backingHeight;
}

@end

7697

1)インポート

OpenGLES2DViewクラスのスーパークラスはUIViewなので最初にUIKitがインポートされています。

そしてOpenGL ESを使用するための5つのヘッダファイルがインポートされています。

EAGL.hはEAGLContextクラス、EAGLDrawable.hはEAGLContextEAGLDrawableクラスの宣言がなされています。

gl.hはOpenGL ESの基本関数、glext.hはOpenGL ESの拡張関数が宣言されています。

ES1フォルダ下のヘッダファイルしかインポートされていないことから、OpenGL ES 1.1にのみ対応することが分かります。

QuartzCore.hはCAEAGLLayerクラスを利用するためにインポートしています。

ナビゲーションバーとツールバーのセグメンテッドコントロール用の定数を定義しているConstants.hもインポートされていますが、このクラスでは使用しないので無くても構いません。
(このことからもApple社が提供しているものではない、またはそこから改造したものだと推察できます)

2)インスタンス変数

インスタンス変数の宣言は『@protected』というコンパイラ指示子が使われています。

これは変数のアクセス範囲を決定する物で、『詳解 Objective-C 2.0』によると『宣言したクラスおよびそのサブクラス内でアクセス可能』で、デフォルトの設定でもあるということから、特に意識する必要はありません。

EAGLContextクラスの変数contextは、レンダリングコンテキストです。

GLuint型の変数は、viewRenderbufferはレンダバッファオブジェクト、viewFramebufferはフレームバッファオブジェクトを示す識別子です。

GLint型の変数backingWidthとbackingHeightは、レンダバッファから取得するサイズで、ビューポートのサイズの設定に使用します。


●OpenGLES2DView.mソースファイル

#import "OpenGLES2DView.h"

@implementation OpenGLES2DView

+ (Class) layerClass
{
    return [CAEAGLLayer class];
}

#pragma mark -
- (BOOL)createFramebuffer {

    glGenFramebuffersOES(1, &viewFramebuffer);
    glGenRenderbuffersOES(1, &viewRenderbuffer);

    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
    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(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        return NO;
    }

    return YES;
}

- (id)initWithCoder:(NSCoder*)coder
{
    if((self = [super initWithCoder:coder])) {
        // Get the layer
        CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;
        eaglLayer.opaque = YES;
        eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                [NSNumber numberWithBool:NO],
kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGB565,
kEAGLDrawablePropertyColorFormat, nil];
        context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

        if(!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer]) {
            [self release];
            return nil;
        }

        glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
        glViewport(0, 0, backingWidth, backingHeight);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrthof(0, self.frame.size.width, 0, self.frame.size.height, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glClearColor(0.78f, 0.78f, 0.78f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glEnableClientState (GL_VERTEX_ARRAY);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
        [context presentRenderbuffer:GL_RENDERBUFFER_OES];

        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
        glEnableClientState(GL_VERTEX_ARRAY);
    }

    return self;
}

- (void)dealloc {
    [super dealloc];
}

@end

7605

ソースファイルでは3つのメソッドが実装されています。

layerClassメソッドは、ビューの基盤レイヤとしてCAEAGLLayerオブジェクトを作成するためのもので、暗黙的に呼び出されます。

createFramebufferメソッドは、フレームバッファオブジェクトとレンダバッファの生成とコンテキストへのバインドを行うもので、initWithCoder:メソッドから呼び出されます。

initWithCoder:メソッドはビューをnibファイルから呼び出した後に追加でビューの初期化を行うもので、OpenGL ESに対応するための初期化が行われています。

1)layerClassメソッド

layerClassメソッドは、OpenGL ESを利用できるようにするため、ビューの基盤レイヤをCAEAGLLayerクラスに変更しています。
(『iOS OpenGL ESプログラミングガイド』のp.35『Core Animationレイヤへのレンダリング』の『2』を参照)

layerClass

+ (Class)layerClass

このクラスのインスタンスのためのレイヤの生成に使用するクラスを返します。

戻り値は、ビューのCore Animationレイヤの生成に使用するクラスを返します。

このメソッドはデフォルトでCALayerクラスのオブジェクトを返します。

サブクラスはこのメソッドをオーバーライドし、必要に応じて異なるレイヤクラスを返すことが出来ます。

例えば描画にOpenGL ESを使用する場合、このメソッドをオーバーライドしてCAEAGLLayerクラスのオブジェクトを返すことが出来ます。

このメソッドは、対応するレイヤオブジェクトを生成するために、ビューの生成の初期に一度だけ呼び出されます。



参考文献

iOS OpenGL ESプログラミングガイド

UIView Class Reference

詳解 Objective-C 2.0 第3版詳解 Objective-C 2.0 第3版
(2011/12/28)
荻原 剛志

商品詳細を見る

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(4)~OpenGL ESへの対応と作業工程

2011. 12. 11
GLFunプロジェクトでOpenGL ESを利用できるようにするには、『GLFun(2)~QuartzFunとGLFunの違い』で述べたようにOpenGLES.frameworkとQuartzCore.frameworkの2つのフレームワークを追加する必要があります。

また今回のGLFunの場合、具体的なコンテンツの描画部分以外の基本的なOpenGL ESのコードは、OpenGLES2DViewとTexture2Dの2つのクラスをサンプルコードからコピーして使うとなっています。


●フレームワークの追加

フレームワークをプロジェクトに追加するには、XcodeのFrameworksフォルダを選択して右クリックし、『追加』→『既存のフレームワーク...』を選択します。

7692

リストの一覧からOpenGLES.frameworkとQuartzCore.frameworkを選択し、『追加』ボタンをクリックします。

7693


●サンプルコードからのクラスの追加

OpenGLES2DViewとTexture2Dの2つのクラスは、原著であるApress社のサイトからサンプルコードをダウンロードする必要があります。

日本語版も出ているiPhone OS 3.x版は『Beginning iPhone 3 Development Exploring the iPhone SDK』、iOS 4.x版は『Beginning iPhone 4 Development Exploring the iOS SDK』で、それぞれ『Source Code/Downloads』タブの『Download Now』からダウンロードすることができます。

iPhone OS 3.x版の場合は『Beginning iPhone Development Projects Oct 12 2009/12 GLFun/Classes』フォルダ、iOS 4.x版は『Begin_iPhone4_Projects_2011_01_21/14 - GLFun/Classes』フォルダ内にある両クラスのヘッダ/ソースファイルを、自身のGLFunプロジェクトのClassesフォルダにコピーし、XcodeのClassesに追加してください。
(どちらからコピーしても同じファイルです)

7694


●iOSにおけるOpenGL ESの実装

前述の通り、本書ではOpenGL ESによる描画の基本的な部分は『2つのクラスをコピーすることで簡単に利用できる』と言うだけで、中身についてはほとんど触れられていません。

確かにそれだけで1冊本が書ける内容ですし、実際iOSでOpenGL ESを使う本は何冊も出ています。

ただ1、2冊買っても中々難しいものがあり、手元にある『OpenGLで作るiPhone SDKゲームプログラミング』や『iPhoneではじめるOpenGL ESプログラミング』では具体的な描画関数を中心に解説されていて、基盤技術の説明がほとんどありません。

公式の『iOS OpenGL ESプログラミングガイド』もOpenGL ESをiOS上でどう実装するかが話の中心なので、基礎技術に関しては他書を読むようにとなっています。

OpenGL ESを使用したiOS用3Dグラフィックス(1)』で紹介したセッションビデオでも、通称『赤本』と呼ばれる『OpenGL Programming Guide』(日本語版は『OpenGLプログラミングガイド』)は必読とされていますが、高価な本であることもあって手が出しづらく、なかなか基礎的な内容に踏み込み難くなっています。

ここでは本プロジェクトで使用されている部分の基盤技術に関して、理解している範囲で説明したいと思います。


●コンテンツの作成と出力先

Quartzでは、Core Graphics技術を使用してコンテンツを作り、ビューを出力先としていました。

 Core Graphics(CGContext) → ビュー(UIView

OpenGL ESの場合は、OpenGL ES技術を使用してコンテンツを作り、Core Animationレイヤを出力先とします。

 OpenGL ES(EAGLContext) → レイヤ(CAEAGLLayer

※『GLFun(2)~QuartzFunとGLFunの違い』参照

7644

7645


●EAGLContextとCAEAGLLayer間の処理

OpenGL ES内でのレンダリング先はフレームバッファオブジェクトであり、EAGLがフレームバッファオブジェクトの内容をCAEAGLLayerに出力します。

7660

フレームバッファオブジェクトは、画面に出力される2D画像のデータそのものを保持しているわけではなく、出力画像を構成するレンダバッファを取りまとめる単なるコンテナです。

実際に画面を構成する色や深度のデータは、レンダバッファが保持しています。

7650

フレームバッファオブジェクトやレンダバッファなど、オブジェクト同士を接続することを『アタッチ』と呼び、それらのオブジェクトとコンテキストを接続することを『バインド』と呼びます。

7654
 
  CAEAGLLayer
CAEAGLLayerの使い方

1. レンダリングされたコンテンツを表示するため、
 UIViewをサブクラス化

2. CALayerの代わりにCAEAGLLayerクラスを返すよう
 に、UIViewの[layerClass]メソッドをオーバーライド
UIKit
3. OpenGL ESのレンダバッファとFBOを生成、バインドOpenGL ES
4. レンダバッファにCAEAGLLayerをアタッチEAGL
5. OpenGL ESを使用してレンダバッファにレンダリングOpenGL ES
6. ディスプレイにレンダバッファのコンテンツを表示EAGL

※『iOS OpenGL ESプログラミングガイド』、『OpenGL ESを使用したiOS用3Dグラフィックス(2)』参照


●ビューポート変換

正規化デバイス座標系からビューポートのデバイス座標系(ウィンドウ座標系)への変換をビューポート変換と呼びます。

ビューポートは出力画面(CAEAGLLayer)のどの領域にOpenGL ESコンテンツを描画するかを示すもので、原点座標と領域サイズで指定します。

通常は画面全体に表示するため、原点を(0, 0)、サイズは出力画面の幅と高さを指定します。

正規化デバイス座標系はビューボリュームをx、y、zそれぞれが-1~1(資料によってはx、yが-1~1、zが0~1)の空間に投影変換したものです。

ビューポート変換により、正規化デバイス座標系(x, y)における(-1, -1)から(1, 1)に投影される平面を、ビューポートの平面のサイズに変換することができます。

※『WisdomSoft/OpenGL入門/ビューポート』、『GLUTによる「手抜き」OpenGL入門/6.1 座標軸とビューポート』、『Shinsaku HIURA Home Page/講義資料/2007年度 コンピュータ工学演習/コンピュータ工学演習 解説と課題(6)』、『Web site of Haiyuan WU/Home page for wuhy/グラフィックス科学演習/4. 3次元グラフィックスの基礎』参照


●投影変換(射影変換、透視変換)

視点座標系(射影座標系、眼点座標系)から正規化デバイス座標系への変換を投影変換と呼びます。

視点座標系は視点のローカル座標系で、カメラを原点として右がx軸の正方向、上がy軸の正方向、視線がz軸の負方向となっており、ビューボリュームの設定に使用されます。

ビューボリューム(視野空間、視体積、視野錘台)は、ワールド座標系の三次元空間において、レンダリングを行う特定の領域ことで、たとえワールド座標系内に存在していてもビューボリュームの外にあるオブジェクトはレンダリングされません。

ビューボリュームはカメラから見て垂直な手前と奥の面と視野角によって決定され、平行投影なら直方体、透視投影なら錐台の空間領域となります。

投影変換によりビューボリュームの空間領域を正規化し、カメラから見た前後関係、透視投影であれば遠方のオブジェクトを小さくするなど、三次元空間から二次元平面に変換することができます。

※『IMURA Masataka/OpenGLメモ/OpenGLの座標系』、『XNA - ソーサリーフォース/XNA プログラミング Tips 一覧/基礎知識/3Dモデルの座標変換について』、『床井研究室/コンピュータグラフィックス/コンピュータグラフィックス 第8回:スクリーンに映す』参照


●モデルビュー変換

ワールド座標系から視点座標系への変換をモデルビュー変換と呼びます。

モデルビュー変換はモデリング変換と視点変換(視界変換、ビューイング変換)を合わせた物です。

モデリング変換は個々の3Dオブジェクトを拡大/縮小、回転、平行移動して、ワールド座標系に配置することです。

視点変換は、ワールド座標系における視点(カメラ)の位置と視線(方向)を決定することです。

※『WisdomSoft/OpenGL入門/モデルビュー変換』、『viLab/授業資料/コンピュータグラフィックス/モデルビュー変換』参照


●OpenGL ESグラフィックスパイプライン

QuartzとOpenGLとの違いから始めたことと、実際にプログラムをコーディングする順序での説明として、一般的な説明の順序とは逆に説明しました。

一般的に示される処理の流れとしては下記のようになります。

頂点配列(プリミティブ)の設定
モデルビュー変換(オブジェクトの配置)
投影変換(ビューボリュームのクリッピング)
ビューポート変換(画面サイズへ変換)
フレームバッファオブジェクトへ出力
CAEAGLLayerで表示

今回のプロジェクトは2Dグラフィックスの表示のため、ライティングや深度処理などが不要になります。

なお、テクスチャリングに関しては後述します。



参考文献

iOS OpenGL ESプログラミングガイド

GLUTによる「手抜き」OpenGL入門

WisdomSoft/OpenGL入門

Shinsaku HIURA Home Page/講義資料/2007年度 コンピュータ工学演習

Web site of Haiyuan WU/Home page for wuhy/グラフィックス科学演習

IMURA Masataka/OpenGLメモ

XNA - ソーサリーフォース/XNA プログラミング Tips 一覧

床井研究室/コンピュータグラフィックス

viLab/授業資料/コンピュータグラフィックス

OpenGLで作るiPhone SDKゲームプログラミングOpenGLで作るiPhone SDKゲームプログラミング
(2009/12/18)
横江 宗太(株式会社パンカク)

商品詳細を見る

iPhoneではじめるOpenGL ESプログラミングiPhoneではじめるOpenGL ESプログラミング
(2010/07/13)
酒井 裕司

商品詳細を見る

OpenGL Programming Guide: The Official Guide to Learning OpenGL, Versions 3.0 and 3.1OpenGL Programming Guide: The Official Guide to Learning OpenGL, Versions 3.0 and 3.1
(2009/07/21)
Dave The Khronos OpenGL ARB Working Group Shreiner

商品詳細を見る

OpenGLプログラミングガイド 原著第5版OpenGLプログラミングガイド 原著第5版
(2006/12/19)
OpenGL策定委員会

商品詳細を見る

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る






bose_soundlink_revolve
Calendar
11 | 2011/12 | 01
Sun Mon Tue Wed Thu Fri Sat
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

Author:水月杏香
永遠の初心者プログラマ。

Wish List
WACOM


ARC
Technical Q&A
情報プロパティリストキー
Start Developing iOS Apps Today
BOSE

bose_soundlink_revolve
Reference
NSApplicationDelegateプロトコル
NSArrayクラス
NSAutoreleasePoolクラス
NSBundleクラス
NSBundle UIKit追加分
NSCalendarクラス
NSCoderクラス
NSCodingプロトコル
NSCopyingプロトコル
NSDataクラス
NSDateクラス
NSDateFormatterクラス
NSDictionaryクラス
NSEntityDescriptionクラス
NSEnumeratorクラス
NSErrorクラス
NSExceptionクラス
NSFetchRequestクラス
NSFileHandleクラス
NSFileManagerクラス
NSIndexPathクラス
NSIndexPath UIKit追加分
NSKeyedArchiverクラス
NSKeyedUnarchiverクラス
NSKeyValueCodingプロトコル
NSLocaleクラス
NSManagedObjectクラス
NSManagedObjectContextクラス
NSManagedObjectModelクラス
NSMutableArrayクラス
NSMutableCopyingプロトコル
NSMutableDictionaryクラス
NSMutableSetクラス
NSNotificationクラス
NSNotificationCenterクラス
NSNullクラス
NSNumberクラス
NSObjectクラス
NSObject UIKit追加分
NSObjectプロトコル
NSPersistentStoreクラス
NSPersistentStoreCoordinatorクラス
NSPredicateクラス
NSPropertyListSerializationクラス
NSRunLoopクラス
NSSetクラス
NSStringクラス
NSString UIKit追加分
NSTimerクラス
NSTimeZoneクラス
NSURLクラス
NSURLProtectionSpaceクラス
NSURLRequestクラス
NSUserDefaultsクラス
NSValueクラス

UIActionSheetクラス
UIActionSheetDelegateプロトコル
UIActivityIndicatorViewクラス
UIAlertViewクラス
UIAlertViewDelegateプロトコル
UIApplicationクラス
UIApplicationDelegateプロトコル
UIBarButtonItemクラス
UIBarItemクラス
UIButtonクラス
UIColorクラス
UIControlクラス
UIDatePickerクラス
UIDeviceクラス
UIEventクラス
UIFontクラス
UIGestureRecognizerクラス
UIImageクラス
UIImageViewクラス
UIKit Function
UILabelクラス
UINavigationControllerクラス
UINavigationItemクラス
UIPickerViewクラス
UIPickerViewDataSourceプロトコル
UIPickerViewDelegateプロトコル
UIPinchGestureRecognizerクラス
UIResponderクラス
UIScreenクラス
UIScrollViewクラス
UISearchBarクラス
UISearchBarDelegateプロトコル
UISegmentedControlクラス
UISliderクラス
UISwipeGestureRecognizerクラス
UISwitchクラス
UITableViewクラス
UITableViewCellクラス
UITableViewControllerクラス
UITableViewDataSourceプロトコル
UITableViewDelegateプロトコル
UITapGestureRecognizerクラス
UITextFieldクラス
UITextFieldDelegateプロトコル
UITextInputTraitsプロトコル
UITextViewクラス
UITextViewDelegateプロトコル
UIToolbarクラス
UITouchクラス
UIViewクラス
UIViewControllerクラス
UIWebViewクラス
UIWebViewDelegateプロトコル
UIWindowクラス

AVAudioPlayerクラス
AVAudioPlayerDelegateプロトコル

CADisplayLinkクラス
CAEAGLLayerクラス
CALayerクラス

CGAffineTransform
CGBitmapContext
CGColor
CGColorSpace
CGContext
CGGeometry
CGImage
CGPath

EAGLContextクラス
EAGLDrawableプロトコル

Foundation Constants
Foundation Data Types
Foundation Functions

MPMediaItemクラス
MPMediaItemArtworkクラス
MPMediaPlaylistクラス
MPMediaPropertyPredicateクラス
MPMediaQueryクラス
MPMusicPlayerControllerクラス

Randomization Services

System Sound Services
Amazon


OpenGL ES
SQLite
Monthly Archives
Recent Comments
Recent TrackBacks
RSS Link
Visitors
QR Code
QR