Rosso Laboratory

Zバッファ干渉について

まず最初にこの記事は、3DCGツール製作者ではなく3DCGツール利用者の知識によるものですので、正確性に関しては少々問題があるかもしれないという点はご留意ください。しかしながら、利用者レベルではこの程度の知識で十分であろうと思います。現にこの程度の知識で私は3DCGを商売の1つにしているぐらいですから(笑)。

それと「Zバッファ干渉」という用語は恐らくVRM界独自のものであり、3DCG界の方では「Zファイティング」という用語が使われていますが、この記事内では「Zバッファ干渉」という用語を使用します。

久しぶりの長文ですのでご注意を(笑) 緑の文字は雑談ですので飛ばしても良いでしょう。

なお「ココが間違っている」という点にお気づきの方はご教示くださいませ。では。
<ジオメトリ演算処理>
まず、Zバッファについて語るためには、「ジオメトリ演算処理」というものを知っておく必要があります。この「ジオメトリ演算処理」というのは、仮想空間上の立体モデルをスクリーン座標に変換する演算処理のことです。簡単に言うと、人の目で見ても何なのかよくわからない3D座標情報の集合体を、人の目で見て形がわかるように2Dのモニター上に投影する処理のことです。

最近は、この「ジオメトリ演算処理」能力もPCに要求されるレベルが非常に高くなっています。昔は「ジオメトリ演算処理」もCPUに要求していたのですが、リアルタイムレンダリングのように高負荷のものは、流石に汎用の演算装置であるCPUではまかないきれなくなり、ビデオカード側にGPUなどと呼ばれる「ジオメトリ演算処理」専用の演算装置がつくようになりました。そのせいでビデオカードにも大きなファンが付いたり、電源もバスだけでは容量が足りず電源から直接ケーブルで繋ぐようになったりと、どんどん大掛かりなものになってきています。価格も下手をすればCPUよりも高価になっていますね。

たまに勘違いしている人がいるのでちょっと付け加えますと、このGPUの処理というものはリアルタイムレンダリング専用ということであり、3DCGソフトにおける静止画の最終出力レンダリングの処理はCPUが行っています。ですから、3DCGソフトで最終レンダリング出力を高速化したいなら、GPUではなくCPUをパワーアップさせなければ意味がありません。3DCG用ワークステーションに高価なビデオカードが搭載されているのは最終出力用ではなく、事前確認用のリアルタイムレンダリング(DirectXよりもOpenGL用にチューンされていることが多い)のためなのです。最終レンダリング出力は非常に時間がかかりますから、時間を無駄にしないためにも事前確認は大切なのです。

VRMユーザーにはあまり関係ない話でしたね。VRMユーザーはGPUの方が重要ですから。でもGPUだけでも駄目なんですよ。CPUが全体をとりまとめていますから、GPUの性能に見合うクラスのCPUにしておかないとCPUがボトルネックになったりしますので。あとメモリも関係しますが、この辺のことは他の場所等で調べてください。私は「元」パワーPCユーザーなので最近の状況はあまり詳しくはありません(私のバックには現役というか、ある意味プロがいますけどね)。

話が脱線しましたので元に戻しましょう。


<Zバッファ法>
Zバッファ法というのは、この「ジオメトリ演算処理」における隠面消去方法の1つで、最も単純な原理のものです。

簡単に書くと、投影される画面上の各画素において(つまり画面上の各ピクセル単位で)「表示すべき最も手前のオブジェクトまでの距離とその色は何か?」ということをバッファに記憶していく方法です。この距離(奥行き)のことをZ値と呼び、その記憶領域をZバッファ、色の記憶領域をフレームバッファと呼びます。画素1点1点が計算されて画面に表示されている訳です。単純で高速が売りのZバッファ法ですが、膨大な計算がなされていることになりますね。また、メモリを多く消費する点が欠点とされています。

文字ではわかりづらいと思うので、実際のVRMでの状況予想を絵にしてみましょう。


<1画素あたりの「Zバッファ法の計算量」と「レンダリング範囲」との関係>

「一番近い点はどこか?」を調べるために、一番奥から調べていく訳ですから、「レンダリング範囲」が大きくなれば計算量が増えますし、画面を大きくしても画素が増えるので計算量が増え、その分表示速度が遅くなるということです。

Zバッファ法以外にはZソート法というものもあります。これはオブジェクトの面を基準とし、一番奥から面をどんどん書いていき、一番手前の面が書きあがった時に絵が完成するというやり方でBASIC時代の絵の書き方がこんな感じでしたね(若い人にはわからないかもしれませんが)。これ以外にも、スキャンライン法やレイトレーシング法というものがありますが、こちらは精細な絵が得られる一方で、非常に時間がかかるのでリアルタイムレンダリングでは使われることはありません。静止画用の最終レンダリングに使われるやり方です。


<Zバッファ干渉>
さて、以上を踏まえて、本題のZバッファ干渉について書きましょう。もう隠面消去方法の仕組みが解ってしまえば、どうしてこういう現象が起こるのかは理解されているとは思いますが。

まず、現象としては、
同一平面上に面が重なった時に表示がおかしくなる」というもので、下図のような状態を指します。


更に「視点を移動させると面がチラチラと表示される」というものですね。

何故こんなことが起こるかと言えば、Zバッファ法の仕組みから、「同一のZ値なので表示すべきフレームバッファの色がどちらの面であるべきなのかGPUが正確には判断できないから」ということです。それでもGPUは判断し表示しなければいけません。ですから重なった部分において、ある部分はコチラの面、またある部分はアチラの面というような安定していない結果になってしまい、混じり合ったような表示になってしまうのでしょう。


上図のように視点を横に平行移動させた時は、Z値があまり変わらない(バッファ内容があまり変化しない)ので、似たような表示が得られますが、


視点を回転移動させた時や近づいた時は計算結果によって、全く異なる表示結果が得られます。これが視点移動した時のチラつく原因になっているという訳です。
運転席視点で直線部よりも曲線部で画面表示速度が遅くなるという現象を私はVRM4で経験していますが、恐らくこの辺の影響があってそうなっているのだろうと思います。


<Zバッファ干渉対策>
元々この記事を書いたきっかけは、

45-50sさんのsuch a cool「両渡り線に一工夫
      ↓
tettaさんのSKY & ICE「九州からの雑談

の流れを受けたものです。

45-50sさんは記事の中で面が重なる部分に「0.1mm」の差を付けるようにして対策をとられていますが、私もその「0.1mm」ぐらいが経験上良いのではないかなと思っています。通常の状態で使う分にはこの値で問題ないでしょう。ただもっとこの値を小さくしたいという場合には、これから書くことを頭にいれておかないと「あれ?」ってことになります。

まず下図を見てください。これは「0.01mm」の差をつけた場合の表示です。



どうですか? おかしくないですよね。しかし、カメラを遠ざけていくと・・・



Zバッファ干渉!

つまり、こういうことです。

最初のおかしくなかった絵のZ値を仮に10mmとし、おかしくなった絵のZ値を仮に100mmとしましょう。

そうすると前者での2面の距離は、10.01と10.00で、0.1%(0.1ポイント)の差ですが、
後者での2面の距離は、100.01と100.00で、0.01%(0.01ポイント)の差になる訳です。

表示を高速化させるために、ある値で数値の丸めが実行されます。そこで0.1ポイントの差なら差として認識されるが、0.01ポイントの差では丸められて0、つまり同一平面と見なされるということが起こるということです(具体的にどの値で丸められているかは不明ですので、0.1とか0.01とかの値はあくまでも仮の話です)。

ということで、あくまでも比率によるものであるということを頭にいれた上で、値を決める必要があるという訳です。先にも書きましたが余程のことがなければ「0.1mm」の差で良いでしょう。


さて、tettaさんの記事の中で、「こういう風に組み合わせたら確実にZバッファ干渉を起こす」というものが知りたいというお話ですが、組合せパターンは無限にあるので、全てを列挙することは不可能です。原則論として同一平面上に面がある部品同士がZバッファ干渉を起こすとしか言い様がありません。レイアウターでの配置ではZバッファ干渉は気にせず、最終仕上げとしてZバッファ干渉を潰していくというスタンスで良いのではないかなぁと個人的には思っています。もし複製したものをたくさん配置するならば、複製前に確認してZバッファ干渉を潰しておかないと後で面倒なことになりますがね。余談ではありますが、第6号のマンションなんかでは部品単体でZバッファ干渉を起こしていますし、どの程度Zバッファ干渉を潰すかは個人的な嗜好によるものになるでしょう。


最後に、3DCGの用語等に興味のある方のためにコチラのページを紹介しておきます。今回の記事を書く上で確認用として利用させていただきましたので。

ランキングに参加中。クリックして応援お願いします!

コメント一覧

zio
tettaさん、おはようございます。

あまり解説図が準備できなかったのでスミマセン。記事内の最後にリンクしてある慶応大学の研究室のページにいくと解説図もあるので解りやすいと思います。

>何で後の部品が優先されないのだろーという素朴な疑問がありました。

2Dのドローソフトなんかだと後から加えた要素が前面に来るのでそういう感じになりますが、3Dの世界はやはりちょっと違うということですね。奥行きが前提としてあるので、どちらが手前にあるかという陰線・陰面の考え方がまず第一に来ますので。

>Zバッファ法で表示画素を決定した後何らかの処理を加えれば干渉は無くせるんでしょうね。

実はDirectX6からは「Wバッファ法」というものが装備されていて、これを使えばもっと精度が上がるらしいのですが、何故か殆ど使われていないらしいです。やっぱり処理が重くなるからなのかもしれませんね。
zio
45-50sさん、おはようございます。

>0.1mmだと、逆に近くで見たときに意外と段差が目立つんですけどね・・・。

確かに。私も0.1mmではちょっと段差が目立つなぁと思った時は、0.05mmぐらいにまで近づけることがあります。ただその場合は干渉領域が小さいとか、同系色で目立たないということも考慮しないと少し干渉が見える場合もありますね。あとは高さ指定が両端指示できる部品なら0.1と0.0で斜にし、面ではなく線で交差させるという手を使ったりもしています。
畑が斜に出来るともっと使い道が広がるんですがねぇ。

>VRM4の部品配置は小数点第4位までですから、比率の比較もその程度の精度なのかもですね

どうでしょう。どちらかというとDirectX側の設定項目じゃないかなぁという気はするんですが、詳細は私にもよくわかりません。Zバッファ自体は整数値を使うらしいのですが、VRM側で何バイト分用意しているのかに依るのかもしれません。
tetta
こんばんは。
解りやすい記事で大筋の原理が理解できた・・・つもりです。感謝です。
高度が同じで部品が重なった場合であろうというのは経験的に理解していたのですが、何で後の部品が優先されないのだろーという素朴な疑問がありました。
Zバッファ法の原理からすると、なるほどね~と言う感じです。
表示速度を無視して考えれば、Zバッファ法で表示画素を決定した後何らかの処理(どちらかの部品に統一するみたいな)を加えれば干渉は無くせるんでしょうね。VRM4に求めているわけではありませんけどね。
重なった場合、高度さ0.1mmが適当であろう理由も分かりスッキリです。

Zバッファ干渉自体はあまり気にせず最終的に調整、重なった部品は気が付いたら高度さ0.1mm付けるってノリでやってこうと思いました。

画像系はアイコン程度しか自作しないんで2Dのドット絵程度の知識しかなく(複雑なのは、もちろん実写からダウンしてるだけなんで)、レンダリングの話、本当に参考になりました。
45-50s
こんばんは。とても参考になりました。

>カメラを遠ざけていくと・・・
>Zバッファ干渉!

そうなんですよ・・・。自分もはじめは0.01mmだけずらすようにしていたんですが、あるとき遠くから見ると干渉していることに気付いて、0.1mmに落ち着いた次第です。
ただ、0.1mmだと、逆に近くで見たときに意外と段差が目立つんですけどね・・・。
VRM4の部品配置は小数点第4位までですから、比率の比較もその程度の精度なのかもですね(関係ないか)。
名前:
コメント:

※文字化け等の原因になりますので顔文字の投稿はお控えください。

コメント利用規約に同意の上コメント投稿を行ってください。

 

※ブログ作成者から承認されるまでコメントは反映されません。

  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最新の画像もっと見る

最近の「VRM用技術」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事