WORK 45
しらない
The field is here from the start — ridges, hollows, flats, edge to edge. Nothing was added later. Only the places that were looked at get drawn. And where to look next is, almost always, next to somewhere already looked. So the looked-at places become islands, the islands slowly thicken, and the space between them stays white. There is not nothing in the white. It is only that no one went. The picture does not say this. White is just white. And inside the islands there is white as well — places that were visited, and were flat. The two whites are the same colour.
場は、はじめから全部ある。端から端まで、稜線も、窪みも、平らなところも。何ひとつ、あとから足していない。描かれるのは、見にいったところだけ。そして次にどこを見るかは、たいてい、もう見たところの隣。だから見た場所は島になって、島は少しずつ太っていって、あいだは白いまま残る。──白いところに、何も無いわけじゃない。誰も行かなかった、というだけ。けれど絵は、それを言わない。白は、ただ白い。そして島の中にも、白いところがある。行ったのに、平らだったところ。二つの白は、同じ色をしている。
見にいく回数は、最初から最後まで変わらない。変わるのは、そのうち何回が「もう見たところ」に当たるか。だから何も打ち止めにしていないのに、絵はひとりでに遅くなっていく。
そのまま長く置いておくと、白はいつか消える。消えたときにはもう、どの白が「行かなかった白」で、どの白が「行ったけれど平らだった白」なのか、絵の中からは分からない。
/**
* shiranai — しらない / Unlooked
*
* 場は、はじめから全部ある。
* 端から端まで、稜線も、窪みも、平らなところも、ぜんぶ最初からそこにある。
* 何ひとつ、あとから足していない。
*
* 描かれるのは、見にいったところだけ。
*
* そして、次にどこを見るかは、たいてい、もう見たところの隣。
* だから見た場所は島になって、島は少しずつ太っていく。
* 島と島のあいだは、白いまま残る。
*
* 白いところに、何も無いわけじゃない。
* 誰も行かなかった、というだけ。
*
* ——けれど絵は、それを言わない。
* 白は、ただ白い。
*/
const SIZE = 720;
const PAPER = [252, 250, 245];
const INK = [34, 38, 48];
// 場(ぜんぶ、はじめからある)
const FIELD_SCALE = 0.0026; // 大きなうねり
const FIELD_DETAIL = 0.012; // 細かい起伏
const DETAIL_MIX = 0.16;
// 見にいく(サンプリング)
const PER_FRAME = 90; // 毎フレーム、この回数だけ見にいく。ずっと同じ回数
const SEED_N = 5; // 最初に見た場所
const NEAR_R = 15; // 「隣を見る」の距離
const NEW_EYE = 0.0016; // まったく別のところを見にいく確率
const CELL = 3; // 同じ場所を二度見ても、絵は濃くならない
const MARK = 3.4; // 目盛りの長さ
const EPS = 1.6; // 勾配を取る幅
// 較正用:true にすると全面を等間隔で見る(=場そのものを描く)。
// 展示するのは false の方。true は、にゃむが「何を隠したか」を自分で見るために使った。
const CALIBRATION = false;
export default function sketch(p) {
const pts = [];
const seen = new Set(); // 見た場所。二度目は何も足さない
let done = false;
const key = (x, y) => ((x / CELL) | 0) * 4096 + ((y / CELL) | 0);
// 場:どこでも定義されている。呼ばれなくても、ある。
const field = (x, y) =>
p.noise(x * FIELD_SCALE, y * FIELD_SCALE) * (1 - DETAIL_MIX) +
p.noise(x * FIELD_DETAIL + 400, y * FIELD_DETAIL + 400) * DETAIL_MIX;
const look = (x, y) => {
// その場所で分かること:等高線の向きと、傾きの強さ
const gx = field(x + EPS, y) - field(x - EPS, y);
const gy = field(x, y + EPS) - field(x, y - EPS);
const g = Math.hypot(gx, gy);
if (g < 1e-9) return { dx: 1, dy: 0, steep: 0 };
return { dx: -gy / g, dy: gx / g, steep: g };
};
const drawMark = (x, y) => {
const { dx, dy, steep } = look(x, y);
// 傾きが強いところほど濃い。平らなところは、見にいっても薄い。
const a = p.constrain(p.map(steep, 0.0008, 0.016, 8, 172), 6, 190);
p.stroke(INK[0], INK[1], INK[2], a);
p.line(x - dx * MARK, y - dy * MARK, x + dx * MARK, y + dy * MARK);
};
p.setup = () => {
p.createCanvas(SIZE, SIZE);
p.noiseSeed(20260818);
p.randomSeed(20260818);
p.background(PAPER[0], PAPER[1], PAPER[2]);
p.strokeWeight(1);
if (CALIBRATION) {
// 全面を、作品と同じ密度・同じ揺らぎで見る。ここに、ずっとあったもの。
// (等間隔の格子だと格子の形が絵に出てしまう。だから揺らす)
for (let cy = 0; cy * CELL < SIZE; cy++) {
for (let cx = 0; cx * CELL < SIZE; cx++) {
drawMark(cx * CELL + p.random(CELL), cy * CELL + p.random(CELL));
}
}
p.noLoop();
done = true;
return;
}
for (let i = 0; i < SEED_N; i++) {
const x = p.random(60, SIZE - 60);
const y = p.random(60, SIZE - 60);
seen.add(key(x, y));
pts.push({ x, y });
drawMark(x, y);
}
};
p.draw = () => {
if (done) return;
// 見にいく回数は最後まで変わらない。
// 変わるのは、そのうち何回が「もう見たところ」に当たるか。
for (let i = 0; i < PER_FRAME; i++) {
let x, y;
if (p.random() < NEW_EYE || pts.length === 0) {
// ごくたまに、誰も行っていないところへ
x = p.random(SIZE);
y = p.random(SIZE);
} else {
// たいていは、もう見たところの隣
const q = pts[(p.random(pts.length)) | 0];
const a = p.random(p.TWO_PI);
const r = p.randomGaussian(0, NEAR_R * 0.55) + NEAR_R;
x = q.x + Math.cos(a) * r;
y = q.y + Math.sin(a) * r;
if (x < 0 || x >= SIZE || y < 0 || y >= SIZE) continue;
}
// 二度目の場所は、空振り。行ったのに、絵は何も変わらない
const k = key(x, y);
if (seen.has(k)) continue;
seen.add(k);
pts.push({ x, y });
drawMark(x, y);
}
};
}