iPhone で OpenGL のテクスチャ用に PNG 画像を読み込む

iPhone開発のヌマタメモ
2009年8月16日 03:59
20090816.png

PNG 画像は使いやすいです。サイズが若干大きめですけど、256階調のアルファマスクが付いて、ある程度の圧縮がしてあって、フルカラー。昔(15年くらい前か(遠い目))はアルファマスクだけの画像を用意したり、紫や緑といった特別な色で背景を塗りつぶして透明色を表したものですけど、もうそんなことはしなくていい。ゲーム制作にはホントにいい時代です。

さて、ゲームと言えば OpenGL。PNG 画像を読み込んで、それを元に OpenGL 用のテクスチャを生成したいと思う事は多いワケです。当然ながら iPhone SDK のために Apple が提供しているサンプルコードにこういうのがいくつかあるので、いままでそれを何も考えずに使っていたのですよ。ちなみに、メモリ上に Core Graphics でビットマップ画像を用意して、UIImage から取り出した CGImage をそこに描画して、RGBA の配列になったところでデータを読み込む、という流れ。

ところがこれ、アルファ値をもつ PNG 画像を読み込むときに問題があることが分かりました。とくに、かなり傾斜の緩いグラデーションがあると明確におかしくなります。

たとえば背景が透明なこんな画像を用意します。

 texture_transparent.png

これを Apple のサンプルコードの方法で読み込んでテクスチャを作り、水色の背景の上に重ねて描画すると、こうなるのですよ。

 texture_out.png

「あれ、こんなもんかな? 暗く見えるのは目の錯覚?」と思ってしまう感じなのですが、実際に透明部分が暗くなっているのです。つまり、本来ならR・G・Bのプレーンは完全な白になっていて、アルファプレーンのみがグラデーションしているのですが、Apple の方法だとアルファプレーンの値がR・G・Bにまで影響を及ぼして、グラデーション部分が黒になってしまうのですね。というかこれ、Apple の画像読み込みルーチンを使った時点で起きてしまう問題なので、単純には解決できないのですよ。

対処方法がないかと思って検索してみて、見つけたのがこちらのページ。stb_image.c という Public Domain な PNG 画像読み込みプログラムを使って読み込むようにする、というもの。

なるほど、ということで、それを使ってテクスチャを読み込んで表示するサンプルを作ってみました。ちなみに上記のページではファイルパスに日本語が含まれていると問題があると書かれていますが、Cocoa で画像ファイルをメモリに読み込んでしまってから使えば、問題なく読み込めるようになります。また2の乗数でないサイズの画像に対応するために、ちょっと拡張して使っています(いちばん近い2の乗数にサイズを拡大してテクスチャを生成しておいて、必要な部分だけを切り取って表示するようにしています)。そこそこちゃんとクラスにまとめているので、使ってみてください。

Apple は、こういう機構をもっと低レベルで用意してくれてもいいと思うんですけどね。

 iphone_transparent_texture.png

プログラムのダウンロード:GitHub - PNG Texture Loader for OpenGL

[9/12 15:45 追記] コメントで「開発人」さんに「やっぱりおかしい」というご連絡をいただき、調べてみたところ、私のテスト不足で、最適化された PNG 画像はアルファ値が RGB の値に影響を及ぼしているということが判明しました(「開発人」さん、ありがとうございました)。ということで、若干の対応を入れたバージョンを GitHub に上げてみました(version-1.1.1 です)。RGB の値がアルファ値を掛けたものになっているなら、割ってやれば元に戻るだろう、という理屈です。100%完璧な対処ではないので、素直に「IPHONE_OPTIMIZE_OPTIONS=-skip-PNGs」で最適化を切る方がいいのかも知れませんが… (__;。

さらに、stb_image.c からのコードを取り除いたバージョンが 2.0 です。アルファ値で割るやり方で統一すれば、stb_image.c はそもそも要らないし、最適化云々は関係なくなるだろう、と。

プログラムのダウンロード:TextureLoadingSample_20090816_ex.zip (Apple による最適化PNG画像対応版)

[21:48 追記] 最適化が行われた PNG 画像でも読み込めるようにしました。Apple の最適化 PNG 画像って、要は、RGBA のデータを BGRA にして、それがあることを示すチャンク (CgBI) を付け足して、ZLIB圧縮のヘッダを取り除いたモノなんですね。CgBI チャンクが来たときだけ ZLIB 圧縮のヘッダチェックをかけないようにして、GL_RGBA の代わりに GL_BGRA で読み込むようにしたらすんなり読み込めるようになりました。

プログラムのダウンロード:TextureLoadingSample_20090816.zip

ただしプログラムを iPhone 実機にインストールするときには、PNG 画像に対して Apple 独自の最適化が行われるので、この方法では読み込めなくなってしまいます。これに対しては、プロジェクト設定に「IPHONE_OPTIMIZE_OPTIONS」という名前のユーザ定義の設定を追加して、「-skip-PNGs」という値を設定すれば PNG 画像の最適化は行われなくなります。なお、読み込みに失敗した場合には、いちおう警告メッセージだけ出力して、Apple の方法で読み込むようにしています。

最適化とは言っても、サイズ的にはほとんど変わらないし、だいたいの場合は元のファイルよりも少し大きくなります。最適化すると読み込みが速いと言っても、Apple の方法では実際にはメモリ上のビットマップに描画し直して、そこからテクスチャを生成しているので、結局同じ程度の速度になるでしょう。ゲームの場合、大抵はBGMや効果音などの読み込み時間がそれ以上にかかりますから、ぜんぜん問題ないはず。

5件のコメントがあります。

user-pic 開発人

テンシーシーさんのほうから来ましたw
沼田さんのソースを頂き、ムニャムニャ実験してみました。
最適化が行なわれたpngファイルを読み込むことはできるのですが、アルファ値がおかしくなるようです。
(上のエントリで書いてある、UIImage経由と同じ状況になる)
プロジェクト設定で最適化をOFFにすると、アルファもきれいに読めこめますし、ソースを追ってみても怪しそうなところも見当たらないので
「そもそもpng最適化の際にアルファがおかしくなってる」
ような気がします。間違いだったらごめんなさいなんですけどなんというかアップル…

高速化に関してはもしかすると
glTexImage2D で GL_BGRA で作ったテクスチャだと
何かが速いのかもしれません。ごめんなさい実験してないんですけど。

user-pic 沼田

ご報告ありがとうございます。

>「そもそもpng最適化の際にアルファがおかしくなってる」

う〜ん、さすがにそれはないと思うんですが…。
とりあえず、私の手元の Mac OS X 10.5 + iPhone SDK 3.0 の環境ではとくに問題が起こっていません。

もしよろしければ、お使いの iPhone SDK のバージョンと Mac OS X のバージョン、それからXcode を上書きしている場合には Xcode のバージョンをお教えいただけませんでしょうか。問題が再現するサンプルコードなどもいただければ有り難く存じます。

今後、追加でテストしていくときの参考にさせていただければと思います。
どうぞよろしくお願いいたします。

user-pic 開発人

開発環境としては
Mac OS X 10.5.8
Xcode 3.1.3
iPhone SDK 3.0
ですね。新OSにはしていません。
沼田さんの作られたサンプルを、そのまま実機で実行すると(実機での実行のためにinfo.plistのbundle~~は書き換えています)やっぱりwhite_ball.pngが黒ずみます。

シミュレータだと大丈夫なように見えますが、実機でおかしいのですね。
ちょっと時間が無くて更なる深追いが出来ていなくて申し訳ないのですが、ご報告までに。

user-pic 沼田

ご報告ありがとうございました。

いろいろとテストしてみたところ、
http://iphonedevelopment.blogspot.com/2008/10/iphone-optimized-pngs.html
にも書かれている通り、たしかに「そもそもpng最適化の際にアルファがおかしくなってる」ようです (__;。以前私がテストしたときには、最適化されていない状態のコピーが残っていたのだろうと思います。大変失礼いたしました m(__)m。

しかし何がしたいのか意味不明です>Apple。と言いますか、たぶん内部的にも CoreGraphics 経由で同じことをやっているのだろうと思います。

ということで、アルファ値が掛けられているだけなら割って戻せばいいのでは?と、そのサポートを加えてみました (version-1.1.1)。もちろん完全に戻せる訳ではないのですが、戻しにくい場合=アルファ値の低い場合(ほぼ透明)なので、視覚的には問題がないかなと思っています。

ちなみに、これなら Apple の読み込み方に手を加えればいいという説もありますので、version-2.0 で Apple の読み込み方だけに戻して、さらに PVRTC テクスチャの読み込みをサポートしました。

http://github.com/sazameki/opengl-texture-loader/downloads

にアップしています。ぜひお試しいただき、ご意見いただければ幸いです。

よろしくお願いいたします。

user-pic tos

はじめまして、ソフトウエアデザイン誌(2010.7)経由で訪れました。
Apple のソースに、沼田さんのアルファー値で割る部分を追加して試しています。

私の場合は、周辺部がアルファーグラデーションではない画像を使用していますが、やや変則的な使用のせいもあって暗くなる現象が生じています。技術的にあまり詳しく有りませんが、今回、沼田さんの version-2.0の割り算部分のソースを Apple のソースに単純に追加することでこの現象はクリアされました。まだ開発途中ですが多くのヒントをありがとうございました。

アップルのコード:Texture2D (Version: 1.7)
開発環境:Mac OS X 10.6.4, Xcode 3.2.4, iOS SDK 4.1

コメントを書く


トラックバックはありません。

トラックバックURL: http://numata.designed.jp/mt-tb.cgi/335