なにやらあらぬ誤解が広まっている(?)ようなので書いてみる。
何かというと、iPhoneではアルファブレンディングがおかしいんじゃないか、という話。
もちろん全くそんなことはないんですが、恐らく全ての元凶はこれ。
Appleのサンプルでよく使ってるビットマップを取得する処理。一部長いので省略。
iPhoneはPNGをリソースに入れると勝手に最適化されてRGBa8888ではなくなったりする。
そこでオフスクリーンバッファに描画することで、手軽にリサイズもできて適当なビットマップをゲッツ!というとても便利な方法。
ただひとつ注意すべき点があって、このコンテキストは kCGImageAlphaPremultipliedLast だということ。
つまりこれで取得できるビットマップは既にカラーにアルファが乗算された状態になっている。
なので、glBlendFuncは(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)ではなくて(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)を使う。
超端折って説明すると、背景がDSTでカラーをCd、アルファをAdとする。
そして今描こうとしてるポリゴンがSRCでカラーをCs、アルファをAsとする。
そうすると、アルファブレンディングの結果は Cs*As + Cd*(1.0 - As) にしたい。
変換したカラーをCs'とするとPremultipliedなので Cs' = Cs*As。
従って Cs' + Cd*(1.0 - As) になればOK。
よってBlendFuncは(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)になる。
で、これだとアルファを指定してフェードとかがうまくできないよ、ということになるわけだがちょっと工夫すれば良い。
描画するときにブレンドの指定にあわせてカラーの指定を少しかえてやる。例えば、
というようにPremultipliedになるようにカラーにアルファをかける。
これでいわゆるアルファブレンディングと同じようになるはず。
ちなみによくあるようにglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)でやりたい場合はPNGをやめれば確実。
TIFFとかPVRとかから直接ビットマップを読み込んでやればPremultipliedにならない。
そのへんはAppleのPVRTextureLoaderとか木下氏のマイコムの連載のタワーディフェンスの作り方が参考になる。
という訳で駆け足でしたが以上で、PowerVRは悪くないッ!
たかが携帯電話ごときにFBOがついてる上に、最新型にいたってはGLSLまで使わせてくれるGPUが悪い子な訳がないッ!!
というお話でした。
何かというと、iPhoneではアルファブレンディングがおかしいんじゃないか、という話。
もちろん全くそんなことはないんですが、恐らく全ての元凶はこれ。
image = [UIImage imageNamed:@"Sprite.png"].CGImage; width = CGImageGetWidth(image); height = CGImageGetHeight(image); data = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte)); context = CGBitmapContextCreate(data, ..., kCGImageAlphaPremultipliedLast); CGContextDrawImage(context, ...); CGContextRelease(context); ... glTexImage2D(..., data);
Appleのサンプルでよく使ってるビットマップを取得する処理。一部長いので省略。
iPhoneはPNGをリソースに入れると勝手に最適化されてRGBa8888ではなくなったりする。
そこでオフスクリーンバッファに描画することで、手軽にリサイズもできて適当なビットマップをゲッツ!というとても便利な方法。
ただひとつ注意すべき点があって、このコンテキストは kCGImageAlphaPremultipliedLast だということ。
つまりこれで取得できるビットマップは既にカラーにアルファが乗算された状態になっている。
なので、glBlendFuncは(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)ではなくて(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)を使う。
超端折って説明すると、背景がDSTでカラーをCd、アルファをAdとする。
そして今描こうとしてるポリゴンがSRCでカラーをCs、アルファをAsとする。
そうすると、アルファブレンディングの結果は Cs*As + Cd*(1.0 - As) にしたい。
変換したカラーをCs'とするとPremultipliedなので Cs' = Cs*As。
従って Cs' + Cd*(1.0 - As) になればOK。
よってBlendFuncは(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)になる。
で、これだとアルファを指定してフェードとかがうまくできないよ、ということになるわけだがちょっと工夫すれば良い。
描画するときにブレンドの指定にあわせてカラーの指定を少しかえてやる。例えば、
glColor4f( red * alpha, green * alpha, blue * alpha, alpha ); glDrawArray(...);
というようにPremultipliedになるようにカラーにアルファをかける。
これでいわゆるアルファブレンディングと同じようになるはず。
ちなみによくあるようにglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)でやりたい場合はPNGをやめれば確実。
TIFFとかPVRとかから直接ビットマップを読み込んでやればPremultipliedにならない。
そのへんはAppleのPVRTextureLoaderとか木下氏のマイコムの連載のタワーディフェンスの作り方が参考になる。
という訳で駆け足でしたが以上で、PowerVRは悪くないッ!
たかが携帯電話ごときにFBOがついてる上に、最新型にいたってはGLSLまで使わせてくれるGPUが悪い子な訳がないッ!!
というお話でした。
詳細な解説ありがとうございました!
大変参考になりましたし、長いあいだ悩んでた問題が解決しました。
しかし寝る前に勢いに任せて書いたので誤字や変な言い回しが多くて恥ずかしいですね。
あとで少し直します。
丁度悩んでたタイミングなので、本当にありがとうございました!
またステキなアプリができるのを祈ってますw