3D Perlinノイズ分析微分
-
28-09-2019 - |
質問
現在、シェーダーモデル4(DirectX 10 HLSL)を使用して3D Perlinノイズバンプマッピングを実装しています。ノイズ自体を生成することは大きな問題ではありません(周囲のチュートリアルやコードがたくさんあります)が、私が見つけていないのは、3D Perlinノイズの分析誘導体です。
デリバティブを考慮しているサイトのみです イニゴ・クイルスのサイト そして関連 gamedev.netディスカッション. 。問題は、最初のリンクでは、ノイズが勾配ベースではなく値ベースであること(これは私にとって要件である)であり、2番目のリンクには2Dグラデーションノイズ誘導体しかないことです。
4つの隣接するノイズサンプルを生成する必要があるため、数値導関数を探していないことに注意してください。
これらの導関数を計算した人はいますか?それらを使用している参照実装はありますか?
解決
今日もウェブ上で解決策を見つけることができなかったので、それを導き出そうとしました。
まず、3D Perlinノイズの表記が定義されています。
表記
3Dパーリンノイズが三線の補間によって計算されると仮定します
n = Lerp(
Lerp(
Lerp(dot000, dot100, u),
Lerp(dot010, dot110, u),
v),
Lerp(
Lerp(dot001, dot101, u),
Lerp(dot011, dot111, u),
v),
w)
どこ u
, v
, w
分数座標のQuintic多項式(つまり、Perlinノイズの改善)による補間因子です。
x0 = frac(x)
y0 = frac(y)
z0 = frac(z)
x1 = x0 - 1
y1 = y0 - 1
z1 = z0 - 1
u = x0 * x0 * x0 * (x0 * (6 * x0 - 15) + 10)
v = y0 * y0 * y0 * (y0 * (6 * y0 - 15) + 10)
w = z0 * z0 * z0 * (z0 * (6 * z0 - 15) + 10)
と dot___
Sは勾配ベクトルのドット積です (gx___, gy___, gz___)
latticeポイントと分数座標:
dot000 = gx000 * x0 + gy000 * y0 + gz000 * z0
dot100 = gx100 * x1 + gy100 * y0 + gz100 * z0
dot010 = gx010 * x0 + gy010 * y1 + gz010 * z0
dot110 = gx110 * x1 + gy110 * y1 + gz110 * z0
dot001 = gx001 * x0 + gy001 * y0 + gz001 * z1
dot101 = gx101 * x1 + gy101 * y0 + gz101 * z1
dot011 = gx011 * x0 + gy011 * y1 + gz011 * z1
dot111 = gx111 * x1 + gy111 * y1 + gz111 * z1
デリバティブの計算
まず、の導関数を計算します u
, v
と w
u' = 30 * x0 * x0 * (x0 - 1) * (x0 - 1)
v' = 30 * y0 * y0 * (y0 - 1) * (y0 - 1)
w' = 30 * z0 * z0 * (z0 - 1) * (z0 - 1)
拡大することによって n
と Lerp(a, b, t) = a + (b - a) * t
,
n = dot000
+ u(dot100 - dot000)
+ v(dot010 - dot000)
+ w(dot001 - dot000)
+ uv(dot110 - dot010 - dot100 + dot000)
+ uw(dot101 - dot001 - dot100 + dot000)
+ vw(dot011 - dot001 - dot010 + dot000)
+ uvw(dot111 - dot011 - dot101 + dot001 - dot110 + dot010 + dot100 - dot000)
次に、の部分的な導関数を取ります n
,
nx = gx000
+ u' (dot100 - dot000)
+ u (gx100 - gx000)
+ v (gx010 - gx000)
+ w (gx001 - gx000)
+ u'v (dot110 - dot010 - dot100 + dot000)
+ uv (gx110 - gx010 - gx100 + gx000)
+ u'w (dot101 - dot001 - dot100 + dot000)
+ uw (gx101 - gx001 - gx100 - gx000)
+ vw (gx011 - gx001 - gx010 + gx000)
+ u'vw(dot111 - dot011 - dot101 + dot001 - dot110 + dot010 + dot100 - dot000)
+ uvw (gx111 - gx011 - gx101 + gx001 - gx110 + gx010 + gx100 - gx000)
,
ny = gy000
+ u (gy100 - gy000)
+ v' (dot010 - dot000)
+ v (gy010 - gy000)
+ w (gy001 - gy000)
+ uv' (dot110 - dot010 - dot100 + dot000)
+ uv (gy110 - gy010 - gy100 + gy000)
+ uw (gy101 - gy001 - gy100 + gy000)
+ v'w (dot011 - dot001 - dot010 + dot000)
+ vw (gy011 - gy001 - gy010 + gy000)
+ uv'w(dot111 - dot011 - dot101 + dot001 - dot110 + dot010 + dot100 - dot000)
+ uvw (gy111 - gy011 - gy101 + gy001 - gy110 + gy010 + gy100 - gy000)
,
nz = gz000
+ u (gz100 - gz000)
+ v (gz010 - gz000)
+ w' (dot001 - dot000)
+ w (gz001 - gz000)
+ uv (gz110 - gz010 - gz100 + gz000)
+ uw' (dot101 - dot001 - dot100 + dot000)
+ uw (gz101 - gz001 - gz100 + gz000)
+ vw' (dot011 - dot001 - dot010 + dot000)
+ vw (gz011 - gz001 - gz010 + gz000)
+ uvw'(dot111 - dot011 - dot101 + dot001 - dot110 + dot010 + dot100 - dot000)
+ uvw (gz111 - gz011 - gz101 + gz001 - gz110 + gz010 + gz100 - gz000)
それで (nx, ny, nz)
ノイズ関数の勾配ベクトル(部分微分)です。
最適化
コンパイラがそれを処理できない場合、いくつかの一般的なサブエクスポンションを考慮することができます。例えば:
uv = u * v
vw = v * w
uw = u * w
uvw = uv * w
拡張された係数 n
複数回再利用されます。それらは次のように計算できます:
k0 = dot100 - dot000
k1 = dot010 - dot000
k2 = dot001 - dot000
k3 = dot110 - dot010 - k0
k4 = dot101 - dot001 - k0
k5 = dot011 - dot001 - k1
k6 = (dot111 - dot011) - (dot101 - dot001) - k3
また、誘導体には同様の係数があり、
gxk0 = gx100 - gx000
gxk1 = gx010 - gx000
...
の計算 n
拡張されたフォームを使用することができます k0
, ... k6
同じように。
最後の言葉
このソリューションは、中心差分法に対して検証されています。
このソリューションは不器用に見えますが、私の実験(CPUのみ、SSE)は、このソリューションによってこれらの導関数を計算することがについてのみ発生することを示しました 50%余分な時間 単一の3Dパーリンノイズサンプルを計算する。
有限差分には、少なくとも300%の余分な時間(余分な3つのサンプルを実行)または600%(中央差のために6つのサンプルを行う)が必要になります。
したがって、このソリューションはパフォーマンスが優れているため、数値的に安定している必要があります。