以下にこのページで説明するコードを示す。
フルセットには、以下に示す5つのプロジェクトが入っている。
Alphablend1 | 半透明処理に問題がある例。 |
Alphablend2 | Alphablend1 の問題を解消した例。 |
この節で説明するコード
OpenGLを利用して半透明な面を描画するためにはアルファブレンディングという処理を利用しなければならない。 アルファブレンディングとは「既に描かれている絵に対して」、これから描こうとしている絵を混ぜ合わせる処理のことである。 何対何で掛け合わせるかを表す数値のことをアルファ値という。 関連するコードとして、Alphablend1/main.cppの一部を以下に示す。
Material4 の第4引数がアルファ値である。 アルファ値は、1.0 が不透明*1で 0.5 が半透明*2、 0.0 が透明*3である。 事例では 1.0 や 0.5 等の切りの良い数値を上げたが、もちろん中間値を設定することもできる。
アルファブレンディングを行う際は、glEnabled(GL_BLEND) によりアルファブレンドを有効にしなければならない。 glBlendFunc は混ぜ合わせ方法の指定である。 アルファブレンディングを有効にすると色の混ぜ合わせを行うという処理が余計に発生するため処理が重くなる *4。 無駄な処理を回避するためアルファブレンディングが不要な不透明の絵を描くときは、アルファブレンディングを無効にしている。
今回のサンプルでは、上記で紹介した drawPlane 関数を使って順番に並んだ3枚の面を出力している。
- 1枚目:赤、半透明
- 2枚目:緑、不透明
- 3枚目:青、半透明
関連するコードとして、Alphablend1/main.cppの一部を以下に示す。
中にある緑の不透明な面を、半透明の赤と青の面が挟む形になっているが、このプログラムには実は問題がある。 下記に出力例を示す。


青の面は半透明になるが、赤の面が半透明にならない。これはなぜか?それは出力順序の問題である。
この節で説明するコード
アルファブレンディングを使って透過処理を行うためには、面の出力順序を意識する必要がある。 アルファブレンディングは、あくまで「既に描かれている絵に対して」色を混ぜ合わせる処理であり、 不透明な面が後に描かれると半透明で描きたいと思っていた面が上塗りされてしまう。 透過処理を正常に行うためには、画面から見て奥の面から順番に面を出力しなければならない。
先ほどの事例では、青(半透明)⇒緑(不透明)⇒赤(半透明)という面の描画順序が固定されていたため、 回転により奥行きに対する順序関係が変わった際に透過処理に問題が生じた。 これを解決するために、表示対象となる面を一度リストに貯めておいてから、奥行き順に並べなおして一括出力するという方法を取る。 それを実現しているのが Alphablend2 サンプルである。
ポイントを下記にまとめる。
- 表示対象面の情報をリストに保存して置けるよう Rectangle3 クラスを準備する。
- 面を奥行きでソートする際の規準は、面の中心座標とする *5。
- 最も注意すべきポイント:奥行き順に並び直してから一括して出力しようとすると glRotatef や glTranslatef 等で行おうと思っていた射影を自分で計算する必要がある!
- 面数が増えるとソートに時間が掛かるため、ソートの付加を軽減するため下記の方針を取る。
- 不透明な面はソートの対象にする必要はなく、即時出力してしまって良い。
- 半透明な面だけ即時に出力せず、リストに貯めておいてから、奥行きから順にソートして一括出力する。
射影を自分で計算する必要がある件について、補足する。 本サンプルでは、射影計算をしてくれる2つのクラスを作成し導入している。 関連するコードとして、Alphablend2/Rectangle3.cppの一部を以下に示す。
Body2WorldProjector および NormalProjector クラスがそのクラスである。 Body2WorldProjector は、現在の射影行列を使って物体を中心としたローカル座標のベクトルをワールド座標のベクトルに変換するクラスである。 NormalProjector は、現在の射影行列を使って法線ベクトルを変換するクラスである。
また、その他のソートや一括描画なども補助クラスとして Graphics3 というクラスを作成し、導入している。 関連するコードとして、Alphablend2/Rectangle3.cppの一部を以下に示す。
Graphics3 は、draw メソッドで該当の面を判断し、不透明だったら即時出力し、半透明であったらリストにその面の情報を保存する。 最後に呼び出した drawAll メソッドで半透明の面をソートし、一括出力している。
問題を解消したプログラムによる出力例を下記に示す。


青の面も赤の面も半透明の出力に成功している。したいのがたったこれだけでも大変だ。