Procedural Stochastic Textures by Tiling and Blendingを読んでみる

Procedural Stochastic Textures by Tiling and Blending
https://drive.google.com/file/d/1QecekuuyWgw68HU9tg6ENfrCTCVIjm6l/view

Thomas Deliot さんと Eric Heitz さんが発表されたテクスチャのタイリングに関連する手法です。
本発表は、具体的な実装方法に言及したものとなっています。採用されている技法そのものについては、
Eric Heitzさんと、Fabrice Neyretさんが別の論文ですでに発表しています。
High-Performance By-Example Noise using a Histogram-Preserving Blending Operator
https://hal.inria.fr/hal-01824773/document

 本手法は、確率的で、非周期的なテクスチャをExampleとし入力して、それと見た目の似たテクスチャをProceduralに生成するものです。確率的で、非周期的なテクスチャの例としては、砂地、苔などのテクスチャが挙げられています。それらをもとに、見た目の似たテクスチャを生成します。通常のテクスチャマッピングでは、このようなテクスチャを広範囲に適用するときに、非常に大きなテクスチャを用意するか、リピートを適用する必要がありますが、テクスチャを何度もリピートした際に、周期的に現れる特徴である「繰り返し感」が問題になります。本手法では、小さなテクスチャから非常に多くのバリエーションを生成することができるため、繰り返し感の低減につながります。また、Exampleとして通常のRGBチャンネルのテクスチャを用いるため、多数のパラメーターを用いるProcedural手法に比べて、アーティストが制御しやすいと言えると思います。
 本手法は大きく分けて、2つのパートに分かれます。一つはTilingで、小さなテクスチャから多くのバリエーションを連続的に生成するための部分です。もうひとつはBlendingで、Tilingの結果から入手した複数のExampleテクスチャのサンプルを、元のテクスチャの特徴を維持しつつブレンドするための部分になります。

Tiling

 Tilingは、複数の独立したUV空間から連続的にExampleテクスチャをサンプリングすることを目的としています。一つのUV空間からの連続的なサンプリングでは、Exampleテクスチャの周期的なリピートは避けられません。そのため、複数のUV空間を用いてExampleテクスチャをサンプリングしてBlendする必要があります。複数の独立したUV空間は、Exampleテクスチャの上に作った仮想的なグリッドの頂点ごとに保持するようにします。また、あるグリッドに内包された点は、近傍のグリッドの頂点が持つそれぞれのUV空間でExampleテクスチャをサンプリングした結果を補間したものを用いるようにします。このようにすれば、グリッドの頂点、辺、内包された領域で、連続な値を得ることができます。
 Tilingで用いるグリッドは、simplex gridと呼ばれる、正三角形を敷き詰めたグリッドを用います。この正三角形グリッドは、平面を充填できるもっとも単純な形状となっています。なぜこれを用いるかというと、この手法では、仮想グリッドの頂点ごとに独立したUV空間を利用し、頂点ごとにテクスチャ座標の計算と、テクスチャサンプリングを行う必要があります。そのためグリッドはなるべく少ない頂点で形成されるのが望ましいです。また、正三角形であるため、gradientがどの辺でも同じになる特徴があります。そのため正三角形グリッドを用いているようです。
 次に実装ですが、まず直交座標系からsimplex gridへの座標変換は、スケールの入ったshearマトリクスを適用することで変換できます。また、simplex gridから直交座標への逆変換も可能で、線形変換なので重心座標も保存されるようです。これを用いて、比較的簡単に任意のUV座標のsimplex gridにおける位置と、その位置を内包する正三角形の頂点のUV値、そして、その三角形における重心座標を求めています。(Listing1.1参照。注:用いられているマトリクスに関しては未だ理解できてない。逆マトリクスを用いるべきなのではと思ってる)また、内包する正三角形の3頂点のUV座標は、そのまま使うと、まったくバリエーションを増やすことができません。そのため、3つの頂点それぞれに、ハッシュを用いて再現性のある形で(同じ頂点では同じ値になるように)オフセットを適用して、独立した別のUV空間になるようにしています。そして、サンプリングされた3つの値を、重心座標に基づいて「補間」することで、最終的なサンプリング結果とします。また、テクスチャサンプリングの際は、元のUV座標の描画スクリーン上のgradientを取得しMipmapのLoDの計算に用いるようにしています。

Blending

 先ほどTilingの項で「補間」と表記しましたが、重心座標に基づく単純な線形補間では、Exampleとしてアーティストが提示したテクスチャと、補間されて生成された結果で、見た目が大きく異なってしまうことがわかっています(Fig1.3の真ん中)。これを改善するために、本手法では、Histogram-preserving blendingという補間方法を用いることで、Exampleテクスチャの見た目に近い状態の結果を出すことに成功しています。(Fig1.3の右側)

Historgram Preserving Blendingについては、冒頭に紹介した別の論文を参照してます
High-Performance By-Example Noise using a Histogram-Preserving Blending Operator
https://hal.inria.fr/hal-01824773/document

Linear Blending

 では、なぜLinear Blendingでは、見た目が大きく変わってしまうかですが、Linear Blendingの結果が、オリジナルのテクスチャが持つ特徴を保持していないからです。これを、元のテクスチャと補完されたテクスチャの、期待値と分散、そしてヒストグラムを考察することで確認したいと思います。
 Linear Blendingは、サンプリングされた複数の値:X_1~X_Nに、ウェイト:w_1~w_Nを、各々乗算して足し合わせたものです。この際のウェイトの合計は1.0とします。正確には、w, X共に変数なのですが、Xのバリエーションに対して、wのバリエーションが極めて少ないものと考え、wをXに対して定数として扱って考えます。
(考え方としては、元のテクスチャと、元のテクスチャをランダムにサンプリングしながら、wを乗算して作られたn枚のテクスチャの加算結果との比較と考えるとわかりやすいかもしれません。)

\begin{aligned}  X^{lin} = \sum_{n=1}^{N}{w_n X_n}  \end{aligned}

まず期待値ですが、wの合計が1.0となることから、もとの集団Xの期待値と同じになります。

\begin{aligned}  \mathbb E[X^{lin}] = \mathbb E[X]  \end{aligned}

次に分散ですが、Xの各サンプリングに相関がないと考えると、共分散cov(X_i,X_j)はゼロとみなすことができ、各項の分散の足し算となります。さらにwを定数として扱っているので、以下のようになると思います。

\begin{aligned}  \sigma^2(X^{lin}) = (\sum_{n=1}^{N}{w_n^2})\sigma^2(X)  \end{aligned}

wの合計が1.0で正規化されていると考えると、wの2乗の合計は、ほとんどのケースで1.0を下回ります。したがって、元のテクスチャよりも、線形補間されたテクスチャは、分散が小さくなる傾向があるようです。特に、wが均等に分配されたケースで一番小さくなります。このことは、(Fig1.3の真ん中)を観察してもわかります。

 次にHistogramがどうなるかを考えます。Linear Blendingでは、元のテクスチャの値に1.0以下のwを掛けるので、各項のヒストグラムは、元のHistogram にくらべ、取りうる値の範囲が小さくなり、それに応じて頻度は高くなります。そして、そうした値同士を足すので、個々のヒストグラムのConvolutionになります。したがって、Convolutionを繰り返すことになるので、結果として得られるHistogramは、鋭いスパイクがあったとしたら、丸められると思われます。またwが大きい場合は、ConvolutionのWindowが大きいことを意味し、その場合は全体の値の中央付近が高くなることが予想できます。

\begin{aligned}  \mathcal{H}^{lin}(X) &= \mathcal{H}_1(X) * \mathcal{H}_2(X) * ... * \mathcal{H}_n(X) \\  &= \frac{1}{w_1}\mathcal{H}(\frac{X}{w_1}) * ... * \frac{1}{w_n}\mathcal{H}(\frac{X}{w_n})  \end{aligned}

Variance-Preserving Blending

上記のようにLinear Blendingでは、分散が小さくなってしまうことがわかりました。したがって、期待値と分散を保った補間方法があれば、Linear Blendingに比べて、オリジナルのテクスチャの特徴を保持したまま補間することができると考えられると思います。そこで考えられるのは、以下の式となります。この式は、一見複雑ですが、期待値からの差を線形補間して、ある係数でスケーリングした式になります。この係数は、分散が元のテクスチャと同じになるように作られています。

\begin{aligned}  X^{cov} = \frac{\sum_{n=1}^{N}{w_n (X_n - \mathbb E[X])}}{\sqrt{\sum_{n=1}^{N}{w_n^2}}} + \mathbb E[X]  \end{aligned}

まず期待値ですが、
E[X]とE[X]の差がゼロとなることから、残るのは、E[X]のみとなり、元のテクスチャと同じです。

\begin{aligned}  \mathbb E[X^{cov}] &= \frac{\sum_{n=1}^{N}{w_n (0)}}{\sqrt{\sum_{n=1}^{N}{w_n^2}}} + \mathbb E[X] \\  &= \mathbb E[X]  \end{aligned}

次に分散ですが、LinearBlendingと場合と同様に式を展開すると、以下のようになり、係数が1になります。つまり分散が保存されることになります。

\begin{aligned}  \sigma^2(X^{cov}) &= \sum_{n=1}^{N}(\frac{w_i}{\sqrt{\sum{w_i^2}}})^2 \sigma^2(X) \\  &=\sum_{n=1}^{N}(\frac{w_i^2}{\sum{w_i^2}}) \sigma^2(X) \\  &= \sigma^2(X) \\  \end{aligned}

次にHistogramを考えてみます。Variance-Preserving Blendingは、線形補間した際のヒストグラムを用いて以下のようにあらわすことができます。

\begin{aligned}  W = \sqrt{\sum{w_i^2}}  \end{aligned}
\begin{aligned}  \mathcal{H}^{cov}(X) = W \mathcal{H}^{lin}(W(X - \mathbb E[X])+\mathbb E[X])  \end{aligned}

したがって、Histogramが保存されるわけではないことがよくわかります。端的に言えば、期待値からの差分をスケーリングしているにすぎないので、保存されるわけはありません。ただし、もしも元のテクスチャのHistogramが、ガウス分布だっとすると、ガウス分布のConvolutionはガウス分布になることが知られていますので、Convolutionとスケーリングを繰り返したHistogramは、ガウス分布に基づくHistogramとなります。さらに、先の式で、期待値と分散が保存されることがわかるので、元のテクスチャのHistogramがガウス分布の場合は、Histogramも保存されることになります。

Histogram Preserve Blending

Historgram Preserve Blendingは、Variance-Preserving Blendingを利用して行います。具体的には、Exampleテクスチャのヒストグラムを調べ、オリジナルのヒストグラムを、ガウス関数にマッピングするための変換、Histogram TransformationをExampleテクスチャに対して事前に施します。方法は、テクスチャの各ピクセルを値の大きさでソートして、それをガウス関数のヒストグラムになるように順次変換していくだけです。イメージとしては、値の大きさでソートした色をガウス関数という入れ物に充填していく感じです。こうすることで、Exampleテクスチャをガウス関数のヒストグラムのテクスチャに変換することができます。このように変換したテクスチャをサンプリングして、Variance-Preserving Blendingを行った後に、元の色に戻す必要がありますが、この逆変換は、事前に用意したLookup tableを参照することで行います。

感想

 技術的には上記の通りで、Exampleテクスチャの個々のサンプルに相関が無い事が前提条件でしたので、サンプルに相関が強い(つまり、模様が規則的な)Exampleテクスチャを用いた場合は、この手法はうまくいきません。それは冒頭に触れてある通りだと思います。ガウス関数へのマッピングやルックアップテーブルの生成に関しては、事前計算、もしくは、非動的(つまりレベルのロード時など)に行えば十分リアルタイムでも実行可能かと思います。
 また、同一のExampleテクスチャを用いて、質感を保ちつつ見た目の異なる大きなテクスチャを、ゲーム内で多数生成することも可能だと思います。Tilingの際に用いるHashを変更し、直接3Dモデルに適用せず、Blendした結果を、一旦テクスチャとして保存すれば、計算負荷も問題になりません。これで、同じ3Dオブジェクトに対して、同じExampleテクスチャを用いても、見た目の異なるテクスチャを用意することが可能です。これは、例えば、MMOなどのキャラクタに、ほぼ、唯一無二といっていいユニークなテクスチャを生成してアサインすることが可能だと思います。さらにExampleテクスチャによって、見た目のコントロールが、かなりの程度、製作者側で可能です。これは非常に有意義な技法だと思います。
 一方で、先ほどリアルタイムでも実行可能といいましたが、実際は、1枚のテクスチャに対して、テクスチャ3回のサンプリングと、LUTのサンプリング、加えてある程度の計算量が考えられます。テクスチャとLUTのキャッシュヒット率はかなり期待できると思いますが、昨今のリアルタイム向けアセットのテクスチャの枚数を考えると、GPUメモリが相当貴重な環境で、テクスチャのサンプラーに余裕があり、GPUの計算リソースが余っているようなケースでないと、直接リアルタイムで用いるのは、パフォーマンスの面で難しいかもしれません。もちろん、そもそもテクスチャのTilingをマネージメントするのが面倒くさいなどの状況では十分有用と思われます。

参照
High-Performance By-Example Noise using a Histogram-Preserving Blending Operator
https://hal.inria.fr/hal-01824773/document

Lagrangian Texture Advection: Preserving both Spectrum and Velocity Field
https://hal.inria.fr/inria-00536064v1/document