風呂瀬 信五

Shadertoyで触れるGLSL:SDFとレイマーチングで読み解く光の彫刻

Shadertoyを開くと、数十行のGLSLから海や雲、金属の彫刻のような光景が立ち上がります。美しいと感じる一方で、コードは数式の羅列にしか見えず、どこから読めばよいのか分からないまま閉じてしまう人は少なくありません。この記事では、シェーダーアートを「読む・解く・感じる」の三段階で味わう視点と、そのまま創作の入口になる小さな実験を紹介します。

画面の全ピクセルに問いかける:フラグメントシェーダーという独特の思考法

Shadertoyの作品は、フラグメントシェーダーだけで描かれています。p5.jsの circle() のように「何を、どこに描くか」を命令する書き方ではありません。画面の全ピクセルが同じ関数を実行し、「自分の座標では、どんな色になるか」を一つずつ答えます。

入口になるのは mainImage という関数です。引数の fragCoord が今問われているピクセルの座標、iResolution が画面サイズ、iTime が経過時間で、いずれもShadertoyが用意する入力です。座標を iResolution で割って正規化し、時間を混ぜて色を返す。作品の大半はこの繰り返しです。

この構造のおかげで、コードには「ある点から世界がどう見えるか」だけが書かれます。ほかのピクセルの様子は知りようがありません。この制約が、個々のピクセルの小さな判断が積み重なって全体の像になるという、シェーダー特有の美学を生んでいます。鑑賞するときは、絵を眺めながら「この一点は何を答えているのか」と想像してみてください。

数式が立ち上げる立体と光:レイマーチングとSDF(符号付き距離場)の美学

Shadertoyで立体的な風景に出会ったら、その多くはレイマーチングで描かれています。カメラから各ピクセルへ光線を飛ばし、「最も近い物体までの距離」の分だけ少しずつ進み、距離がほぼゼロになった場所を表面とみなす手法です。

その距離を返す関数が**SDF(符号付き距離場)**です。たとえば球は length(p) - r の一行で表せます。ポリゴンのメッシュも読み込まず、形そのものが数式として存在します。形の合成も、距離の min や max を取るだけで済みます。イニゴ・キレス(Inigo Quilez)が自身のサイト(iquilezles.org)で多くのSDFの式を公開しており、Shadertoyの作品でもよく参照されています。

最小の構成は次のとおりです。

float map(vec3 p) {
    return length(p) - 1.0; // 半径1の球
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    vec3 ro = vec3(0.0, 0.0, -3.0);        // カメラ位置
    vec3 rd = normalize(vec3(uv, 1.5));    // 光線の向き
    float t = 0.0;
    for (int i = 0; i < 64; i++) {
        float d = map(ro + rd * t);
        if (d < 0.001 || t > 10.0) break;
        t += d;
    }
    vec3 col = vec3(t < 10.0 ? 1.0 - t * 0.2 : 0.0);
    fragColor = vec4(col, 1.0);
}

美しい作品も、骨格はこの繰り返しです。ループの回数が多いほど細部まで届き、少なければ形が溶けます。この数十行の違いが、光の彫刻の密度を決めています。

名作コードを「読む」ための視点:ノイズの重ね合わせと陰影の演出法

長いコードの前で立ちすくんだときは、上から順に理解しようとせず、役割ごとに切り分けて読むのが近道です。多くの作品は、形を返す map、法線の計算、ライティング、そして色の仕上げに分かれます。まず map だけを見て「何と何を組み合わせた形か」を把握し、次に色の部分へ進んでください。

自然物のような質感を作る場面では、ノイズの重ね合わせがよく使われます。周波数を上げながら振幅を下げて足していく手法はfBm(フラクタルブラウン運動)と呼ばれ、雲や地形、水面の表現で広く使われています。コードにループで p *= 2.0 や a *= 0.5 のような更新が見えたら、その可能性が高いでしょう。

陰影にも作者の意図が表れます。法線と光の向きの内積で明るさを決め、距離に応じて霧のように色を溶かし、影のために光源方向へもう一度光線を飛ばす。こうした工夫を見つけるたびに、コードが画面の情景と結びついていきます。わからない関数は、返す値を fragColor に直接出して可視化してみてください。この「関数を色で見る」習慣が、読解の最大の助けになります。

読む鑑賞から触れる制作へ:まずは定数を揺らし、空間を歪ませてみる

Shadertoyの魅力は、その場でコードを書き換えて結果を確かめられる点にあります。気に入った作品をフォークし、数値を一つだけ変えて再生してみましょう。形が壊れてもかまいません。壊れ方を観察すること自体が、数式の意味を理解する手がかりになります。

最初の実験として、空間そのものを歪ませる方法があります。map の冒頭で座標を揺らします。

float map(vec3 p) {
    p.xy += 0.2 * sin(p.yx * 3.0 + iTime);
    return (length(p) - 1.0) * 0.7;
}

球は水に映ったように揺れ始めます。座標を変形すると距離の値が正確でなくなり、光線が表面を突き抜けることがあります。そのため距離を少し縮めて返す補正を入れました。こうした不具合の扱いも、実際に触ると身につきます。

ほかのフレームワークへの橋渡しも可能です。p5.jsでは createShader() でGLSLを使え、Three.jsには ShaderMaterial、TouchDesignerにはGLSL TOPがあります。Shadertoyで磨いた感覚は、これらの環境にも持ち出せます。ただし iTime などの入力は環境ごとに自前で渡す必要があるため、移植のときは注意してください。

純粋計算のなかに見出す有機的な詩情:シェーダーアートがひらく新たな知覚

シェーダーアートは、数式だけで世界を作ります。素材もカメラも彫刻刀もなく、あるのは座標と時間と、その関数だけです。それでも画面には、生き物の呼吸のような揺らぎや、夕暮れの空気の重さが現れることがあります。計算が有機的に見える不思議は、作品を見るたびに新しく感じられます。

背景には、デモシーンの文化があります。限られたバイト数やGPU性能のなかで、いかに豊かな映像を出すかを競ってきた歴史です。制約が表現を研ぎ澄ませるという感覚は、Shadertoyの短いコードにも受け継がれていると言えるでしょう。

次にShadertoyを開いたら、気になった作品の mainImage を最後まで読まなくてもかまいません。一つの定数だけを動かし、画面がどう変わるかを見てみてください。その小さな変化のなかに、作者が選んだ数値の意味が見えてきます。読むことと作ることが近づくほど、光の彫刻は少しずつ自分の手元にも現れるはずです。

関連記事