(。・ω・)ノ゙ コンチャ♪
久々の書き込みでーっす!iPhoneアプリで久々に手こずりまくったんで、メモっときます‥
今回は、
「iPhone アプリで OpenGLと加速度センサーかジャイロを用いて
OpenGLで描かれたオブジェクトを、端末の動きに合わせて回転させる!」
なんかこーゆーの‥
意外と、ブログだったり、ドキュメントないんだよね‥
みたいなものを作りたく、あーでもねー!!こーでもねー!!っとやってきましたが、
やっとそれなりのもんができましたとっ!
(*゚▽゚)/゚・:*【祝】*:・゚\(゚▽゚*)
じゃーその方法ですが、まずはOpenGLのお話からどーすお!
- OpenGL -
iPhoneSDKが新しくなってから??Xcodeのバージョンが新しくなってから??
どっちでもいいですが、OpenGL ES で新規でプロジェクト始めると、
よくある参考書などと内容が異なっていて、使えねぇー!!!!
っと思いきや、じつは対して変わってないのですお!!
前の バージョンで言うところの
- ES1Renderer.mのinitメソッド部分は、
"自分でつけたプロジェクト名ViewController.m"のawakeFromNibで、
- ES1Renderer.mのrenderメソッドは、
"自分でつけたプロジェクト名ViewController.m"のdrawFrameで、
って感じです(ノ゚ρ゚)ノ ォォォ・・ォ・・ォ・・・・
じゃーとりあえず、デフォルトのプロジェクトだと、
オブジェクトが激しい上下運動しててうざいんで、止めましょう!
止め方
- "プロジェクト名"ViewController.mのdrawFrameの中に、
glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
transY += 0.075f;
っていうのがあって、これをコメントアウトする
そーすっと止まる。
とりあえず、コードの流れは、デリゲードが呼ばれて、ぐじゃぐじやって、
awakeFromNibに入って、コンテキスト作ったり(OpenGLで必要な事)して、
同時にStartAnimationが呼ばれると、リンク(CADisplayLinkとかいう‥)でdrawFrame
が呼ばれ続け、呼ばれたらさっき消したコードで、上下運動し続けるっていう仕組みっす。
そーすっと止まる。
とりあえず、コードの流れは、デリゲードが呼ばれて、ぐじゃぐじやって、
awakeFromNibに入って、コンテキスト作ったり(OpenGLで必要な事)して、
同時にStartAnimationが呼ばれると、リンク(CADisplayLinkとかいう‥)でdrawFrame
が呼ばれ続け、呼ばれたらさっき消したコードで、上下運動し続けるっていう仕組みっす。
さっきのコードが上限運動するコードなんで‥大雑把ですが‥
で、とりあえず、静止したオブジェクトが表示されていれば、
ココまではOKですお∑d(≧▽≦*)OK!!
おし!次!
- 加速度センサーやジャイロ -
- 加速度センサー
まず加速度センサーとはなんですか‥ヾ(・・;)ォィォィ
‥‥簡単に言うと、iPhone の傾きがどの軸に対して傾いているか的な!?
そういう事です‥ココ見てください→http://japan.internet.com/developer/20100803/26.html
で、これは、UIAccelerometerって言うもので、これを使うのもよし!
これならば、iPhone3系にも対応しています。
じゃーこれと、OpenGLどー絡めんの??って話ですが、
絡めましたが、うまいこといかず却下!!!
却下なんで、ソースコード載せなくていーっすか‥
じゃー次!
- CoreMotion
iPhone4からこーいーのが使えるんですお!
しかも、少々ややこしいのですが、CoreMotionの中にも3種類あります。
1. Device Motion
2. Accelerometer
3. Gyro
っていう3種類です。2番目は、UIAccelerometerと同じでしょー。
で、1のDeviceMotionの中にも4つ種類があってですね‥
1-1. CMAttribute デバイスの向き
→ なんかGyroっぽいんですが、おそらくこんな感じ
motion.attribute.pitch X軸の回転角度
motion.attribute.roll Y軸の回転角度
motion.attribute.yaw Z軸の回転角度
1-2. CMRotationRate
→ 使った事ないんで何とも‥名前的に、回転速度じゃね?
1-3. CMAcceleration gravity
→ 重力速度だって‥ってことは、地球じゃ必ず1じゃねーの?あ、誤差で0.98になるんだって‥はい!必要なし!
1-4. CMAcceleration userAcceleration
→ UIAccelerometerと同じじゃね??
3は回転の速度なので、、、2のAccelerometerと、3-1のattributeを使ってみーよお
- CoreMotion Accelerometer
じゃーまず来れから!
startAnimation メソッドで
if (!animating) {
motionManager = [[CMMotionManager alloc] init];
if(motionManager.accelerometerAvailable) {
// これは.hでCGFloatで定義しておいてくらはい
motionManager.accelerometerUpdateInterval = 1.0f / 5.0f;
CMAccelerometerHandler accelerometerHandler;
accelerometerHandler = ^ (CMAccelerometerData *accelerometerData, NSError* error) {
if(error) {
}
else {
/*
if (accelerometerData.acceleration.x > power){
NSLog(@"Accelerometer x:->");
}
if (accelerometerData.acceleration.x < -1*power){
NSLog(@"Accelerometer x:<-");
}
if (accelerometerData.acceleration.y > power){
NSLog(@"Accelerometer y : ↑");
}
if (accelerometerData.acceleration.y < -1*power){
NSLog(@"Accelerometer y : ↓");
}
if (accelerometerData.acceleration.z > power){
NSLog(@"Accelerometer z : 手前");
}
if (accelerometerData.acceleration.z < -1*power){
NSLog(@"Accelerometer z : 奥");
}*/
// これは.hでCGFloatで定義しておいてくらはい
accelX = accelerometerData.acceleration.x;
accelY = accelerometerData.acceleration.y;
accelZ = accelerometerData.acceleration.z;
}
};
// 向きの更新通知を開始する
[motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler:accelerometerHandler];
}
CADisplayLink *aDisplayLink = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(drawFrame)];
[aDisplayLink setFrameInterval:animationFrameInterval];
[aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
self.displayLink = aDisplayLink;
animating = TRUE;
}
accelX accelY accelZの3つの値は.hで、定義してくださーい‥
で、この数値を使って、OpenGLのオブジェクトをうまく回転させる事ができれば、
デバイスの動きと、オブジェクトが連動して動かせるという理屈ですが‥いかんせん!
うまくいかん_!!!(`(エ)´)ノ_彡☆ブーブー!!
(  ̄っ ̄)ムゥ というか、加速度でやってて気がついてしまった!!!
できなくね‥??
あーできねーさ、、なぜならこの図をみてくれぇ!
ちょっとややこしいんですが、三角関数の計算とか出てきちゃうけど‥
X軸の回転は、iPhoneを図のように向きにして、手前、奥に傾ける(回転)ことにより、
その加速度センサーから、動いた角度を三角関数にならって求める事ができる‥
Z軸の回転は、今度は、図で言うと、左右に傾ける(回転)事によって求める事が可能
しかーし!Y軸って‥
良ーく考えると、このように図の向きで持ったときに、Y軸を中心にまわすと、
iPhoneをY軸の矢印のようにまわしたところで、加速度センサーって反応しないんじゃん。
加速度センサーは、重力の方向に(つまり下)働いてる力を取るんで、加速度センサーは、
Y軸に、-1のまま変わらないんです‥つまり垂直方向の回転は、とれないということになる‥
なんで、、、却下!!!ボツ!ハイ次!!!!
- CoreMotion DiviceMotion -
よし!これが最終手段であります‥これでできなかったらわからん‥みんなやってるけど‥
とりあえず、ソース
さっきのstartAnimationを変更です‥
Accelerometerの所を下記に
// 1 Device Motion.
if (motionManager.deviceMotionAvailable) {
// 更新の間隔を設定する
motionManager.deviceMotionUpdateInterval = 1.0f / 5.0f;
CMDeviceMotionHandler deviceMotionHandler;
deviceMotionHandler = ^ (CMDeviceMotion* motion, NSError* error) {
// デバイスの向きを表示する
/*
if (motion.attitude.pitch > power){
NSLog(@"x:->");
}
if (motion.attitude.pitch < -1*power){
NSLog(@"x:<-");
}
if (motion.attitude.yaw > power){
NSLog(@"y : ↑");
}
if (motion.attitude.yaw < -1*power){
NSLog(@"y : ↓");
}
if (motion.attitude.roll > power){
NSLog(@"z : 手前");
}
if (motion.attitude.roll < -1*power){
NSLog(@"z : 奥");
}*/
gyroX = motion.attitude.roll;
gyroY = motion.attitude.pitch;
gyroZ = motion.attitude.yaw;
NSLog(@"X %f Y %f Z %f", gyroX, gyroY, gyroZ);
};
// 向きの更新通知を開始する
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler:deviceMotionHandler];
}
よーし、これで、drawFrameをこんなん
- (void)drawFrame
{
[(EAGLView *)self.view setFramebuffer];
// Replace the implementation of this method to do your own custom drawing.
static const GLfloat squareVertices[] = {
-0.5f, -0.5,
0.5f, -0.5f,
-0.5f, 0.5f,
0.5f, 0.5f,
};
static const GLubyte squareColors[] = {
255, 255, 0, 255,
0, 255, 255, 255,
0, 0, 0, 0,
255, 0, 255, 255,
};
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f); // もともとは平行投影。なので最近、と最遠の設定に意味はない。
glFrustumf(-1.0f, 1.0f, -1.5, 1.5, 3.0f, 100.0f); // 透視投影。最遠はともかく最近は
glTranslatef(0.0f, 0.0f, -3.5f);
glMatrixMode(GL_MODELVIEW);
glRotatef(gyroX, 1.0f, 0, 0);
glRotatef(gyroY, 0, 1.0f, 0);
glRotatef(gyroZ, 0, 0, 1.0f);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
OpenGLの行列の各軸の回転の定義はこーっすよ
[X軸回転]
[ 1, 0, 0, 0]
[ 0, cos(angle), -sin(angle), 0]
[ 0, sin(angle), cos(angle), 0]
[ 0, 0, 0, 1]
[Y軸回転]
[cos(angle), 0, sin(angle), 0]
[0, 1, 0, 0]
[-sin(angle), 0, cos(angle), 0]
[0, 0, 0, 1]
[Z軸回転]
[cos(angle), -sin(angle), 0, 0]
[sin(angle), cos(angle), 0, 0]
[ 0, 0, 1, 0]
[ 0, 0, 0, 1]
[ 1, 0, 0, 0]
[ 0, cos(angle), -sin(angle), 0]
[ 0, sin(angle), cos(angle), 0]
[ 0, 0, 0, 1]
[Y軸回転]
[cos(angle), 0, sin(angle), 0]
[0, 1, 0, 0]
[-sin(angle), 0, cos(angle), 0]
[0, 0, 0, 1]
[Z軸回転]
[cos(angle), -sin(angle), 0, 0]
[sin(angle), cos(angle), 0, 0]
[ 0, 0, 1, 0]
[ 0, 0, 0, 1]
行列を使うには、sinとcosの値が必要なり
忘れた人は、Google先生に聞いてください‥
ここの最初の図のように、
http://www8.plala.or.jp/ap2/suugaku/sankakukansuunoshoho.html
角度がわかってるので、sinとcosはそのまま使えます‥
なので、X軸の回転の場合は、sin(gyroX)、cos(gyroX)とかで、いいんですねぇ〜
これを応用して、3つの軸ごとに4x4の行列を作りますよー
そしてそれを、乗算します!!
行列の乗算って確かかなりめんどくさー!!だからソースこんなんなっちゃました!!!
drawFrameを下記に修正
- (void)drawFrame
{
[(EAGLView *)self.view setFramebuffer];
// Replace the implementation of this method to do your own custom drawing.
static const GLfloat squareVertices[] = {
-0.5f, -0.5,
0.5f, -0.5f,
-0.5f, 0.5f,
0.5f, 0.5f,
};
static const GLubyte squareColors[] = {
255, 255, 0, 255,
0, 255, 255, 255,
0, 0, 0, 0,
255, 0, 255, 255,
};
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f); // もともとは平行投影。なので最近、と最遠の設定に意味はない。
glFrustumf(-1.0f, 1.0f, -1.5, 1.5, 3.0f, 100.0f); // 透視投影。最遠はともかく最近は
glTranslatef(0.0f, 0.0f, -3.5f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
const float X[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, cos(gyroX), -sin(gyroX), 0.0f,
0.0f, sin(gyroX), cos(gyroX), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
const float Y[16] = {
cos(gyroY), 0.0f, sin(gyroY), 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-sin(gyroY), 0.0f, cos(gyroY), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
const float Z[16] = {
cos(gyroZ), -sin(gyroZ), 0.0f, 0.0f,
sin(gyroZ), cos(gyroZ), 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
// 3つの行列を乗算
const float XY[16] = {
X[0]*Y[0]+X[1]*Y[4]+X[2]*Y[8]+X[3]*Y[12], X[0]*Y[1]+X[1]*Y[5]+X[2]*Y[9]+X[3]*Y[13], X[0]*Y[2]+X[1]*Y[6]+X[2]*Y[10]+X[3]*Y[14], X[0]*Y[3]+X[1]*Y[7]+X[2]*Y[11]+X[3]*Y[15],
X[4]*Y[0]+X[5]*Y[4]+X[6]*Y[8]+X[7]*Y[12], X[4]*Y[1]+X[5]*Y[5]+X[6]*Y[9]+X[7]*Y[13], X[4]*Y[2]+X[5]*Y[6]+X[6]*Y[10]+X[7]*Y[14], X[4]*Y[3]+X[5]*Y[7]+X[6]*Y[11]+X[7]*Y[15],
X[8]*Y[0]+X[9]*Y[4]+X[10]*Y[8]+X[11]*Y[12], X[8]*Y[1]+X[9]*Y[5]+X[10]*Y[9]+X[11]*Y[13], X[8]*Y[2]+X[9]*Y[6]+X[10]*Y[10]+X[11]*Y[14], X[8]*Y[3]+X[9]*Y[7]+X[10]*Y[11]+X[11]*Y[15],
X[12]*Y[0]+X[13]*Y[4]+X[14]*Y[8]+X[15]*Y[12], X[12]*Y[1]+X[13]*Y[5]+X[14]*Y[9]+X[15]*Y[13], X[12]*Y[2]+X[13]*Y[6]+X[14]*Y[10]+X[15]*Y[14], X[12]*Y[3]+X[13]*Y[7]+X[14]*Y[11]+X[15]*Y[15],
};
const float scaleMatrix[16] = {
XY[0]*Z[0]+XY[1]*Z[4]+XY[2]*Z[8]+XY[3]*Z[12], XY[0]*Z[1]+XY[1]*Z[5]+XY[2]*Z[9]+XY[3]*Z[13], XY[0]*Z[2]+XY[1]*Z[6]+XY[2]*Z[10]+XY[3]*Z[14], XY[0]*Z[3]+XY[1]*Z[7]+XY[2]*Z[11]+XY[3]*Z[15],
XY[4]*Z[0]+XY[5]*Z[4]+XY[6]*Z[8]+XY[7]*Z[12], XY[4]*Z[1]+XY[5]*Z[5]+XY[6]*Z[9]+XY[7]*Z[13], XY[4]*Z[2]+XY[5]*Z[6]+XY[6]*Z[10]+XY[7]*Z[14], XY[4]*Z[3]+XY[5]*Z[7]+XY[6]*Z[11]+XY[7]*Z[15],
XY[8]*Z[0]+XY[9]*Z[4]+XY[10]*Z[8]+XY[11]*Z[12], XY[8]*Z[1]+XY[9]*Z[5]+XY[10]*Z[9]+XY[11]*Z[13], XY[8]*Z[2]+XY[9]*Z[6]+XY[10]*Z[10]+XY[11]*Z[14], XY[8]*Z[3]+XY[9]*Z[7]+XY[10]*Z[11]+XY[11]*Z[15],
XY[12]*Z[0]+XY[13]*Z[4]+XY[14]*Z[8]+XY[15]*Z[12], XY[12]*Z[1]+XY[13]*Z[5]+XY[14]*Z[9]+XY[15]*Z[13], XY[12]*Z[2]+XY[13]*Z[6]+XY[14]*Z[10]+XY[15]*Z[14], XY[12]*Z[3]+XY[13]*Z[7]+XY[14]*Z[11]+XY[15]*Z[15],
};
glLoadMatrixf(scaleMatrix);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glPopMatrix();
[(EAGLView *)self.view presentFramebuffer];
}
4x4x4の行列の計算はまじパネェーっす(* ̄o ̄)ゝ
あい!!これでできたー!
ばっちり。でもこの方法であってるのかは、なぞ!
何か質問などあったらおねがいしやす‥
あ、回転軸と、面、角度やジャイロについてまとめたものです。
何かの参考にどうぞ
0 件のコメント:
コメントを投稿