Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Objective-C++ - インスタンス変数のコンストラクタ/デストラクタ

タグ: Objective-C C++

http://d.hatena.ne.jp/faith_and_brave/20081121/1227260455

Xcode(GCC)には、「Call C++ Default Ctors/Dtors in Objective-C」というオプションがあります。

これを ON にすると、 Objective-C のクラスメンバに C++ クラスオブジェクトを持ったときにC++ クラスオブジェクトのコンストラクタとデストラクタが呼ばれるようになります。

(;゚Д゚)マジで!
それはリンゴとハチミツが恋をした並の衝撃。
ということはこのクラス

@interface Foo : NSObject
{
	NSString* string;
	boost::shared_ptr<Bar> bar;
	Baz* baz;
};
- (id)initWithString:(NSString*)str bar:(const boost::shared_ptr<Bar>&)br baz:(Baz*)bz;
@end

@implementation Foo
- (id)initWithString:(NSString*)str bar:(const boost::shared_ptr<Bar>&)br baz:(Baz*)bz
{
	if(self = [super init])
	{
		string = [str retain];
		bar = br;
		baz = bz;
	}
	return self;
}

- (void)dealloc
{
	[string release];
	[super dealloc];
}
@end

こう書けるわけか。
shared_ptrをnewするのは気持ち悪かったからこれは良い。
ついでに

@property (nonatomic) boost::shared_ptr<Bar> bar;

できるようにならんかなーと試してみたが、やっぱダメだった。

##演算の結果

タグ: C++
#include <iostream>
#define CALL(arg) CALL_ ## arg
#define CALL_(x, y) #x " fly out of your " #y
#define CALL_nullpo "gatt"

int main()
{
	std::cout << CALL((demons, nose)) << std::endl;
	std::cout << CALL(nullpo) << std::endl;
}

このコードはVC8では

demons fly out of your nose
gatt

このようにいい具合に動作するが、gcc 4.0では

error: passing "CALL_" and "(" does not give a valid preprocessing token

とエラーが出てコンパイルできない。

Cの規格によると、##演算子の挙動は

置換要素並びの##前処理字句(実引数の中の##前処理字句を除く)を削除し,その直前にある前処理字句をその直後の前処理字句と連結する。プレースマーカ前処理字句は特別に扱う。すなわち,二つのプレースマーカ前処理字句を連結すると,一つのプレースマーカ前処理字句となる。プレースマーカ前処理字句と、プレースマーカでない前処理字句を連結すると,そのプレースマーカでない前処理字句となる。その結果が正しい前処理字句にならない場合,その動作は未定義とする。

(*゚ー゚) ……
読みにくいので、本質ではない部分を中略する。

置換要素並びの##前処理字句(実引数の中の##前処理字句を除く)を削除し,その直前にある前処理字句をその直後の前処理字句と連結する。
(中略)
その結果が正しい前処理字句にならない場合,その動作は未定義とする。

なお、ISO/IEC 9899では中略部分はピリオドが使われない程度にまとまった文になっている。
こんな「プレースマーカの扱いについて書かれていると思ったらいつの間にか終わっている」文章にはなっていない。

さて、前処理字句というのは

ヘッダ名
識別子
前処理数
文字定数
文字列リテラル
区切り子
上の分類のいずれにも当てはまらない非空白類文字

のことを言う。
CALL_ は「識別子」なので前処理字句
( は「区切り子」なので前処理字句
その二つを繋げると CALL_( になるが、これは前処理字句ではない
ゆえに CALL_ ## ( の結果は未定義となる。

これに関しては、実はMSDNでも以下の様に「だめだよ」と明記されている。

The resulting token must be a valid token.

にも関わらず連結できてしまうのは、この「token」の定義がCの「preprocessing token」と異なるか、あるいは単にプリコンパイラがエラーにするのをサボっているのだろう。

BOOST_PP_ASSERT_MSG

タグ: C++ Boost

名前からして「第1引数が0なら第2引数をエラーメッセージとして表示する」ものだと思ったのだが、

#include <boost/preprocessor/debug/assert.hpp>
BOOST_PP_ASSERT_MSG(0, "hoge");

をビルドしても、VC8では

(2) : error C2059: 構文エラー : '文字列'

gcc4.0では

2: error: expected unqualified-id before string constant

と出るだけで、肝心の"hoge"は微塵も姿を見せない。
なぜだろうとヘッダ(Boost 1.38.0)を覗いてみると、

#define BOOST_PP_ASSERT_MSG BOOST_PP_ASSERT_MSG_D
#define BOOST_PP_ASSERT_MSG_D(cond, msg) BOOST_PP_EXPR_IIF(BOOST_PP_NOT(cond), msg)

とある。
つまりこのマクロ、今の実装は単に

#include <boost/preprocessor/debug/assert.hpp>
"hoge";

こう展開し、あとはコンパイラが空気を読んでエラーメッセージを生成してくれることを期待していると、そういうものらしい。

ポイントスプライト(というかパーティクル)で遊ぶ

タグ: iPhone OpenGL Objective-C
particle2.png

指を置くとパーティクルが噴き出す。
複数(5本まで)指を置くと、別の指から噴き出したパーティクル同士が引かれ合う。
そんなおもちゃ。

  • 例によって「OpenGL ES Application」の新規プロジェクトを作成する(Xcode 3.1.2)。
  • iPhone OS 2.2 Libraryのサンプル「GLPaint」に含まれている「Particle.png」をプロジェクトに追加する。
  • 「Frameworks」に「CoreGraphics.framework」を追加する。
  • 新規ファイル「ParticleSystem.h」をプロジェクトに追加し、以下のコードを記述する。
    #import <CoreGraphics/CGGeometry.h>
    
    typedef CGPoint Coord;
    typedef Coord Vector;
    
    @interface Particle : NSObject
    {
    	NSUInteger life;
    	Coord coord;
    	Vector vector;
    }
    - (BOOL)step;
    @property (nonatomic) NSUInteger life; // 残生存ステップ数
    @property (nonatomic) Coord coord; // 現在座標
    @property (nonatomic) Vector vector; // ステップ辺りの移動量
    @end
    
    @interface ParticleSystem : NSObject
    {
    	NSUInteger life;
    	Coord coord;
    	NSUInteger interval;
    	NSMutableArray* particles;
    	
    	NSInteger counter;
    }
    - (void)step;
    @property (nonatomic) NSUInteger life; // パーティクルの初期生存ステップ数
    @property (nonatomic) Coord coord; // パーティクルの初期座標
    @property (nonatomic) NSUInteger interval; // パーティクルの生成間隔(0なら生成せず)
    @property (nonatomic, readonly) NSArray* particles; // 管理しているパーティクル
    @end
    
    @interface ParticleSystem(Super)
    + (void)stepSystems:(NSArray*)particleSystems;
    @end
  • 新規ファイル「ParticleSystem.m」をプロジェクトに追加し、以下のコードを記述する。
    #include <math.h>
    #include <limits.h>
    #import "ParticleSystem.h"
    
    @implementation Particle
    
    @synthesize life, coord, vector;
    
    - (BOOL)step
    {
    	if(life == 0)
    		return NO;
    	coord.x += vector.x;
    	coord.y += vector.y;
    	life--;
    	return YES;
    }
    
    @end
    
    @implementation ParticleSystem
    
    @synthesize life, coord, interval, particles;
    
    - (id)init
    {
    	if(self = [super init]) {
    		life = 60;
    		interval = 1;
    		particles = [[NSMutableArray alloc] initWithCapacity:life / interval + 1];
    	}
    	return self;
    }
    
    - (void)dealloc
    {
    	[particles release];
    	[super dealloc];
    }
    
    - (void)step
    {
    	if(interval != 0) {
    		if(counter == 0) {
    			Particle* particle;
    			particle = [[Particle alloc] init];
    			particle.life = life;
    			particle.coord = coord;
    			Vector vector = {
    				(rand() % 1001 - 500) / 500.f,
    				(rand() % 1001 - 500) / 500.f
    			};
    			particle.vector = vector;
    			[particles addObject:particle];
    		}
    		counter = (counter + 1) % interval;
    	}
    
    	NSMutableIndexSet* deadParticleIndices = [NSMutableIndexSet indexSet];
    	NSUInteger index = 0;
    	for(Particle* particle in particles) {
    		if(! [particle step]) {
    			[deadParticleIndices addIndex:index];
    		}
    		index++;
    	}
    	[particles removeObjectsAtIndexes:deadParticleIndices];
    }
    
    @end
    
    @implementation ParticleSystem(Super)
    + (void)stepSystems:(NSArray*)particleSystems
    {
    	for(ParticleSystem* system in particleSystems) {
    		[system step];
    	}
    
    	NSUInteger n = [particleSystems count];
    	if(n <= 1)
    		return;
    
    	for(NSUInteger i = 0; i < n - 1; i++) {
    		ParticleSystem* system1 = [particleSystems objectAtIndex:i];
    		for(NSUInteger j = i + 1; j < n; j++) {
    			ParticleSystem* system2 = [particleSystems objectAtIndex:j];
    			for(Particle* p1 in system1.particles) {
    				Vector v1 = p1.vector;
    				for(Particle* p2 in system2.particles) {
    					Vector v2 = p2.vector;
    					float dx = p2.coord.x - p1.coord.x;
    					float dy = p2.coord.y - p1.coord.y;
    					float d = sqrtf(dx * dx + dy * dy);
    					if(d < 10.f)
    						continue;
    					float s = 0.002f;
    					float vx = s * dx / d;
    					float vy = s * dy / d;
    					v1.x += vx;
    					v1.y += vy;
    					v2.x -= vx;
    					v2.y -= vy;
    					p1.vector = v1;
    					p2.vector = v2;
    				}
    			}
    		}
    	}
    }
    @end
  • EAGLViewクラスに以下のインスタンス変数を追加する。
    NSMutableArray* particleSystems; // 表示中のパーティクルシステム
    NSMutableDictionary* touchingSystems; // 操作中のパーティクルシステム
  • 追加したインスタンス変数は EAGLView::dealloc で解放する。
    [touchingSystems release];
    [particleSystems release];
  • EAGLViewクラスに以下のメソッドを追加する。
    - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
    {
    	for(UITouch* touch in touches) {
    		ParticleSystem* system = [[ParticleSystem alloc] init];
    		system.coord = [touch locationInView:self];
    		[touchingSystems setObject:system forKey:[NSValue valueWithPointer:touch]];
    		[particleSystems addObject:system];
    		[system release];
    	}
    }
    
    - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
    {
    	for(UITouch* touch in touches) {
    		ParticleSystem* system = [touchingSystems objectForKey:[NSValue valueWithPointer:touch]];
    		system.coord = [touch locationInView:self];
    	}
    }
    
    - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
    {
    	for(UITouch* touch in touches) {
    		NSValue* value = [NSValue valueWithPointer:touch];
    		ParticleSystem* system = [touchingSystems objectForKey:value];
    		system.interval = 0;
    		[touchingSystems removeObjectForKey:value];
    	}
    }
    
    - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
    {
    	for(UITouch* touch in touches) {
    		NSValue* value = [NSValue valueWithPointer:touch];
    		ParticleSystem* system = [touchingSystems objectForKey:value];
    		[touchingSystems removeObjectForKey:value];
    		[particleSystems removeObject:system];
    	}
    }
  • EAGLView::initWithCoder:animationInterval = 1.0 / 60.0; の下辺りに以下のコードを追加する。
    // テクスチャを読み込む
    CGImageRef image = [UIImage imageNamed:@"Particle.png"].CGImage;
    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);
    
    GLubyte* data = (GLubyte*)malloc(width * height * 4);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef particleContext =
    	CGBitmapContextCreate(data, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    CGContextClearRect(particleContext, CGRectMake(0, 0, width, height));
    CGContextDrawImage(particleContext, CGRectMake(0, 0, width, height), image);
    CGContextRelease(particleContext);
    
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    free(data);
    
    // テクスチャを有効化
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glEnable(GL_TEXTURE_2D);
    
    // ポイントスプライトを有効化
    glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);
    glEnable(GL_POINT_SPRITE_OES);
    
    // ブレンド方針を設定
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glEnable(GL_BLEND);
    
    // ポイントスプライトのサイズは64で固定
    glPointSize(64);
    
    // 頂点配列と色配列を有効化
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    
    // その他初期化
    particleSystems = [[NSMutableArray alloc] init];
    touchingSystems = [[NSMutableDictionary alloc] init];
    self.multipleTouchEnabled = YES;
  • EAGLView::drawView を以下の様に書き換える。
    [EAGLContext setCurrentContext:context];
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(0.0f, backingWidth, backingHeight, 0.0f, -1.0f, 1.0f);
    
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    [ParticleSystem stepSystems:particleSystems];
    
    NSMutableIndexSet* deadParticleSystemIndices = [NSMutableIndexSet indexSet];
    NSUInteger index = 0;
    for(ParticleSystem* system in particleSystems) {
    	NSArray* particles = system.particles;
    	NSUInteger particlesCount = [particles count];
    	if(particlesCount == 0) {
    		// パーティクルを管理しておらず
    		if(system.interval == 0) {
    			// パーティクル生成間隔が0(=生成しない)のシステムは削除対象
    			[deadParticleSystemIndices addIndex:index];
    		}
    	}
    	else {
    		GLfloat data[particlesCount * 6];
    		GLfloat* p = data;
    		GLfloat ratioUnit = 1.f / system.life;
    		for(Particle* particle in particles) {
    			GLfloat ratio =  particle.life * ratioUnit;
    			*(p++) = particle.coord.x;
    			*(p++) = particle.coord.y;
    			*(p++) = 0.8f * ratio;
    			*(p++) = 0.8f * ratio;
    			*(p++) = 2.0f * ratio;
    			*(p++) = 1.f;
    		}
    		glVertexPointer(2, GL_FLOAT, 6 * sizeof(GLfloat), data);
    		glColorPointer(4, GL_FLOAT, 6 * sizeof(GLfloat), data + 2);
    		glDrawArrays(GL_POINTS, 0, particlesCount);
    	}
    	index++;
    }
    [particleSystems removeObjectsAtIndexes:deadParticleSystemIndices];
    
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];

以上。
今回は「ポイントスプライトを使って何かする」のが目的なので、それ以外は割りと適当。
「UITouchインスタンスの同一性をキーにパーティクルシステムを管理している」部分も、これが正しいかどうかは分からない。

単一のテクスチャを

  • 回転等させず
  • GL_POINT_SIZE_MAX(iPhoneなら64)以下のサイズで

描きたい場合、ポイントスプライトを使う方がスマートで、かつ速い。
以前書いた記事の後半(まさにこの条件に合致している)なども、ポイントスプライトで実装するとそれだけで30fpsから38fpsに改善される。
ただし、OpenGLのポイントスプライトはテクスチャアトラスが使えない。
そのため「全種類のテクスチャを1回の描画呼び出しで描ききる」ことは、ポイントスプライトでは不可能である。

NSMutableArrayは環状バッファ

タグ: Objective-C Mac

――という噂を耳にしたが出典が見つからない。
なのでとりあえず現在(MacOS X 10.5.6)の実装を慮ってみる。

void test(unsigned int n)
{
	NSMutableArray* numbers = [NSMutableArray arrayWithCapacity:n + 1];
	for(unsigned int i = 0; i < n; i++)
	{
		[numbers addObject:[NSNumber numberWithInt:i]];
	}
	
	NSNumber* number = [NSNumber numberWithInt:n];
	for(unsigned int i = 0; i <= 4; i++)
	{
		unsigned int index = n * i / 4;
		NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
		for(int j = 0; j < 1000000; j++)
		{
			[numbers insertObject:number atIndex:index];
			[numbers removeObjectAtIndex:index];
		}
		NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
		NSLog(@"%d %6d: %f", n, index, end - start);
	}
}

指定した数の要素を持つNSMutableArrayの、「先頭」「前より」「真ん中」「後ろより」「後方」への要素追加/削除にかかる時間(×1000000)を測る。

引数は、

for(int i = 10; i <= 100000; i *= 10)
{
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	test(i);
	[pool drain];
}

「10」「100」「1000」「10000」「100000」の5通り。

これをMBP(C2D2.8)で実行。

10      0: 0.189038
10      2: 0.203648
10      5: 0.206734
10      7: 0.203767
10     10: 0.187448
100      0: 0.230543
100     25: 0.262474
100     50: 0.277086
100     75: 0.269977
100    100: 0.232720
1000      0: 0.231873
1000    250: 0.390210
1000    500: 0.631260
1000    750: 0.389550
1000   1000: 0.231937
10000      0: 0.229667
10000   2500: 2.249399
10000   5000: 3.242165
10000   7500: 1.745310
10000  10000: 0.232156
100000      0: 0.228711
100000  25000: 18.939664
100000  50000: 38.732296
100000  75000: 22.498229
100000 100000: 0.230097

先頭と後方への追加/削除だけが定数時間で済んでいるところを見ると、たしかに環状バッファっぽい感じはする。

続きを読む

Blogテンプレート変更

  • DTDがXHTML(前のはHTML4.01)
  • 本文の横幅が可変長(前のは固定長)
  • 2カラム
  • デフォルトの見栄えが好み

以上の条件を満たすものを選んだ結果、これ(skin14)に決まりました。

コールグラフを出してみる(Doxygen)

タグ: Doxygen
void Z();
void Y();
void X();
void W();
void V();
void U() { Z(); V(); }
void T() { Y(); Z(); }
void S() { X(); Y(); }
void R() { W() ;X(); }
void Q() { V(); W(); }
void P() { Z(); V(); }
void O() { T(); U(); }
void N() { S(); T(); }
void M() { R(); S(); }
void L() { Q(); R(); }
void K() { P(); Q(); }
void J() { N(); O(); }
void I() { M(); N(); }
void H() { L(); M(); }
void G() { K(); L(); }
void F() { I(); J(); }
void E() { H(); I(); }
/// @callergraph
void D() { G(); H(); }
void C() { E(); F(); }
/// @callgraph
/// @callergraph
void B() { D(); E(); }
/// @callgraph
void A() { B(); C(); }

以上のソースコードを以下のDoxyfileでドキュメント化すると、コメントブロックに「@callgraph」コマンドを含む関数から「呼び出しグラフ(コールグラフ)」が、「@callergraph」コマンドを含む関数から「呼び出し元グラフ」が生成される。

# dotはコールグラフの作成に必須
HAVE_DOT = YES
# 全ての関数をドキュメント化する
EXTRACT_ALL = YES
# 以下「Latex不要」「日本語で」「dotのフォントはヒラギノ角ゴで」
GENERATE_LATEX = NO
OUTPUT_LANGUAGE = Japanese
DOT_FONTNAME = "Hiragino Kaku Gothic Pro"
部分コールグラフ

また、Doxyfileに以下の文を追加すると、全ての関数から「呼び出し/呼び出し元グラフ」が生成される。

CALL_GRAPH = YES
CALLER_GRAPH = YES
コールグラフA全体 コールグラフH全体

このくらいのサイズなら、まあ、見ていて楽しい。
しかし、プログラムがでかかったり、関数名が長い(Objectie-Cとか特に)と非常に鬱陶しいことになる。
そういう場合は、「グラフの最大深度」を設定して適度な大きさに収めたり

MAX_DOT_GRAPH_DEPTH = 3
コールグラフA深度3 コールグラフH深度3

DHTMLで任意に開閉できるようにすると、いくらか見やすくなる。

HTML_DYNAMIC_SECTIONS = YES
コールグラフ開閉

あとは、このグラフをどう役立てるかだ……

Boost.Preprocessorでタプルのシーケンスを作るには

タグ: C++ Boost

Boost.Preprocessorタプルを要素とするシーケンスを作る場合、通常はこのように書く。

((hoge, fuga))((foo, bar))

これを

(hoge, fuga)(foo, bar)

この形から生成できないものかと調べてみたら、メーリングリストにそのものずばり解法が上がっていた

#include <stdio.h>
#include <boost/preprocessor.hpp>

#define MAKE_SEQ(size, rel) MAKE_SEQ_D(size, rel)
#define MAKE_SEQ_D(size, rel) \
    BOOST_PP_CAT( \
        MAKE_SEQ_A_ ## size rel, \
        0X0 \
    )() \
    /**/

// size 2
#define MAKE_SEQ_A_2(x, y) ((x, y)) MAKE_SEQ_B_2
#define MAKE_SEQ_B_2(x, y) ((x, y)) MAKE_SEQ_A_2

#define MAKE_SEQ_A_20X0()
#define MAKE_SEQ_B_20X0()

int main()
{
	printf(
		"sequence: %s",
		BOOST_PP_STRINGIZE(
			MAKE_SEQ(2, (hoge, fuga)(foo, bar))
		)
	);
}
sequence: ((hoge, fuga)) ((foo, bar))

こういうのがさらっと思いつけばなぁ( ´∀`)

iPhone実機とシミュレータのOpenGL ESを見比べる

タグ: iPhone OpenGL

そのむかし、出立てのiPhone SimulatorはOpenGL ESをサポートしていなかった。
そのためか、SDKが2.2.1になった今でも「iPhone実機ではできるがシミュレータではできない」ことが割とよくある。
目立つところではOES_matrix_palette拡張関数とOES_draw_texture拡張関数。
これらの関数はシミュレータでは完全にスルーされるため、実機でなければ挙動を確認することはできない。

逆に「シミュレータではできるが実機ではできない」ことも存在する。
例えば、新規プロジェクト「OpenGL ES Application」の

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glLineWidth(128);
glDrawArrays(GL_LINE_STRIP, 0, 4);

こう変えた場合、シミュレータでは以下のように描かれ、

GL_LINE_STRIP 幅64ピクセル シミュレータ

実機では以下のように描かれる。

GL_LINE_STRIP 幅64ピクセル 実機

この場合、両者に違いはない。
いや、まあ。
この画像だけ見ると、実機の方が崩れているように見えるが、これは「『幅』を作る軸をXとYのどちらにするかの切り替え角が実機とシミュレータで異なる」ためにこう見えているだけで、どちらもGL_ALIASED_LINE_WIDTH_RANGEが示す最大値64(指定値128は最大値を超えているので、最大値が適用される)で描けていることに変わりはない。
実際、少し回転させるとシミュレータも実機と同じように崩れてくる。

しかし、上記のコードを

glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth(128);
glDrawArrays(GL_LINE_STRIP, 0, 4);

このように書き換えた場合、シミュレータではCore GraphicsでいうところのkCGLineCapButtでそれぞれの線分を描いたような形になるが、

GL_LINE_STRIP GL_LINE_SMOOTH 幅64ピクセル シミュレータ

実機では単に1本の線として描かれる。

GL_LINE_STRIP GL_LINE_SMOOTH 幅64ピクセル 実機

これは、実機のGL_SMOOTH_LINE_WIDTH_RANGEの最大値が1(シミュレータはGL_ALIASED_LINE_WIDTH_RANGEと同じ64)であるためで、それはすなわち実機ではGL_LINE_SMOOTHが意味を成さないことを示している。

これは結構痛い。
以前「星型にクリップした画像を貼り付ける」作業を行った時、あとでGL_LINE_SMOOTHで縁取ってアンチエイリアスを施そうと思っていたのだが、こんな状態なので諦めた。
今のiPhoneはFSAA/MSAAが効かないっぽいので、プリミティブ境界にアンチエイリアスを施すのは結構手間である。

ただ、GL_POINTSだけは、GL_SMOOTH_POINT_SIZE_RANGEが実機・シミュレータともに最大64であるため、アンチエイリアスを掛けられる。

glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPointSize(128);
glDrawArrays(GL_POINTS, 0, 4);
GL_POINTS 幅64ピクセル

そのため「直径64ピクセル以下の中抜きの円をアンチエリアス有りで描く」のは、「アンチエイリアスが施された線分を描く」より簡単だったりする。

glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
glPointSize(64);
glDrawArrays(GL_POINTS, 0, 4);
glBlendFunc(GL_ONE, GL_ONE);
glPointSize(60);
glDrawArrays(GL_POINTS, 0, 4);

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
glPointSize(64);
glDrawArrays(GL_POINTS, 0, 4);
直径62ピクセル・幅4ピクセルの円

ねんどろいどKAITO

最近テンション低いお('A`)
こういう時は何か買うのが一番だお!( ゚∀゚ )

というわけでKAITO。

KAITOとミク

横に並べるためにミクを買っておいたのだが、並べたら並べたでKAITOの方が小さく見えてしまうという。
おのれツインテール。

02月06日のココロ日記(BlogPet)

むいさんとココロがセッキンチュゥ♪セッキンチュゥ♪

*このエントリは、ブログペットのココロが書いてます♪

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。