2010-11-28 29 views
8

Je suis actuellement en train de mettre en œuvre une cartographie de bruit 3D Perlin en utilisant Shader Model 4 (DirectX 10 HLSL). Générer le bruit lui-même n'est pas un gros problème (il y a des tonnes de tutoriels et de codes autour) mais ce que je n'ai pas trouvé sont des dérivés analytiques du bruit 3D Perlin.Dérivé analytique du bruit 3D Perlin

Les seuls sites prenant en compte les dérivés sont Ińigo Quilez's site et un GameDev.net discussion associé. Le problème est que dans le premier lien le bruit est basé sur la valeur, pas basé sur le gradient (qui est une exigence pour moi), dans le deuxième lien, il n'y a que la dérivée du bruit de gradient 2D. Notez que je ne recherche pas de dérivés numériques, car ceux-ci nécessitent la génération de 4 échantillons de bruit voisins, ce qui représente beaucoup trop de frais généraux.

Est-ce que quelqu'un a calculé ces dérivés? Existe-t-il une implémentation de référence qui les utilise?

+0

http://www.scratchapixel.com/lessons/procedural-generation-vritual-worlds%20/perlin-noise-part-2/perlin-noise-computing-derivatives – user18490

Répondre

15

Je n'ai pas non plus trouvé de solution sur le web aujourd'hui, j'ai donc essayé de la dériver.

Premièrement, les notations d'un bruit Perlin 3D sont définies.

Notation

Supposons que le bruit Perlin 3D est calculée par l'interpolation trilinéaire comme

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 sont les facteurs d'interpolation par le polynôme quintique de coordonnées de la fraction (par exemple, l'amélioration de Perlin bruit):

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) 

et dot___ s d ot produits des vecteurs gradients (gx___, gy___, gz___) de s aux points en treillis et la fraction coordonnées:

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 

Calcul des dérivés

En premier lieu, les dérivés de calcul de u, v et 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) 

En étendant n avec 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) 

ensuite prendre les dérivées partielles de 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) 

Puis (nx, ny, nz) est le vecteur de gradient (dérivés partielle) de la fonction de bruit.

Optimisation

Certains sous-expression commune peut être refactorisée, si le compilateur ne peut pas le manipuler.Par exemple:

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

Les coefficients du n élargi sont réutilisés plusieurs fois. Ils peuvent être calculés par:

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 

également les dérivés a des coefficients similaires,

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

Le calcul de n peut utilise la forme développée avec k0 ... k6 aussi bien.

Derniers mots

Cette solution a été vérifiée par rapport à la méthode centrale de différence. Bien que cette solution semble maladroite, mon expérience (CPU seulement, SSE) a montré que, le calcul de ces dérivés par cette solution n'engendre que 50% de temps supplémentaire pour calculer un seul échantillon de bruit Perlin 3D.

La différence finie nécessite au moins 300% de temps supplémentaire (3 échantillons supplémentaires) ou 600% (6 échantillons pour la différence centrale). Par conséquent, cette solution est meilleure en performance, et devrait également être plus stable numériquement.

+2

dot111 existe deux fois dans votre liste. une instance de ce devrait être réellement dot101 – plasmacel

+0

Merci @plasmacel. Fixé. –

+0

Avez-vous un cahier Wolfram Mathematica de la dérivation? – plasmacel