Terrain With Perlin Noise

Ejercicio 2: Generación infinita en videojuegos #

1. Introducción

El Ruido Perlin es una función matemática que utiliza la interpolación entre un gran número de gradientes de vectores precalculados para crear un valor que varía de forma pseudoaleatoria en el espacio o en el tiempo. Se asemeja al ruido blanco y se utiliza con frecuencia en imágenes generadas por ordenador para simular la variabilidad de muchos tipos de fenómenos, dándoles un aspecto más realista.

Perlin Noise es un método matemático para generar fluctuaciones aleatorias pero muy contiguas, por lo que, a diferencia de cualquier tipo de función aleatoria, es una manera muy apta para generar terrenos aleatorios que, aunque aleatorios, requieren una forma coherente.

Lee más sobre el Ruido Perlin y otros métodos de generación de terreno aleatorio en: https://medium.com/nerd-for-tech/generating-digital-worlds-using-perlin-noise-5d11237c29e9

2. Solución

En los videojuegos y ambientes virtuales se suele procurar optimizar los recursos de la maquina para dar una mejor experiencia al usuario. Una forma de realizar esto es haciendo uso de la renderización parcial: Esto evita que la máquina se cargue con trabajo que de todos modos no será visible para el usuario.

En esta solución se propone la generacion aleatoria de terreno para un simulador de vuelo simple.

Código
var cols, rows; // variables para almacenar el número de columnas y filas en el mapa de ruido
var scl = 20; // escala para el tamaño de los bloques de ruido
var w = 1400; // ancho del mapa de ruido
var h = 1000; // altura del mapa de ruido

var flying = 0; // variable para almacenar el valor del desplazamiento vertical del mapa de ruido

var terrain = []; // arreglo vacío que almacenará el valor del mapa de ruido en cada posición

function preload(){
    airplane = loadModel('/showcase/sketches/terrain/airplane.obj'); // precargar el modelo del avión
}

function setup() {
  createCanvas(800, 800, WEBGL); // crear un canvas en modo WEBGL
  cols = w / scl; // calcular el número de columnas del mapa de ruido
  rows = h / scl; // calcular el número de filas del mapa de ruido
  slider = createSlider(-300,300,0,1); // crear un slider para desplazar la cámara
  slider.position(10,10); // colocar el slider en la esquina superior izquierda
  camera = createCamera(); // crear una cámara para la escena

  for (var x = 0; x < cols; x++) {
    terrain[x] = [];
    for (var y = 0; y < rows; y++) {
      terrain[x][y] = 0; // inicializar el valor del mapa de ruido en cada posición con cero
    }
  }
}

function draw() {

  flying -= 0.1; // decrementar el valor del desplazamiento vertical del mapa de ruido
  var yoff = flying; // almacenar el valor actual del desplazamiento vertical del mapa de ruido
  for (var y = 0; y < rows; y++) {
    var xoff = 10;
    for (var x = 0; x < cols; x++) {
      terrain[x][y] = map(noise(xoff, yoff), 0, 1, -200, 55); // mapear el valor del ruido a un valor entre -200 y 55
      xoff += 0.2;
    }
    yoff += 0.2;
  }

  background(0); // definir un fondo negro para la escena
  translate(0, 50); // trasladar el sistema de coordenadas hacia abajo para mostrar la escena completa
  rotateX(PI / 3); // rotar la escena alrededor del eje X para mostrar la vista desde arriba
  let sld=slider.value(); // almacenar el valor actual del slider
  camera.camera(230,400,600,200,100,200,0,1,0); // definir la posición inicial de la cámara
  camera.setPosition(sld,400,600); // actualizar la posición de la cámara con el valor del slider
  push();
  scale(0.03); // escalar el modelo del avión
  fill(0,0,0,150); // definir un color negro semitransparente para el modelo del avión
  rotateZ(55); // rotar el modelo del avión alrededor del eje Z
  model(airplane, true);
  pop();
  fill(200, 200, 200, 150);
  translate(-w / 2, -h / 2);
  for (var y = 0; y < rows - 1; y++) {
    noStroke();
    beginShape(TRIANGLE_STRIP);
    for (var x = 0; x < cols; x++) {
        fill(100,0,255,150)
      vertex(x * scl, y * scl, terrain[x][y]);
      vertex(x * scl, (y + 1) * scl, terrain[x][y + 1]);
    }
    endShape();
  }
  
}

El avión se incuye como un archivo .OBJ, que es un estándar para representar diseños en 3D a partir de sus coordenadas.

El usuario jwdunn1 propone la generación de un mundo inspirado en el popular videojuego Minecraft haciendo uso del ruido Perlin:

3. Conclusión

El ruido Perlin brinda un patron tan impredecible como la naturaleza misma, lo cual puede ser aprovechado para hacer creer a nuestras mentes que estamos viendo paisajes naturales en los que incluso podemos crear una falsa sensación de movimiento.