見出し画像

つぶやきProcessingで描いた炎のメイキング

Processing Advent Calendar 2023 参加記事

というわけで↓の作品のメイキングを書きます!

1. 正方形を敷き詰める

まずは画面全体に小さな正方形をびっしり敷き詰めます。

正方形を縦横ぎちぎちに敷き詰める
const squareSize = 5;

function setup() {
  createCanvas(400, 400);
}

function draw() {
  // 小さな正方形を縦横に敷き詰める
  for (let x = 0; x < width; x += squareSize) {
    for (let y = 0; y < height; y += squareSize) {
      square(x, y, squareSize);
    }
  }
}

2. noiseを利用して色をつける

正方形の塗りつぶしの色に対して、noiseで白〜黒の濃淡をつけます。
さらに、y方向の値にframeCountを入れて、縦方向に動きをつけます。
煙のようなビジュアルになりました!

正方形にnoiseで濃淡をつける
const squareSize = 5;

function setup() {
  createCanvas(400, 400);
}

function draw() {
  for (let x = 0; x < width; x += squareSize) {
    for (let y = 0; y < height; y += squareSize) {
	  // noiseを使って正方形の色に濃淡をつける
      // y方向にframeCountを入れて動かす
      let n = noise(x * 0.01, y * 0.01 + frameCount * 0.05);
      let c = n * 255;
	  fill(c);
      square(x, y, squareSize);
    }
  }
}

3. 正方形の枠線を消す

noStroke()を設定して、正方形の枠線を消します。

正方形の枠線を消した。ますます煙っぽい
const squareSize = 5;

function setup() {
  createCanvas(400, 400);
  noStroke(); // 追加
}

function draw() {
  for (let x = 0; x < width; x += squareSize) {
    for (let y = 0; y < height; y += squareSize) {
      let n = noise(x * 0.01, y * 0.01 + frameCount * 0.05);
      let c = n * 255;
	  fill(c);
      square(x, y, squareSize);
    }
  }
}

4. 色を二値化

適当なしきい値を設定して、各正方形を描写する際にnoiseの値がそれより大きいかどうかのふるいにかけます。
noiseの値がしきい値を越えたら黒、そうでなければ白に正方形を塗りつぶします。

noiseを基準に二値化。牛の模様みたい
const squareSize = 5;

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  for (let x = 0; x < width; x += squareSize) {
    for (let y = 0; y < height; y += squareSize) {
      let n = noise(x * 0.01, y * 0.01 + frameCount * 0.05);
      // 色を二値化
      // 適当なしきい値を設け、noiseの値がそれを越えたら黒、
      // そうでなければ白にする
      let c = n > 0.5 ? 0 : 255;
      fill(c);
      square(x, y, squareSize);
    }
  }
}

5. しきい値を縦方向に調整する

正方形が上からどの位置にあるかによって、「黒になりやすさ」「白になりやすさ」を調整していきます。

正方形の位置によって白になりやすさ、黒になりやすさを調整。なんか炎っぽい
const squareSize = 5;

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  for (let x = 0; x < width; x += squareSize) {
    for (let y = 0; y < height; y += squareSize) {
      let n = noise(x * 0.01, y * 0.01 + frameCount * 0.05);
      // 上から何割の位置にあるかを計算し、
      // 上に行くほど黒くなるように調整
      let c = n > y / height ? 0 : 255;
      fill(c);
      square(x, y, squareSize);
    }
  }
}

yには縦方向の座標位置の値が入るため、それをheightで割ると、「上から何割地点か」がわかる値になります。
上側が0に近く、下側が1に近い値を取ります。

この値を「正方形が白になる確率」として考えてみましょう。
0から1までの値を取るので、確率0%〜100%と考えてみます。
一番上は正方形が白になる確率は0%です。
そして下に行けば行くほど正方形が白になる確率は上がり、一番下で100%になります。

縦方向に「白になりやすさ」を考える

noiseの値をこの確率のふるいにかけます。
noiseは0から1の値を取るため、下記の式は…

let n = noise(x * 0.01, y * 0.01 + frameCount * 0.05);
let c = n > y / height ? 0 : 255;
fill(c);

こんな感じの意図になります。

// 正方形の位置ごとにnoiseの値を取得
let n = noise(x * 0.01, y * 0.01 + frameCount * 0.05);
// 正方形の位置が上にあればあるほど、0になりがちになるようにセット
// 下にあればあるほど255になりがちになるようにセット
let c = n > y / height ? 0 : 255;
// 0 もしくは 255 の値を塗りつぶしの色として設定
fill(c);

6. 白い部分をグラデーションカラーにする

今まで白かった部分(色として255を設定していた部分)に対して、赤〜黄色のグラデーションカラーを適用します。
lerpColorを使ってグラデーションになるようにします。
y / heightの値をlerpColorの割合として設定して、縦方向にグラデーションするようにします。
lerpColorの割合も0から1の間を取らないといけないのでちょうどいいんですよね。

グラデーションを適用。かなり炎っぽい
const squareSize = 5;

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  for (let x = 0; x < width; x += squareSize) {
    for (let y = 0; y < height; y += squareSize) {
      let n = noise(x * 0.01, y * 0.01 + frameCount * 0.05);
      // 今まで白い領域だったところを炎として描画する
      // 白い領域だったところを赤〜黄色のグラデーションにする
      let gradient = lerpColor(color("#BF2626"), color("#F8D500"), y / height);
      let c = n > y / height ? 0 : gradient;
      fill(c);
      square(x, y, squareSize);
    }
  }
}

7. パラメータを調整してガンガンに燃やす

noiseのパラメータを調整し、炎をガンガンに燃やします。

noiseのパラメータを調整。強火。
const squareSize = 5;

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  for (let x = 0; x < width; x += squareSize) {
    for (let y = 0; y < height; y += squareSize) {
      // noiseの値をいい具合にする
      let n = noise(x * 0.0175, y * 0.05 + frameCount * 0.5);
      let gradient = lerpColor(color("#BF2626"), color("#F8D500"), y / height);
      let c = n > y / height ? 0 : gradient;
      fill(c);
      square(x, y, squareSize);
    }
  }
}

8. 文字数を削減し、つぶやく

文字数を削減して、「#つぶやきProcessing」できるようにします。

// つぶやくためにコードを圧縮
// * 画面サイズを定義して使い回す
// * 正方形のサイズを定義せずに数字をベタ書き
// * 色の指定方法の記述を圧縮
// 
// 最終的にツイート(ポスト)する際にはスペースや改行を除去

let S = 400;
function setup() {
  createCanvas(S, S);
  noStroke();
}
function draw() {
  for (x = 0; x < S; x += 5) {
    for (y = 0; y < S; y += 5) {
      fill(
        noise(x * 0.0175, y * 0.05 + frameCount * 0.5) > y / S
          ? 0
          : lerpColor(color("#BF2626"), color("#F8D500"), y / S)
      );
      square(x, y, 5);
    }
  }
}

完成〜!
上手につぶやけました!


こんな感じで炎の質感を短いコードで表現できて満足はしているんですが、現状「四角い画面の下部で炎が燃えている」感じなので、火の玉のような造形にするにはどうしたらいいんだろうな〜と考えています。
こういう感じ→🔥
しずくのような形で、上の先端に向かって細くなっていくイメージですね。
もし「こういうやり方があるよ!」というアイデアをお持ちの方がいらっしゃいましたらぜひ教えてください…!

また、本記事の執筆にあたり、p5.captureに大変助けられました。
すごく使いやすいgif化・動画化ライブラリなのでオススメです!

というわけでつぶやきProcessingの炎メイキングでした!
みなさま今年もありがとうございました!
年末年始も元気にクリエイティブコーディングしていこうな!

Processingとp5.jsとクリエイティブコーディングが大好きです。 めちゃくちゃ元気!