現代アートの巨匠と呼ばれるゲルハルト・リヒター。彼の作品の真似をしていて出来上がった(元とは全然違う)ものを、制作の過程とコードを交えて紹介します。
👉 Read this article in English.
ゲルハルト・リヒター 「4900の色彩」
絵の具の見本帳をヒントに、25色のカラーチップをランダムに配置して作られた「カラーチャート」シリーズ。ゲルハルト・リヒターのこの作品に興味を持ったのは、AI と創造性について書かれた本「レンブラントの身震い」の中で、ランダム性は創造性と同じなのか?という観点で紹介されていたのを読んだからでした。
25色の色を用意して、その中からランダムに選んで順番にグリッド上に置いていく。そんなことで面白いものが作れるのか?コードを書いて実行してみます。
ハルゲルト・リタヒー 32x32#processing #creativecoding pic.twitter.com/XqlX8EjKQI
— deconbatch (@deconbatch) December 4, 2022
結果、あまり楽しくない…。
これで面白いものが出来るかどうかは、「いかに 25色の良い色を選ぶか」で決まってくるのではないでしょうか?
良い色の組み合わせ、良いカラーパレットをどうコードで生成するか?突き詰め甲斐のある問いですが、これはかなり難しい問題です。
/**
* Color chips with 25 random colors.
* ref. Gerhard Richter '4900 Farben'
*
* @author @deconbatch
* @version 0.1
* @license CC0
* p5.js 1.5.0
* created 2022.12.08
*/
const margin = 20;
const chipNum = 32;
const chipSiz = 20;
const chipGap = 0;
const colorNum = 25;
const canvasW = chipSiz * chipNum + margin * 2;
function setup() {
createCanvas(canvasW, canvasW);
colorMode(HSB, 360, 100, 100, 100);
// set random color palette
const palette = new Array(colorNum);
for (let i = 0; i < colorNum; i++) {
palette[i] = color(random(360), random(40, 60), random(60, 80));
}
// draw
background(0, 0, 90, 100);
translate(margin, margin);
stroke(0, 0, 90, 100);
strokeWeight(chipGap);
for (let chipY = 0; chipY < chipNum; chipY++) {
for (let chipX = 0; chipX < chipNum; chipX++) {
fill(palette[floor(random(colorNum))]);
rect(
chipX * chipSiz,
chipY * chipSiz,
chipSiz,
chipSiz
);
}
}
}
規則性を入れてみる
よい色の生成は今の私には荷が重いので、違う方向性を考えます。
ゲルハルト・リヒターの「4900の色彩」を見た時、私は、似た色や明度の低い部分を自然と目でなぞり、色の並びに規則性や構造を見出そうとしていました。だったら、最初から構造を埋め込んでみるのはどうでしょう?
例えば、あるパターンで大きな構造を作り、そこに同じパターンの小さな構造を入れていく。
パターンは、ゲルハルト・リヒターの作品と同じように 25色からランダムに選んだ色のパターンとします。
ここに同じパターンを小さくしたものを乗せていきますが、普通に乗せたら大きな構造の意味が無くなってしまいます。
なので、小さなパターンは色を半透明にして乗せていきます。
p5.js/Processing コード例
構造は再帰的だけれど、再帰のコードにするまでもないので、ベタで解りやすい 2重ループにします。大きな構造に BLUR をかけると、ちょっといい感じになりました。
p5.js ではキャンバスサイズに変数が使えること、二次元配列が面倒なこと、p5.Color を使ったときに fill() で透明度を指定できないらしいことから、p5.js と Processing で少し処理が異なるコードになっています。
/**
* Color patterns with 25 random colors.
* ref. Gerhard Richter '4900 Farben'
*
* @author @deconbatch
* @version 0.1
* @license GPL3
* p5.js 1.5.0
* created 2022.12.12
*/
const margin = 20;
const chipNum = 5;
const chipSiz = 24;
const chipGap = 2;
const colorNum = 25;
const palette = new Array(colorNum);
const pattern = new Array(chipNum * chipNum);
const canvasW = chipSiz * chipNum * chipNum + margin * 2;
function setup() {
createCanvas(canvasW, canvasW);
colorMode(HSB, 360, 100, 100, 100);
smooth();
noLoop();
// palette
for (let i = 0; i < colorNum; i++) {
let h = random(360);
let s = random(40, 60);
let b = random(60, 80);
palette[i] = color(h, s, b, 100);
}
// make pattern
for (let y = 0; y < chipNum; y++) {
for (let x = 0; x < chipNum; x++) {
pattern[x + y * chipNum] = palette[floor(random(colorNum))];
}
}
// draw
background(0, 0, 90, 100);
translate(margin, margin);
// large pattern
noStroke();
for (let y = 0; y < chipNum; y++) {
for (let x = 0; x < chipNum; x++) {
fill(pattern[x + y * chipNum]); // no transparent
rect(
x * chipSiz * chipNum,
y * chipSiz * chipNum,
chipSiz * chipNum,
chipSiz * chipNum
);
}
}
filter(BLUR, 6);
// small patterns
strokeWeight(chipGap);
stroke(0, 0, 90, 100);
for (let largeY = 0; largeY < chipNum; largeY++) {
for (let largeX = 0; largeX < chipNum; largeX++) {
for (let y = 0; y < chipNum; y++) {
for (let x = 0; x < chipNum; x++) {
pattern[x + y * chipNum].setAlpha(50); // transparent
fill(pattern[x + y * chipNum]);
rect(
(largeX * chipNum + x) * chipSiz,
(largeY * chipNum + y) * chipSiz,
chipSiz,
chipSiz
);
}
}
}
}
}
/**
* Color patterns with 25 random colors.
* ref. Gerhard Richter '4900 Farben'
*
* @author @deconbatch
* @version 0.1
* @license GPL3
* Processing 3.5.3
* created 2022.12.12
*/
public void setup() {
size(760, 760);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
smooth();
noLoop();
int margin = 20;
int chipNum = 6;
int chipGap = 2;
int chipSiz = floor((width - margin * 2) / chipNum / chipNum);
// palette
int colorNum = 25;
color palette[] = new color[colorNum];
for (int i = 0; i < colorNum; i++) {
float h = random(360.0);
float s = random(40.0, 60.0);
float b = random(60.0, 80.0);
palette[i] = color(h, s, b);
}
// make pattern
color pattern[][] = new color[chipNum][chipNum];
for (int y = 0; y < chipNum; y++) {
for (int x = 0; x < chipNum; x++) {
pattern[x][y] = palette[floor(random(colorNum))];
}
}
// draw
translate(margin, margin);
background(0.0, 0.0, 90.0, 100.0);
// large pattern
noStroke();
for (int y = 0; y < chipNum; y++) {
for (int x = 0; x < chipNum; x++) {
fill(pattern[x][y], 100.0); // no transparent
rect(
x * chipSiz * chipNum,
y * chipSiz * chipNum,
chipSiz * chipNum,
chipSiz * chipNum
);
}
}
filter(BLUR, 6);
// small patterns
strokeWeight(chipGap);
stroke(0.0, 0.0, 90.0, 100.0);
for (int largeY = 0; largeY < chipNum; largeY++) {
for (int largeX = 0; largeX < chipNum; largeX++) {
for (int y = 0; y < chipNum; y++) {
for (int x = 0; x < chipNum; x++) {
fill(pattern[x][y], 50.0); // transparent
rect(
(largeX * chipNum + x) * chipSiz,
(largeY * chipNum + y) * chipSiz,
chipSiz,
chipSiz
);
}
}
}
}
}
まとめ:模写も楽しいですよ
今回のような既存の美術作品をクリエイティブ・コーディングで模写することを時々やっています。やってみると、「こうしたらどうなるだろう?」という発想がわくことも多くて楽しいです。
先達の芸術から何かを学ぶとか硬い話抜きに、単純に遊びとして面白く、おすすめですよ。