Domanda

Attualmente sto implementando una mappatura del rumore 3D Perlin utilizzando Shader Model 4 (DirectX 10 HLSL).Generare il rumore in sé non è un grosso problema (ci sono tonnellate di tutorial e codici in giro) ma quello che non ho trovato sono derivati ​​analitici del rumore 3D Perlin.

Gli unici siti che tengono conto dei derivati ​​sono Il sito di Ińigo Quilez e un correlato Discussione su GameDev.net.Il problema è che nel primo collegamento il rumore è basato sul valore, non sul gradiente (che è un requisito per me), nel secondo collegamento c'è solo la derivata del rumore del gradiente 2D.

Tieni presente che non sto cercando derivate numeriche poiché richiedono la generazione di 4 campioni di rumore vicini e questo è un sovraccarico eccessivo.

Qualcuno ha calcolato queste derivate?Esiste un'implementazione di riferimento che li utilizza?

È stato utile?

Soluzione

Inoltre oggi non sono riuscito a trovare una soluzione sul web, quindi ho provato a ricavarla.

Innanzitutto vengono definite le notazioni di un rumore Perlin 3D.

Notazione

Supponiamo che il rumore 3D Perlin sia calcolato dall'interpolazione trilineare come

n = Lerp(
        Lerp(
            Lerp(dot000, dot100, u),
            Lerp(dot010, dot110, u),
            v), 
        Lerp(
            Lerp(dot001, dot101, u), 
            Lerp(dot011, dot111, u),
            v),
        w)

Dove u, v, w sono i fattori di interpolazione per il polinomio quintico delle coordinate della frazione (ovvero, rumore Perlin migliorato):

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)

E dot___s sono prodotti scalari dei vettori del gradiente (gx___, gy___, gz___)s nei punti del reticolo e nelle coordinate della frazione:

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

Calcolo delle derivate

Innanzitutto, calcola le derivate di u, v E 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)

Espandendosi n con 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)

Quindi prendiamo le derivate parziali di 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)

Poi (nx, ny, nz) è il vettore gradiente (derivate parziali) della funzione rumore.

Ottimizzazione

Alcune sottoespressioni comuni possono essere eliminate, se il compilatore non è in grado di gestirle.Per esempio:

uv = u * v
vw = v * w
uw = u * w
uvw = uv * w

I coefficienti nell'espansione n vengono riutilizzati più volte.Possono essere calcolati da:

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

Anche i derivati ​​hanno coefficienti simili,

gxk0 = gx100 - gx000
gxk1 = gx010 - gx000
...

Il calcolo di n can utilizza la forma estesa con k0, ... k6 anche.

Parole finali

Questa soluzione è stata verificata rispetto al metodo della differenza centrale.

Sebbene questa soluzione sembri goffa, il mio esperimento (solo CPU, SSE) ha dimostrato che, il calcolo di queste derivate con questa soluzione comporta solo circa 50% di tempo supplementare al calcolo di un singolo campione di rumore Perlin 3D.

La differenza finita richiederà almeno il 300% di tempo in più (eseguendo 3 campioni in più) o il 600% (eseguendo 6 campioni per la differenza centrale).

Pertanto questa soluzione è migliore in termini di prestazioni e dovrebbe anche essere più stabile numericamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top