はじめに
このページでは、Creative Coding でよく見かける、 全体的に塗りつぶす系の作品をどのように作るのか、 その基礎的な部分について説明していきます。
基本的なアイデア
すべての画素を塗りつぶすプログラムを書くだけです。 例えば、以下のコードでは、400 x 300 ピクセル(画素)の すべての領域を示す (x,y) を生成します:
def setup():size(400,300)
def draw():
for y in range(300):
for x in range(400):
setColorAt(x,y)
:
setColorAt(x,y) というのは、適当に作った架空の関数です。 この関数の意図するところは、ピクセル (x,y) の位置を与えると、 何か良い感じの色を設定して返してくれる関数と想定しています。
setColorAt 関数により、すでに「いい感じ」に色が設定されているので、 あとは、位置 (x,y) にあるピクセルを 塗りつぶしていけば、めでたく 400 x 300 の領域が塗りつぶせることとなります。
1 ピクセル単位では遅い?
このように、1 ピクセル(画素)単位でもよいですが、 例えば、400 x 300 ピクセルの描画領域でも、 12,0000 個のピクセルが存在します。 それらをひとつづつ塗りつぶしていくには、 それなりに負荷がかかります
ある程度、高速なパソコンであれば問題は無いと思いますが、 でももし setColorAt 関数にて複雑な計算をしているのであれば、 400 x 300 ピクセルの領域を塗りつぶすために、 少々時間を必要としてしまうかもしれません。
その結果、プログラムによる生成されるアニメーションが、 カクカクとぎこちなく動いてしまう場合もあります。
なめらかなアニメーションのためには
1 秒間に書き換えられる画面(フレーム)が多い程、 我々人間は滑らかな動きであると感じます(厳密に言うと上限が存在するようですが)。 そのため、ある程度なめらかなアニメーションを実現するため、 可能であれば 1 秒間に 20 〜 30 回ほど画面を書き換えたいものです (ちなみに格闘ゲームなどでは秒間 60 回も画面を書き換えているものも存在します)。
そのためには、計算負荷を減らす必要があります。 簡単なテクニックとしては、1 ピクセル毎に色を決めるのではなく、 10 x 10 ピクセル単位で色を決める、という方法があります。
例えば、400 x 300 ピクセルでも、 10 x 10 ピクセル単位で色を塗りつぶしていけば、 setColorAt 関数の呼び出し回数は 40 x 30 = 1200 回となります。 1 ピクセル毎に計算する場合と比べ、その処理負荷は 1/100 になります。
def setup():size(400,300)
def draw():
for y in range(30):
for x in range(40):
setColorAt(x,y)
square(x*10,y*10,10)
:
これは、ものすごく乱暴に行ってしまうと、 1 ピクセル単位で計算するよりも 100 倍高速になっています。
サンプルコード
setColorAt 関数を工夫すると、次のようなアニメーションも可能となります。
#つぶやきProcessing
— Koji Saito (@KojiSaito) February 20, 2020
def setup():size(500,500)
s=1.0e3;r=range
def draw():
global s;w=250;R=r(-w,w,8);s+=50
for v in R:
for u in R:
c=complex(u/s-.7,-v/s+.3);z=0
for t in r(33):
z=z*z+c
if abs(z)>1:break
t-=32;fill(t%8*48,t%7*47,t%9*46);rect(u+w,v+w,8,8) pic.twitter.com/wXO3w6yJEc
このツィートでは、setColorAt 関数は定義していませんが、 処理の基本構造は全く同じです。
この作品の詳しい作り方については、 ここで説明すると長くなってしまいますので、 別の記事にゆずりたいと思います。
ここでは、単にランダムに色を塗りつぶすコードを紹介したいと思います。
def setup():size(400,300)
def setColorAt(x,y):
fill(random(255),random(255),random(255))
def draw():
for y in range(30):
for x in range(40):
setColorAt(x,y)
square(x*10,y*10,10)
ランダムに色を設定するだけなので、 setColorAt 関数では、x,y という引数を受け取っていますが、 実際の処理では使用していません。
今回は説明のため、setColorAt という関数を使っていますが、 実際のコードでは関数を定義せず、直接処理を記述する場合もあります。 例えば、上のコードの場合、次のコードのように書いていまうこともあります:
def setup():size(400,300)
def draw():
for y in range(30):
for x in range(40):
fill(random(255),random(255),random(255))
square(x*10,y*10,10)
特に、 つぶやきProcessing では、 文字数削減が重要なので、関数を定義しない場合もあります (もちろん、関数を定義した方が文字数削減できる場合もあります)。
これで、全体的に塗る系のプログラム構造の基本に関する説明を終わりたいと思います。