Midterm Project – Realms

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.

Think of NFTs as digital collectibles, like trading cards or unique stamps. When an artist creates a digital artwork, they can “mint” an NFT, which essentially means they create a special, one-of-a-kind digital token associated with that artwork.

In my project, through a GUI, the user can specify the style (acrylic, japanese, animated…) and mint their own NFT. The revolving theme is Nature, as I was inspired by the course’s name “decoding nature”.

Example from OpenSea – NFT marketplace:

View post on imgur.com

Sketch:

For a better experience (Full Screen), use the following link.

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. i.e. most shapes have irregular edges, cracks, and distortions.

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);
        }
    }
}

Hand-drawn effect:
To create hand-drawn 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);

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.

View post on imgur.com

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.

View post on imgur.com

The code for it is:

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();
}

Branches generation:
To display realistic branches, I’m alternating the size after each iteration. The branch is drawn using a sequence of circles. I’m using a seed variable called “BranchChance” to add randomness.

display() {
    if (this.size < 2) {
      return false;
    }

    stroke(0);
    fill(0, 0, 0, min(255, 5 * this.size));
    ellipse(this.x, this.y, this.size, this.size);

    this.x += (this.size / 7) * cos(this.angle);
    this.y += (this.size / 7) * sin(this.angle);
    this.size *= random(0.99, 1);
    this.angle += random(-0.05, 0.05);

    if (this.size < 10) {
      this.angle += random(-0.1, 0.1);
    }
    if (this.size < 30) {
      this.angle += random(-0.1, 0.1);
    }

    if (
      (this.size < 10 && random(0, 1) < 0.2 * branchChance) ||
      (this.size > 4 && random(0, 100 / (this.t + 1)) < branchChance)
    ) {
      this.branches.push(new Branch(this.x, this.y, this.angle - PI / 7, this.size, 0));

      this.angle += PI / 7;
      this.t = 0;
    }

    for (let i = this.branches.length - 1; i >= 0; i--) {
      if (!this.branches[i].display()) {
        this.branches.splice(i, 1);
      }
    }
    this.t++;
    return true;
}

Noise Gradient background:
To set a sunset gradient for the canvas with some noise, I’m using ellipses once again as per the following:

function animateSunset(){
  clear();
  let cells = 128;
  let sat = 255;
  let bri = 192;
  let maxNoiseScale = 14;
  let p = 8;
  let cell = width / cells;

  randomSeed(0);

  z2 += 0.001;
  xoffset += 0.001;

  let noisePower = Math.pow(10, Math.log10(maxNoiseScale) / cells);

  let passes = 1;
  for (let z = 1; z <= passes; z++) {
    let cols = cells;
    let rows = cells;
    let noiseScale = noisePower;

    for (let j = -2; j < rows + 15; j++) {
      let y = (j / rows);
      let wy = y * height;

      for (let i = -2; i < cols + 8; i++) {
        let x = (i / cols);
        let wx = x * width;

        // Color
        let q = noise((x * noiseScale) + xoffset, y * noiseScale, z2);
        if (q > 0.5) {
          q = (((Math.pow(((q - 0.5) * 2) - 1, p) - 1) * -1) / 2) + 0.5;
        }
        else {
          q = Math.pow(q * 2, p) / 2;
        }
        q = ((q * 150) + 145) % 255;
        let boxColor = color(q, sat, bri, random(11) + 1);
        fill(boxColor);

        // Draw circle
        let factor = noise(x + xoffset, y, z2) * 23 + 1;
        let diameter = cell * factor;
        ellipse(wx, wy, diameter, diameter);
      }
      noiseScale *= noisePower;
    }
  }
}

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
];
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 both the gradients to be smooth and match all the components of the artwork, I had to find the right settings for the linear interpolation.

Future Plans:

Incorporate 3D elements using WebGL or other 3D engines.
Allow more customization through the GUI.

Plotter result:

View post on imgur.com

 

View post on imgur.com

Project Images:

View post on imgur.com

View post on imgur.com

 

Leave a Reply

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