ジェネラティブアート作成時の頭のなか #AltEdu2022 4 日目
#AltEdu2022 4 日目のお題は、文字を使ったグラフィック - とのこと。
ちなみに明日以降の予定はこんな感じとのこと:
さて、作品作りにとりかかっていこう。とは言うものの、実のところ文字を使った作品はこれまであまり作ったことがなく、以下に示す 1 作品のみだったような気がする。
単に文字を使った作品だけではなく、文字が示す内容とグラフィックが近くなるようにする必要もある。
すぐに思いついたのは、夕日の作品。
これを SUNSET という文字列でマスクしたらどうだろうか?
かなり安易な発想で、なんのひねりもなく恐縮である。でも、そもそも毎回ヒットやホームラン級のアイデアが出るわけではない(これまでの私の作品制作においても、ヒットですら怪しいところではあるが、それはそれとして)。もちろん、いつもヒットやホームランを連発する凄い人も世の中にはいるとは思う。でも、そうではない私のような人間でも、作品を求められている時や、自分で作ると決めた時には、何らかを作りだせる工夫を持っておかなければならないと思う。むしろアイデアが出ないときこそ、なんとか捻り出すような底力を養うことこそが重要なんだと思う。
というわけで、自分の貧困な発想力に諦めを感じつつ、コードを書いてみる。#つぶやきProcessing 化を考えると、動画では文字数的に難しいと思う。なので静止画用のコードを書いてみる。ある意味、プログラムの合成を行うわけである。
冒頭の PROCESSING という文字を用いた作品では、ピクセル値毎の掛け算を行う処理を私が書き、updatePixels 関数を用いて更新していた。しかし、そんなことをしなくとも blendMode(MULTIPLY) とすれば十分である。時間をおいて考えると、改善案が見ててくる。というわけで、以下のコードでは blendMode 関数を利用することで文字数削減も行っている:
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",10+i%8,300+i/8)
blendMode(MULTIPLY)
for iy in range(200):
for ix in range(250):
d=dist(ix,iy,150,-100)
t=noise(ix*.01+frameCount*.02,iy*.05)
fill((t+.7)*pow(d,5)*2e-10,(t+.5)*d*d*.002,(t+.2)*d*.6)
rect(ix*2,iy*2,2,2)
なんだか昭和時代のコードジャケットのようだ。これはこれで魅力的なんだが、文字の形より覗くグラフィックは、夕日という感じが弱いように思える。もうすこし sunset 感が欲しい。
夕日部分を表示する面積が少ないから、夕日かどうか分からないのではないか?と思い、であれば夕日を表示する部分の面積を多くしようと考える。要はもっと文字を描くべきだ、という考えだ。さて、どのような文字列を描けば良いのだろう…などと一瞬考えるが、sunset とくれば memories だろうと、何の根拠もなく思いつく。sunset memories …夕日の思い出。よく分からないけど、まあ良いだろう。
というわけで、SUNSET MEMORIES という文字列で夕日をマスクするようにしてみる:
うん。悪くない。…とはいえ、まだまだ夕日感が足りない気もする。マスクされた領域だけでは、夕焼け雲の模様の変化が乏しく感じられてしまうのではないだろうか?…という仮設をたてて、であれば noise 関数の変調の程度をもう少し上げるように変更してみる。ついでに、本作ではアニメーションはしないので frameCount を使っている部分も消去する:
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",10+i%8,300+i/8);text("MEMORIES",10+i%8,375+i/8)
blendMode(MULTIPLY)
for iy in range(200):
for ix in range(250):
d=dist(ix,iy,150,-100)
t=noise(ix*.02,iy*.1)
fill((t+.7)*pow(d,5)*2e-10,(t+.5)*d*d*.002,(t+.2)*d*.6)
rect(ix*2,iy*2,2,2)
なかなか良い感じになった。
あとはこれを #つぶやきProcessing するべく文字数削減していく。そのままコードを tweet 欄に貼り付けると 62 文字オーバーとのこと。
変数を ix, iy からx,y に変更しする。また、これら x,y については 2 重ループではなく 1 重ループと除算と剰余を用いるように変更する:
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",10+i%8,300+i/8);text("MEMORIES",10+i%8,375+i/8)
blendMode(MULTIPLY)
for t in L(50000):
y=t/250;x=t%250
d=dist(x,y,150,-100)
v=noise(x*.02,y*.1)
fill((v+.7)*pow(d,5)*2e-10,(v+.5)*d*d*.002,(v+.2)*d*.6)
rect(x*2,y*2,2,2)
これで 34 文字オーバー。まだまだ文字数を減らさなければならない。dist 関数の処理は位置 (x,y) と位置 (150,-100) との距離を求めているので、これを y+100 で近似する。
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",10+i%8,300+i/8);text("MEMORIES",10+i%8,375+i/8)
blendMode(MULTIPLY)
for t in L(50000):
y=t/250;x=t%250
d=y+100
v=noise(x*.02,y*.1)
fill((v+.7)*pow(d,5)*2e-10,(v+.5)*d*d*.002,(v+.2)*d*.6)
rect(x*2,y*2,2,2)
あまり雰囲気に変化はないので、これを採用する。現在、21 文字オーバー。さらに、最後の fill 関数の赤成分指定のところの式 (v+.7)*pow(d,5)*2e-10 を、ものすごくざっくりとした近似式(?)(v+1)*d に置き換えてみる。
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",10+i%8,300+i/8);text("MEMORIES",10+i%8,375+i/8)
blendMode(MULTIPLY)
for t in L(50000):
y=t/250;x=t%250
d=y+100
v=noise(x*.02,y*.1)
fill((v+1)*d,(v+.5)*d*d*.002,(v+.2)*d*.6)
rect(x*2,y*2,2,2)
雰囲気もさほど変わらないので、これを採用する。また緑成分の計算式 (v+.5)*d*d*.002 もバッサリと v*d で近似する( (v+.5)*d だと黄色が強かったので単純に v*d とした)。
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",10+i%8,300+i/8);text("MEMORIES",10+i%8,375+i/8)
blendMode(MULTIPLY)
for t in L(50000):
y=t/250;x=t%250
d=y+100
v=noise(x*.02,y*.1)
fill((v+1)*d,v*d,(v+.2)*d*.6)
rect(x*2,y*2,2,2)
少々、赤みが強くなっている気がしないではないが、許容範囲なのでこの式を採用する。最後は青成分であるが、あまり青色は本作には寄与していない感じもするので、思い切って 0 にしてしまう。
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",10+i%8,300+i/8);text("MEMORIES",10+i%8,375+i/8)
blendMode(MULTIPLY)
for t in L(50000):
y=t/250;x=t%250
d=y+100
v=noise(x*.02,y*.1)
fill((v+1)*d,v*d,0)
rect(x*2,y*2,2,2)
ここまで来ると、もう赤と黄色のノイズで良いのでは?という気もしないわけでもないが、まあ、こんな感じで良いのではないかと思う。
ちなみにこの状態で tweet 欄に貼り付けると、無事 280 文字に収まっている。しかし、#つぶやきProcessing タグと #AltEdu2022 タグを入れると、あと 18 文字削減する必要がある。
最後の for ループのインデント分を削除すべく、セミコロンを用いたマルチステートメントにしつつ、赤成分の (v+1)*d も単なる d に置き換えて様子をみてみる(v は noise 関数の値であるので 0~1 だし、赤成分は 0〜255 が有効な値となるので、多分 d に置き換えてもあまり変化が無いと推測):
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",10+i%8,300+i/8);text("MEMORIES",10+i%8,375+i/8)
blendMode(MULTIPLY)
for t in L(50000):y=t/250;x=t%250;d=y+100;v=noise(x*.02,y*.1);fill(d,v*d,0);rect(x*2,y*2,2,2)
画像のトーンはあまり変化していない。そして、文字数もタグを入れた状態で 6 文字超過という状況となった。
あとは text 関数内の 10+i%8 を 9+i%8 として、それぞれ 1 文字づつ文字数を減らし、d の値も y+100 から y+99 として 1 文字削減する。また、50000 という値については 9 の 5 乗が 59049 であるので 9**5 とする:
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",9+i%8,300+i/8);text("MEMORIES",9+i%8,375+i/8)
blendMode(MULTIPLY)
for t in L(9**5):y=t/250;x=t%250;d=y+99;v=noise(x*.02,y*.1);fill(d,v*d,0);rect(x*2,y*2,2,2)
実行結果についても殆ど変化がない。これであと 2 文字超過という状況にまでこぎ着けた。さてどうしたものか…とコードを眺めてみると、もはや変数 v は 1 箇所でしか使用していないことに気がつく。
というわけで、変数 v を使っているところに直接 noise 関数を記述する:
L=range
size(500,500)
noStroke()
clear()
fill(-1)
textSize(80)
for i in L(72):text("SUNSET",9+i%8,300+i/8);text("MEMORIES",9+i%8,375+i/8)
blendMode(MULTIPLY)
for t in L(9**5):y=t/250;x=t%250;d=y+99;fill(d,noise(x*.02,y*.1)*d,0);rect(x*2,y*2,2,2)
結果、#つぶやきProcessing タグおよび #AltEdu2022 タグ込みで 280 文字に収めることができ、無事 tweet できた。