遮蔽処理の問題点

Posted 2013.05.08 in 吉里吉里

遮蔽の詳しい条件を確認していて、いくつか妙な点があることに気がついた。

だんだん小さくなる遮蔽矩形

遮蔽面が複数ある場合、それらの面によって作られる遮蔽領域は
より上のレイヤーからより下のレイヤーに向かうにつれて、だんだんと広がっていくはずである。
しかし遮蔽面によって作られる遮蔽矩形が重なった場合、
その交差をとって新たな遮蔽矩形とするので

遮蔽領域は逆にだんだん小さくなっていく。

さらに遮蔽面が重なっていない場合、その交差は空の矩形となるので遮蔽領域がいったん消滅し
次の遮蔽面から新たに遮蔽矩形が作成されるといった奇妙な動作になる。

この交差判定は、遮蔽領域の合成処理であると同時に
子レイヤーの遮蔽領域を、親レイヤーの領域内に制限するためのものだろう。
しかしそれらは本来別個の処理である。
また遮蔽領域を単一の矩形で管理していることがこの問題をややこしくしている。

不要な条件

「遮蔽される条件」として

・レイヤーとその親レイヤー全てが遮蔽有効条件
(レイヤーが可視であり、不透明であり、不透明時に透けない表示タイプである)を満たしている

があるが

この条件はおそらく不要である。

この条件は「遮蔽する条件」の一部でもあるので、
遮蔽レイヤーの親レイヤーは常にこの条件を満たし、遮蔽される。
しかしその兄弟レイヤーなどはこの条件を満たすとは限らない。
この条件により、本来遮蔽されうるはずのレイヤーが遮蔽されなくなってしまっている。

ちなみにレイヤーが遮蔽されないようにするトリックはこの条件を逆手に取ったものであり、
この条件がなくなると使えなくなる。

限定的な遮断

実際に UpdateExcludeRect を使った遮断が行われるのは、
子レイヤーと重なる領域に限定されている。
けれどもよくよく考えると、そのような限定はまったく必要がないように思える。
なぜわざわざそのように限定しているのか。

察するに、吉里吉里の遮蔽処理はおそらく本来

「親レイヤーを遮蔽する子レイヤーがある場合に
 親レイヤーの描画を省く」

ことのみを目的として作られた機能なのであろう。
だとすれば合点が行く。

しかし実際には、全てのレイヤーを考慮した遮蔽処理が行われている。
そのせいでいろいろ不整合が生じているものと思われる。

遮断の手抜き

tTJSNI_BaseLayer::CopySelf を見ると、UpdateExcludeRect が描画矩形を
上下にまたいでいない場合、遮断が行われないことがわかる。
言い換えると

遮断は横方向にしか行われない

ということである。

これは一見ひどい手抜きに思えるが、
実際には吉里吉里の画面描画の仕組みを考慮した設計だろう。

というのも、吉里吉里では画面を再描画する時、
画面領域を縦方向に細かく分割して描画が行われるからだ。
この領域を遮断によって縦方向にさらに分割すると、
かえってコストが高くついてしまう可能性が考えられる。
そこで敢えて”手抜き”をしているものと思われる。

ただし画面領域を分割するかどうかは設定で切り替えられるので、
分割しないように設定した場合は、
この”手抜き”によって遮蔽処理の効果が大幅に低下する可能性がある。

遮断のミス

tTJSNI_BaseLayer::CopySelf において、
UpdateExcludeRect が描画矩形の左辺をまたいだ状態を判定する次の条件───


else if(r.left >= uer.left && r.left < uer.left)

この条件は絶対に成立しない!

このため UpdateExcludeRect が描画矩形の左辺をまたいだ状態の時、遮断が行われない。
これは単純なミスだろう。
正しくは


else if(r.left >= uer.left && r.left < uer.right)

である。

まとめ

ここまで見てきたように、遮蔽処理にはかなりの癖がある。
レイヤーの type を ltOpaque にすると、遮蔽処理が行われ
遮蔽処理がもたらす難解な問題に遭遇する可能性があることに注意が必要だ。
最背面のレイヤーを ltOpaque にすることは得に問題ないだろうが、
手前に ltOpaque なレイヤーを作る場合は気をつけなくてはならない。

深みにはまる前に、根本的に ltOpaque の使用を避けるのも一つの方法だろう。
しかし遮蔽による描画量の削減はそれでもなお魅力的である。
敢えて ltOpaque を使い、遮蔽処理に由来する問題に遭遇した時、
遮蔽処理に関する知識がきっと問題解決の役に立つはずだ。


Leave a Reply

*