Comprender el ruido Perlin

31

Estoy jugando con Perlin Noise después de trabajar con Diamond Square. Seguí la implementación de Hugo Elias que básicamente realiza una serie de funciones con x, y como entrada para arrojar cada valor de coordenadas.

Mi código PHP está aquí :

Tengo dos preguntas:

¿Cómo uso el algoritmo para generar un mapa de altura en una matriz? No lo entendí completamente y simplemente porté a PHP el pseudocódigo, pero hice la última función (map_perlined) después de leer en alguna parte que el algoritmo "mágicamente" le da valores de transición para cada punto x, y dado (aparentemente, sin tener que leer su valores adyacentes), solo obtengo esto cuando lo uso como función aleatoriamt_rand(-100,100)/100;

ingrese la descripción de la imagen aquí

Y esto cuando se usa el criptográfico: 1.0-(($n*($n*$n*15731+789221)+1376312589)&0x7fffffff)/1073741824.0;(que, por cierto, se puede implementar "tal cual" en PHP?):

ingrese la descripción de la imagen aquí

En resumen, tres preguntas:

  1. ¿Es correcto mi código?
  2. ¿La función aleatoria se puede portar a PHP como se describe en el código? No arroja errores, pero los resultados no están ahí.
  3. ¿Cómo uso realmente el algoritmo?

ACTUALIZAR

Ok, hizo un puerto PHP del código que se muestra en el documento de Gustavson, y como dijo otro codificador, solo genera una octava. ¿Tiene algún otro sitio / documento / guía útil sobre cómo usar esto con los conceptos de octavas múltiples, amplitud, frecuencia, etc. para controlar la función de ruido? En el artículo de Gustavson solo muestra los resultados, no la implementación real del algoritmo, ¿tal vez me estoy perdiendo algo?

ACTUALIZACIÓN 2
@NATHAN

Hice algo como:

$persistence = 0.5;

for ($j = 0; $j < $size; $j++) {
    for ($i = 0; $i < $size; $i++) {

        for ($o = 0; $o < 8; $o++) {
            $frequency = pow(2,$o);
            $amplitude = pow($persistence, $o);
            $value += SimplexNoise($i*$frequency, $j * $frequency) * $amplitude;
            }

            //$value = SimplexNoise($i, $j) + 0.5 * SimplexNoise($i, $j) + 0.25 * SimplexNoise($i, $j);
            $this->mapArray[$i][$j] = new Cell($value);

Y después de normalizar los valores a 0..1, obtengo un mapa de altura bastante aburrido como:

ingrese la descripción de la imagen aquí

¿Cómo siembro el mapa? ¿Quizás lo que necesito implementar es la versión 3d con el tercer valor de una altura aleatoria? Pero si es así, tendría que averiguar para tener en cuenta los valores vecinos, que terminaría con algo así como un algoritmo de diamante cuadrado, exactamente lo que no quiero hacer.

ACTUALIZACIÓN 3

Más trabajo de Perlin. Todavía tengo que encontrar una manera de guiar el ruido a mis resultados. Comprueba estas octavas y el resultado final:

Octava I a IV

Octava1Octava2Octava3Octava4

Resumió

Octavas 1-4 sumadas

Cada octava es más o menos lo mismo. Revisa el código:

$persistence = 0.5;

    for ($j = 0; $j < $size; $j++) {
      for ($i = 0; $i < $size; $i++) {
        $value = 0;

        for ($o = 0; $o < 4; $o++) {
          $frequency = pow(2,$o);
          $amplitude = pow($persistence, $o);
          $value += improved_noise($i*$frequency, $j*$frequency, 0.5)*$amplitude;

        }
        $this->map[$i][$j] = new Cell($value);

Los resultados están normalizados. ¿Qué utilizarías para tener una fuerte influencia en el desarrollo del ruido? Veo ejemplos en los que cambiar la amplitud da superficies suaves o rugosas, pero incluso si doy una gran amplitud, veo poca diferencia.

Gabriel A. Zorrilla
fuente
Simplemente agregue varias instancias, aumentando la frecuencia y disminuyendo la amplitud cada vez, como: perlin (x) + 0.5 * perlin (2 * x) + 0.25 * perlin (4 * x) + ... (para tantas octavas como usted quiere). También puede intentar alterar los factores para obtener diferentes looks; no necesitan ser poderes de 2.
Nathan Reed
1
Después de la actualización, parece que no está escalando Y correctamente: estoy demasiado cansado para asimilar PHP (ya que no sé PHP); pero tuve un problema similar en mi lengua materna cuando implementé Perlin la primera vez. También mata las octavas y solo depura un nivel de perlin.
Jonathan Dickinson
¿Alguien para mi actualización III?
Gabriel A. Zorrilla

Respuestas:

28

Lo que implementaste no es ruido de Perlin. No estoy seguro de por qué Hugo Elias dice que sí, pero está confundido. Aquí está la implementación de referencia de Ken Perlin. En realidad, no llama a ningún generador externo de números aleatorios, pero usa una función hash incorporada para producir los vectores de gradiente pseudoaleatorio.

Tenga en cuenta también que el ruido Perlin consta de solo una octava. Resumir varias octavas (instancias escaladas de la función de ruido), como sugiere Hugo Elias, es una técnica útil, pero no parte del ruido de Perlin. Lo que se obtiene al hacer eso se llama ruido fractal, a veces "ruido browniano fractal" (debido a la supuesta similitud con el movimiento browniano).

Si desea comprender geométricamente lo que está haciendo el algoritmo, pruebe este documento . Se trata de un tipo diferente de ruido llamado "ruido simplex", pero también incluye una explicación del ruido clásico de Perlin. Por cierto, el ruido simple también fue inventado por Perlin y se supone que es una mejora sobre su ruido clásico, por lo que podría intentar implementarlo también si está interesado en jugar con funciones de ruido.

Nathan Reed
fuente
2
+1 para el artículo de Gustavson. Explica tanto el ruido perlin como el simplex de la manera más clara que he visto hasta ahora. Obviamente, las reglas de ruido simplex!
FxIII
También encontré ese papel hace algún tiempo, pero el de Hugo parecía más simple. ¡Lo leeré y lo intentaré! ¡Gracias!
Gabriel A. Zorrilla
2
tenga cuidado al descargar ruido simplex, podría tener un virus;)
bobobobo
Sé que este es un tema antiguo, pero decir que la implementación de referencia no usa un número aleatorio es incorrecto. Cuando se inicializa la biblioteca (o la primera vez que se llama una función de ruido) se generan 256 gradientes aleatorios. El hashing al que te refieres es simplemente forzar el conjunto infinito de enteros en el rango [0, 255] que está en caché. Esencialmente, esto es solo una optimización de tabla de búsqueda, y el algoritmo funciona igual de bien si, por ejemplo, siembras un PRNG con una coordenada de cuadrícula y lo usas para generar el gradiente, de esa manera es (mucho) más lento.
bcrist
@bcrist Creo que te refieres a una versión anterior del ruido de Perlin. El "ruido mejorado" de Perlin, al que me vinculé , utiliza un conjunto fijo de 12 vectores de gradiente, no 256 aleatorios. Utiliza una tabla de permutación como función hash para asignar coordenadas de cuadrícula a uno de esos 12 vectores de gradiente.
Nathan Reed
11

Ese es un error común. Lo que Hugo Elias llama ruido "Perlin" es, de hecho, ruido fractal o rosado. Para comprender mejor qué es el ruido de Perlin, puede leer el artículo de Perlin vinculado en la respuesta de Nathan Reed, o los documentos de libnoise (existe el mismo error: el ruido de Perlin es lo que llaman ruido de degradado), o los documentos de CoherentNoise .

Ahora, para responder realmente a su pregunta: no obtuvo el resultado esperado porque la frecuencia de ruido es demasiado alta. Sus frecuencias comienzan con 1 y aumentan, lo que significa que cada píxel en el mapa resultante tiene un valor aleatorio. Para ver una estructura más fina del mapa, debe "acercar" el ruido. Realmente no hablo PHP, pero supongo que el código debería verse así:

$arrayMap[$i][$j] = PerlinNoise_2D($i/$width, $j/$height, $p, $octaves);

Es decir, usted "extiende" un período de ruido sobre todo el mapa. Por supuesto, puede usar otros coeficientes, solo pruebe diferentes, vea qué sucede.

No importa
fuente
Gracias por los documentos coherentes de ruido! Veo que lo has escrito :) ¿Cuál es el error en libnoise docs? ¿No es el ruido de Perlin un tipo de ruido de gradiente?
legends2k