見出し画像

妖しいトンネル:Processing 作例

ボロボロの廃屋。蔦が絡まる裏手の木戸。そこに開いた隙間。その隙間は暗く、奥は真っ暗。ここからでは中は見えない。
そんな隙間、覗き込むなんて恐ろしくてできない。手を入れてみるなんて考えただけでもゾッとする。

でも…
覗き込んだら何が見えるんだろう? 手を突っ込んだら一体どうなるだろう?
怖い、けどやってみたい…

恐怖より好奇心が勝ってしまう、それが人の子、人類はそうやって進歩してきたんですよね?

というわけで、人類の進歩のために、綺麗だけどちょっと怖い、興味を惹かれるけどなんだか気味悪い、そういうものを作ってみようと思いました。

お花のトンネルで「わぁ綺麗なお花〜」なんて覗き込んでみるけど奥までは見えなくて、その先に何かが潜んでいそうな妖しい雰囲気、というテーマで作ってみました。

この記事は全文無料でお読みいただけます。もしお気に召しましたら投げ銭お願いしますね。😉✨

画像1

今回の作例

上の例はお花のトンネルですが、このお花の描画部分はコードが長い上に今回の主題はそこではないので、この作例紹介ではお花ではなくて、単純な円を使って説明いたします。
円を書くところを変えればオリジナルのトンネルを作れると思います。
死んだ魚の眼なんて描いたら、それはそれは恐ろしいものになりそう。うわぁ、もう怖い!

お花のトンネルのコードはこちらに掲載しています。


画像2

トンネル描画の試行錯誤

トンネルを描くことを考えると、奥を暗くして遠近法で配置していくとよい気がします。
ただ、勘で配置するとこんな感じになっちゃいます。

画像3

こういうとき、どういう数式を当てはめるとよいかを考えるのに、私はよく以下のようなサイトでグラフを描いて考えます。

これを使って数式を決めてトンネルを描いてみたのがこれ。

画像4

悪くないんですけど、なんだかあまり奥行きが感じられない気がします。

実際に草むらとか、木の葉の生い茂ったところを観察してみると、 奥の暗いところと手前の明るいところでは、明るさだけではなくて彩度も違う気がします。

画像5

画像6

これは木の葉特有かもしれませんが、手前の方が彩度が低く、奥の方が彩度が高い(色が濃い)ように見えます。

試しに手前の彩度を高くしたもの。

画像7

奥の方の彩度を高くしたもの。

画像8

こっちの方がしっくりきますね。

さらに、穴の奥を真っ黒ではなくて、円と同じ色の暗い色にすることで妖しさが増す気がします。
それがこれ。

画像9

うん! いい感じになりました。

画像10

サンプルコード

これらを盛り込んだコードがこちらです。

// 妖しいトンネル:Processing 作例
// @deconbatch
// Processing 3.2.1
// 2018.06.17

void setup() {

  size(960, 960);
  colorMode(HSB, 360, 100, 100, 100);
  imageMode(CENTER);
  smooth();
  noLoop();
  noStroke();

}

void draw() {
  
  translate(width / 2, height / 2);

  float hueBase = random(360.0);
  background(hueBase, 100, 10, 100);
  blendMode(BLEND);

  // イメージを描いてコピーする
  pushMatrix();
  drawFlower(hueBase);
  popMatrix();
  PImage imgFlower = get(width / 2 - 200, height / 2 - 200, 400, 400);

  // コピーしたイメージを使ってトンネルを描く
  background(hueBase, 100, 10, 100);
  blendMode(LIGHTEST);

  int depthMax = 50;  // トンネルの深さ
  for (int depth = depthMax; depth >= 1; --depth) {

    // 深いほど、イメージを小さく、配置を狭く、色を濃く、明るさを暗くする
    float ratio     = map(depth, 1, depthMax, 0.0, 0.9);
    int   flwSize   = floor(width * 0.5 * (1.0 - sqrt(ratio)));
    float flwLayout = width * 0.7 * (1.0 - sqrt(ratio));
    float flwSat    = 1.0 + sqrt(ratio);
    float flwBri    = pow(1.0 - sqrt(ratio), 2);
    
    PImage tmpFlower = imgFlower.copy();
    tmpFlower.resize(flwSize, flwSize);
    repaintFlower(
                  tmpFlower,
                  flwSat,
                  flwBri
                  );
    layFlower(tmpFlower, flwLayout);
    rotate(PI*random(1.0));

  }

  saveFrame("frames/tunnel01.png");
  exit();

}

void repaintFlower(PImage img, float saturationRatio, float brightnessRatio) {
  
  for (int idxW = 0; idxW < img.width; ++idxW) {  
    for (int idxH = 0; idxH < img.height; ++idxH) {
      int   idxPoint = idxH * img.width + idxW;
      color cPoint   = img.pixels[idxPoint];
      float valHue   = hue(cPoint);
      float valSat   = saturation(cPoint) * saturationRatio;
      float valBri   = brightness(cPoint) * brightnessRatio;
      float valAlp   = alpha(cPoint);
      img.pixels[idxPoint] = color(valHue, valSat, valBri, valAlp);
    }
  }

}

void layFlower(PImage flower, float radius) {

  for (float radian = 0; radian < 2.0 * PI; radian += PI / 8.0) {
    if (random(1.0) < 0.8) {
      float imgRadius = map(random(1.0), 0.0, 1.0, radius * 0.9, radius * 1.1);
      image(flower, imgRadius * cos(radian), imgRadius * sin(radian));
    }
  }

}

void drawFlower(float baseColor) {

  fill(baseColor, 40, 80, 100);
  ellipse(0, 0, 300, 300);

}

/*
Copyright (C) 2018- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

このコードのミソは、トンネル全てを正直に描画するのではなく、トンネルを形作る要素を一回描画し、それをコピーして使いまわすというところです。

その描画とコピーの部分はここ。

  // イメージを描いてコピーする
  pushMatrix();
  drawFlower(hueBase);
  popMatrix();
  PImage imgFlower = get(width / 2 - 200, height / 2 - 200, 400, 400);

途中でブレンドモードを変更していますが、

  blendMode(LIGHTEST);

これが無いとこうなります。

画像11

コピーしたときに背景の暗いところも含めてコピーしているので、そのまま描画すると背景部分で塗りつぶしていってしまいます。

コピーしたイメージのサイズは resize() で変更、明るさや彩度を変えているのは repaintFlower()、それを配置しているのが layFlower() です。
layFlower() のロジックを変えればトンネルじゃなくて谷とか、崖とか塔とかも描けそうですね。

この作例では drawFlower() で単なる円を描画していますが、ここで花を描画すればお花のトンネルになります。

画像12

もし興味がわきましたら、皆さんもここにいろんなものを描画して楽しんでみてください。

最後までお読みいただき、ありがとうございました。


ここでこの記事はおしまいです。もしこの記事がお気に召しましたら投げ銭お願いします。😉✨

ここから先は

0字

¥ 100

この記事が面白かったらサポートしていただけませんか? ぜんざい好きな私に、ぜんざいをお腹いっぱい食べさせてほしい。あなたのことを想いながら食べるから、ぜんざいサポートお願いね 💕