頭と尻尾はくれてやる!

iOSアプリなどの開発日記です


OpenGLのRetina対応

GLKitを使っているとさほど気にしないのかもしれないけど、GLKitを使わない場合でRetina対応するお話。
以下の三つでキレイになったよ。
{
    eaglLayer.contentsScale = [UIScreen mainScreen].scale;
}
↑画像を出力するのに設定してるCAEAGLLayerオブジェクトのcontentsScaleをセット。CALayerオブジェクトに対しても同じことやってきたよね。
{
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, self.frame.size.width*2.0, self.frame.size.height*2.0);
}
↑デプスバッファを設定するところでサイズを倍にする。
{
    glViewport(0, 0, self.frame.size.width*2.0, self.frame.size.height*2.0);
}
↑表示するサイズも倍にしとく。


UIImageから作ったテクスチャが上下反転する

UIImageをOpenGLのテクスチャに使う
↑ここでUIImageをOpenGLのテクスチャに使う方法を書いてるんだけど、最近不具合があることに気が付いたってお話。
上のを書いた時には上下対称のテクスチャだったので気が付かなかったんだけど、上下が非対称の画像を使うところがあったのよ。
そのテクスチャを使う場面でどういうわけか画像が上下反転するんだ。
{
    UIImage *textureImage = [UIImage imageNamed:@“image.png”];
}
テクスチャを作る時には何気なく普段通りに↑こんな感じで読み込んでる。
そう言えば、このimageNamed:メソッドはキャッシュされるんだよね。
以前、GLKitを使わない場合にテクスチャを設定する時に座標軸の関係から上下を反転させる必要があった。
これらのことからすると、テクスチャ設定時にシステム側がキャッシュした元画像を上下反転してるんだろうか?
そんなことあるんかいな?って思うけど試しにキャッシュを避けてみる。リファレンスにはキャッシュされたくなければ
imageWithContentsOfFile:
を使えとあるからこれを使ってみたら、問題解決。
ちなみにこちらのメソッドだとファイル名にRetina用画像を示す”@2x"まで書かないといけないのが微妙なんだけどね。


なばなの里へはタクシーは来てくれない

三重県への家族旅行ネタの続き。

なばなの里オフィシャルサイトTOP
↑「なばなの里」ってところにビール園があって地ビールが飲めるってんで車をホテルに置いてタクシーで行き来しようかと思ったのよ。
行きはホテルでタクシー呼んでもらった。
ホテルに置いてたパンフに紹介されていた「全国タクシー配車」ってアプリもダウンロードしといたので帰りも安心。
タクシーでなばなの里まで。

長島ビール園

↑お目当ての長島ビール園へ。ビールや食事を堪能。ピルスナー美味しかったよ〜

なばなの里の鏡池

↑園内は散歩してもいろいろあって楽しい。足湯も楽しんだ。

ここまではいいんだよ。

帰りのタクシーを呼ぼうと配車アプリを使うもいくらやっても配車できるタクシーがない、と。タクシー会社が二社ほどあったけどどちらもいくらやってもダメ。
仕方ないから調べのついたタクシー会社3、4社に電話したけど、どこも「なばなの里」と言ったとたんに「すぐに配車はできない」って言うのよ。少し市の中心部から離れているから儲からないからやんわり乗車拒否してるんだろうかね。

結局、幸運なことに最終のバスがまだ残っていたのでそれで桑名駅まで行ってホテルに帰り着いたよ。

観光地だからタクシーはいくらでもつかまるという考えは甘い、ってことみたい。覚えておこうっと。


色じゃない値をフラグメントシェーダで出力したい

OpenGLのシャドウマッピング
↑これの追記。
8bitでの出力じゃなくて16bitなり32bitで出力する方法を調べていたんだけど、やっぱりないのかな?

{
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,  outputLength, outputLength, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, g_texIDs[1], 0);
}
FBO(フレームバッファオブジェクト)のテクスチャへの出力を上のようにGL_DEPTH_COMPONENTを指定しGL_UNSIGNED_INT(32bitのはずだけどここは無効の記述もある)で32bitの値を出力するんだあ!と設定。直後のフレームバッファのチェック
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
もOKなんだけど、、、
さて、シェーダ側で出力ってどうすんだろ?
フラグメントシェーダでのアウトプットは
gl_FragColor = vec4(r,g,b,a);
みたいなのじゃない?どうやってunsigned intを一つ出力するのよ〜?!っていろいろ調べたんだけど、、、わかんなかったわ。

OpenGL ES 3.0だと影用のなんぞある(今は2.0でやってる)みたいなのを見たり、どうせそのうちMETALやるだろうし、とか思うとどうもやる気がねえ、、、深みにはまる前にここは速やかに撤退しとくわ。ううう。


OpenGLのシャドウマッピング

OpenGLで影を付けてみようってお話。


↑とりあえずこんな感じになった。一番最初は光源と同じ角度で見てて、その後ジェスチャー(パン)で視点を動かしてる。デバイスはiPhone 5s、iOS 7.1.2
テクスチャのサイズは1024x1024でやってる。
それにしても、、、汚いね。
実際に自分でやってみるとキレイで自然な影っていろいろと難しいんだなとわかったよ。おかげで最近は3DのCGでキレイな影を見るとおおお〜!美しい〜!と見入ってしまうことがあるんだ。

影を付ける手法はデプスバッファシャドウとか言うそうな(いろいろあるみたい)。
wgld.org | WebGL: シャドウマッピング |
↑原理的にはこのサイトがとても参考になったよ。

処理の流れは
光源から見てレンダリングして深度情報をテクスチャに書き出す(もちろん表示しない=オフスクリーンレンダリング)。
カメラから見たシーンを描画、この時先ほどの深度情報と比較して影になるか否かを判断する。
とまあどこでも書いてるような記述だけど、いざ自分で実装しようとするとえらく苦労したよ。

はまったところをいくつか書いておこうっと、、、はまった、というか今でもよくわからなかったりするんだけどさ。

そもそも深度情報をどうやって得るの?って話なんだけど、glTexImage2Dでテクスチャに出力するのはいいとして、何を出力するのかよくわかんない。
GL_UNSIGNED_BYTEでGL_DEPTH_COMPONENTを出力する!って記述をよく見るんだけど、何がよくないのか俺コードではフレームバッファのチェックでエラーがでちゃう。
いや、そもそもGL_DEPTH_COMPONENTで深度が書き込まれるというならシェーダで何するのよ?

結局、テクスチャへの出力は
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  outputLength, outputLength, 0, GL_RGBA,GL_UNSIGNED_BYTE, NULL);
こんな感じでGL_RGBAの値(4バイトも要らないんだけど)を書き込めるようにしといて、シェーダで深度情報(0から1)を計算して出力するようにした。なんか違うような気がするんだけどねえ。


表示するピクセルの座標の変換は最初は難しそうだな、と思っていたけどやってみるとどうということでもなくて、ただ面倒なだけ。頂点シェーダからもらった座標に対してmodel view matrixとprojection matrixの逆行列でローカル座標に戻して、さらに光源から見た場合のmodel view matrixとprojection matrixをかけてやればいい、というだけ。
逆行列はGLKitのを使ってこんな感じで。
{
    bool invertible;
    GLKMatrix4 invModelViewMatrix = GLKMatrix4Invert(modelViewMatrix , &invertible);
    if(!invertible) {
       NSLog(@"modelViewMatrix is not invertible”);
    }
}
以外に難しいのが表示するピクセルが一番光源に近いのか?って判定をどうするか?って問題。
シェーダ内で光源から見た場合の深度値を計算して、テクスチャに記録されてる一番小さい深度値と比較するんだけど、highp float の比較なので丸められる分を考慮する必要もあり(うまい方法もありそうだけど)、それが適切でないとすごく不自然になったり汚くなってしまう。深度値が8バイト(256階調)ってのが問題なんだろうな。

いろいろと修正する余地がありまくりなんだけど、今回はこの辺で。







Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.