Perlin mountains

Perlin noise

Log in to post a comment.

// Forked from "Gaussian mountains" by Alexjust
// https://turtletoy.net/turtle/c0af204ebf


Canvas.setpenopacity(-1);

let dens = 5 // min=1, max=10, step=1
dens=1/dens
const turtle = new Turtle();
const width = 90;
const zstart = 80;
const layer = dens*6;
const layers = 27/dens;
const perspective = 0.7*dens+0.07;

turtle.penup();
let h=0;
const leeway=29 // min=0, max=30, step=1
const amp=6 // min=-10, max=10, step=0.1
const res =1 // min=1, max=10, step=1

const rows = 600
const scale = 21.3 // min=0, max=40, step=0.1
const rad = 19 // min=0, max=50, step=1
const vermult = 1 // min=0, max=20, step=1
const cols = 600
const filledge = 1 //min=0, max=1, step=1

let gaussMat = generatePerlinNoiseMatrix(rows, cols, scale);
gaussMat = applyGaussianBlur(gaussMat, rad)

function walk(i) {
    const left = -width + perspective * i;
    const right = width - perspective * i;
    const z = zstart - layer * i;
    
    if (filledge==1){
            
        turtle.penup();
        turtle.goto(-500, z);
        turtle.pendown();
        turtle.goto(left, z);
        turtle.penup();
        turtle.goto(500, z);
        turtle.pendown();
        turtle.goto(right, z);
        turtle.penup()
        
    }
    turtle.goto(left, z);
    let u = 0;
    for (let n = left; n < right; n += res) {
        
        
        
        if (n < left + leeway ) {
            
            h = z + gaussMat[u][i*vermult]*amp*(Math.sin((n-left)/leeway)*1.21603709862);
            console.log((Math.sin((n-left)/leeway)*1.21603709862))
        } else if(n > right - leeway){
            
            h = z + gaussMat[u][i*vermult]*amp*(Math.sin((right-n)/leeway)*1.21603709862);
            
        }
        else{
            
            h = z + gaussMat[u][i*vermult]*amp;
    
        }
        
        turtle.goto(n, h);
        turtle.pendown();
        u+=1;
    }

    turtle.goto(right, z);
    turtle.penup();
    return i < layers;
}


function generateGaussianNoiseMatrix(rows, cols, scale, mean) {
  const noiseMatrix = [];

  for (let i = 0; i < rows; i++) {
    const row = [];
    for (let j = 0; j < cols; j++) {
      const u1 = 1 - Math.random();
      const u2 = 1 - Math.random();

      const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
      const z1 = Math.sqrt(-2 * Math.log(u1)) * Math.sin(2 * Math.PI * u2);

      const noiseValue = mean + scale * z0;
      row.push(noiseValue);
    }
    noiseMatrix.push(row);
  }

  return noiseMatrix;
}

// Function to apply Gaussian blur to a matrix
function applyGaussianBlur(matrix, radius) {
  const kernelSize = radius * 2 + 1;
  const kernel = [];

  for (let i = 0; i < kernelSize; i++) {
    const row = [];
    for (let j = 0; j < kernelSize; j++) {
      const x = i - radius;
      const y = j - radius;
      const weight = Math.exp(-(x * x + y * y) / (2 * radius * radius));
      row.push(weight);
    }
    kernel.push(row);
  }

  const blurredMatrix = [];

  for (let i = 0; i < matrix.length; i++) {
    const newRow = [];
    for (let j = 0; j < matrix[i].length; j++) {
      let sum = 0;
      let totalWeight = 0;
      for (let ki = 0; ki < kernelSize; ki++) {
        for (let kj = 0; kj < kernelSize; kj++) {
          const mi = i - radius + ki;
          const mj = j - radius + kj;
          if (mi >= 0 && mi < matrix.length && mj >= 0 && mj < matrix[i].length) {
            sum += matrix[mi][mj] * kernel[ki][kj];
            totalWeight += kernel[ki][kj];
          }
        }
      }
      newRow.push(sum / totalWeight);
    }
    blurredMatrix.push(newRow);
  }

  return blurredMatrix;
}

function generatePerlinNoiseMatrix(width, height, frequency) {
  const grid = [];
  for (let i = 0; i < width; i++) {
    grid.push([]);
    for (let j = 0; j < height; j++) {
      const angle = Math.random() * 2 * Math.PI;
      grid[i].push({ x: Math.cos(angle), y: Math.sin(angle) });
    }
  }

  function dotProductGradient(x, y, gradient) {
    return (x * gradient.x) + (y * gradient.y);
  }

  function interpolate(a, b, t) {
    return (1 - t) * a + t * b;
  }

  function smoothstep(t) {
    return t * t * (3 - 2 * t);
  }

  const noiseMatrix = [];
  for (let y = 0; y < height; y++) {
    noiseMatrix.push([]);
    for (let x = 0; x < width; x++) {
      const cellX = Math.floor(x / frequency);
      const cellY = Math.floor(y / frequency);

      const topLeft = dotProductGradient(x - cellX * frequency, y - cellY * frequency, grid[cellX][cellY]);
      const topRight = dotProductGradient(x - (cellX + 1) * frequency, y - cellY * frequency, grid[cellX + 1][cellY]);
      const bottomLeft = dotProductGradient(x - cellX * frequency, y - (cellY + 1) * frequency, grid[cellX][cellY + 1]);
      const bottomRight = dotProductGradient(x - (cellX + 1) * frequency, y - (cellY + 1) * frequency, grid[cellX + 1][cellY + 1]);

      const tx = smoothstep((x - cellX * frequency) / frequency);
      const ty = smoothstep((y - cellY * frequency) / frequency);

      const interpolateTop = interpolate(topLeft, topRight, tx);
      const interpolateBottom = interpolate(bottomLeft, bottomRight, tx);

      noiseMatrix[y].push(interpolate(interpolateTop, interpolateBottom, ty));
    }
  }

  return noiseMatrix;
}