Midterm Progress #2 – Landscape

Concept:

My midterm project consists of a landscape NFT collection as a combination of my interest in cryptography and generative art. It uses different mathematical and artistic concepts to produce unique nature-related artwork. Through a GUI, the user can specify the style (acrylic, japanese, animated…) and generate their own NFT. As for choosing nature as a theme, I was inspired by the course’s name “decoding nature”.

Librairies/Algorithms:

TweakPane
Perlin Noise
Sin-Wave
Particle System
Recursion
Granulation
Linear Interpolation

Code: https://editor.p5js.org/bdr/sketches/qhH_1I3FP

Progress:

View post on imgur.com

View post on imgur.com

Code Walkthrough (Special Functions):

Vintage effect:
In order to get an ancient style, I’m using noise as a primary way to simulate the imperfections and irregularities alongside the random function. To generate the dots in the corner, I’m using the following:

function displayNoise(){
    strokeWeight(0.5);
    for(let dx = 0; dx < width; dx += random(1, 3)) {
        for(let dy = 0; dy < height; dy += random(1, 3)) {
        	let pointX = dx * noise(dy/10);
            let pointY = dy * noise(dx/10);
            if (get(pointX, pointY)[0]==241) {
                stroke(darkColor);
            } else {
                stroke(lightColor);
            }
        	point(pointX, pointY);
        }
    }
}

Mountains:
To create hand-drawn mountain contours, I’m using sin-waves relying on vertices, noise, and random functions. To get varying amplitudes I’m using the following:

let a = random(-width/2, width/2);
let b = random(-width/2, width/2);
let c = random(2, 4);
let d = random(40, 50);
let e = random(-width/2, width/2);

for (let x = 0; x < width; x ++){          
  	let y = currY[j];
  	y += 10*j*sin(2*dx/j + a);
  	y += c*j*sin(5*dx/j + b);
  	y += d*j*noise(1.2*dx/j +e);
  	y += 1.7*j*noise(10*dx);
    dx+=0.02;
    mountains.push({x,y})
}

As for the vertices and to be able to close the shape in order to be able to fill it later on, I’m using:

beginShape();
vertex(0, height);
for (let i = 0; i < mountains.length; i++) {
    stroke(darkColor);
    vertex(mountains[i].x, mountains[i].y);
}
vertex(width, height);
endShape(CLOSE);

Matrix Japanese Text:
The Japanese text represents different nature and life related quotes that are displayed vertically in a Matrix style. I’m thinking of animating it later.

["風流韻事" // Appreciating nature through elegant pursuits such as poetry, calligraphy and painting
,"柳は緑花は紅" // Willows are green, flowers are crimson; natural state, unspoiled by human touch, nature is beautiful, all things have different natures or characteristics.
,"花鳥風月" // The beauties of nature. 
,"旅はまだ途中だぞ" // Our adventure is not done yet
,"前向きにね" // Stay positive
,"雨降って地固まる" // After rain, comes fair weather
,"苦あれば楽あり" // There are hardships and also there are pleasures
,"初心忘るべからず" // Should not forget our original intention
,"浮世憂き世" // Floating world
,"自分の生きる人生を愛せ" // Love the life you are living
,"行雲流水" // Flow as the clouds and water
,"人生は夢だらけ" // Life is full of dreams
,"春になると森は青々としてくるです" // In spring, the forest becomes lush
,"今日の夕焼けはとてもきれいです" // Today's sky is nice and red
,"生き甲斐" // Realisation of what one expects and hopes for in life
];

Gradient:
To get a smooth gradient, I’m using the mathematical concept: linear interpolation. It constructs new data points within the range of a given discrete set data points which are in our case colors.

function applyGradient(x, y, w, h, color1, color2) {
   noFill();
   for (let i = y; i <= y + h; i++) {
      let mid = map(i, y, y + h, 0, 1);
      let clr = lerpColor(color1, color2, mid);
      stroke(clr);
      line(x, i, x + w, i);
   }
}

Granulation and Blur:
To do that, I looped over the canvas pixels and shifted some of them randomly. To further fuzzify the artwork, I rounded the pixels after moving them as well and getting their density.

function granulateFuzzify(amount) {
    loadPixels();
    let d = pixelDensity();
    let fuzzyPixels = 2;
    let modC = 4 * fuzzyPixels;
    let modW = 4 * width * d;
    let pixelsCount = modW * (height * d);
    for (let i = 0; i < pixelsCount; i += 4) {
        let f = modC + modW;
    		if (pixels[i+f]) {
            pixels[i] = round((pixels[i] + pixels[i+f])/2);
    			  pixels[i+1] = round((pixels[i+1] + pixels[i+f+1])/2);
    			  pixels[i+2] = round((pixels[i+2] + pixels[i+f+2])/2);
    		}
        pixels[i] = pixels[i] + random(-amount, amount);
        pixels[i+1] = pixels[i+1] + random(-amount, amount);
        pixels[i+2] = pixels[i+2] + random(-amount, amount);
    }
    updatePixels();
}

Stars Particle System:
To get a stars system, I had a separate class named Star and created new instances using a for loop.

class Star {
  constructor(x, y) {
    this.xPos = x;
    this.yPos = y;
  }

  draw() {
    let randSize = random(3);
    fill(255, 255, 255, random(0, 200));
    ellipse(this.xPos, this.yPos, randSize, randSize);
  }
}

function makeStars(numStars) {
  if (starCount < numStars) {
    let s = new Star(int(random(width)), int(random(height / 1.8)));
    s.draw();
  }
  starCount++;
}

Challenges:

One of the challenges I have faced is certainly setting the right parameters for the waves to get a somewhat hand-drawn but still realistic set of mountains. It took a lot of experimentation, trial and errors to finally find the right combination. Similarly, for the gradient to be smooth and match all the components of the artwork, I had to find the right settings for the linear interpolation.

Next Steps:

Animation: animate the canvas/artwork, most probably a 3D effect.

Leave a Reply

Your email address will not be published. Required fields are marked *