Pregunta

Actualmente estoy poniendo en práctica una Perlin 3D ruido mapeado de relieve utilizando Shader Model 4 (DirectX 10 HLSL). Generando el ruido en sí mismo no es un gran problema (hay un montón de tutoriales y códigos de alrededor), pero lo que no he encontrado son derivados de análisis de 3D Perlin ruido.

Los únicos sitios que tienen los derivados en cuenta son sitio Iñigo Quilez y una relacionada GameDev.net discusión. El problema es que en el primer link el ruido se basa el valor, no degradado en función (que es un requisito para mí), en el segundo link, sólo hay 2D degradado de ruido derivado.

Tenga en cuenta que yo no estoy buscando derivadas numéricas que las requieren 4 muestras de ruido vecina que se generen y que de manera demasiado trabajo.

¿Alguien ha calculado estos derivados? ¿Existe una implementación de referencia que utiliza ellos?

¿Fue útil?

Solución

También puede que no se encuentra una solución en la web hoy en día, así que traté de derivación del mismo.

En primer lugar las notaciones de una Perlin 3D ruido se define.

Notación

Supongamos que el ruido Perlin 3D se calcula mediante la interpolación trilineal como

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

donde u, v, w son los factores de interpolación por el polinomio de quinto grado de coordenadas de fracción (es decir, la mejora de ruido 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)

y dot___s son productos escalares de los vectores (gx___, gy___, gz___)s gradiente en puntos de la red y las coordenadas de fracción:

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

computación los derivados

En primer lugar, derivados de cómputo de u, v y 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)

Con la ampliación de 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)

Luego tomar derivadas parciales 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)

Entonces (nx, ny, nz) es el vector gradiente (derivadas parciales) de la función de ruido.

Optimización

Algunos sub-expresión común se puede factorizar a cabo, si el compilador no puede manejarlo. Por ejemplo:

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

Los coeficientes de la n expandido se reutilizan varias veces. Ellos pueden calcularse por:

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

También los derivados tiene coeficientes similares,

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

El cálculo de n lata utiliza la forma expandida con k0, ... k6 también.

Palabras finales

Esta solución ha sido verificada contra método de diferencia central.

Aunque esta solución apariencia torpe, mi experimento (sólo CPU, SSE) mostró que, el cálculo de estos derivados por esta solución sólo incurre sobre 50% de tiempo extra para el cálculo de una sola muestra de ruido Perlin 3D.

diferencia finita al menos necesidad 300% de tiempo extra (haciendo adicionales 3 muestras) o 600% (haciendo 6 muestras por diferencias central).

Por lo tanto, esta solución es mejor en el rendimiento, y también debe ser estable más numéricamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top