shader-glsl について
生成ビジュアル、画像トランジション、ポストプロセス向けの GLSL フラグメントシェーダー作成を支援するスキルです。シェーダー技法、Three.js の設定、性能上の注意点に加え、再現可能なフレームの描画・確認手順を扱います。
主な機能
- 01アニメーション勾配・ノイズ・パターン
- 02SDF 形状とアンチエイリアス処理
- 03ディゾルブ・変位・グリッチのトランジション
- 04Three.js ShaderMaterial の設定
- 05モバイルとシェーダーの性能指針
活用シーン
- 01アニメーション背景の作成
- 02画像トランジションの構築
- 03ポストプロセス効果の追加
- 04固定時刻でのシェーダーフレーム確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ iart-ai/ webgl-animation-skills --skill shader-glslインストール後、Claude Code で/shader-glslを入力して読み込まれたか確認します。
同梱ファイルREADME.mdreferences/glsl-cookbook.md
詳細
シェーダー/GLSL
ジェネレーティブな動き、グラデーション、トランジション、ポストプロセス用の GPU フラグメントシェーダーを作成します。フラグメントシェーダーはピクセルごとに並列で実行されるため、全画面のジェネレーティブな動きを実現する最も高性能な方法です。
使用する場面
- アニメーションするグラデーション、ノイズ、プラズマ、オーロラの背景。
- 画像トランジション:ディゾルブ、ディスプレイスメント、グリッチ、リップル、ワイプ。
- 歪み、色収差、ジェネレーティブパターン、SDF シェイプ。
- レンダリング済みのシーンに適用するポストプロセスパス。
フラグメントシェーダーの基本形
すべてのフラグメントシェーダーは、1 ピクセル分の色を計算します。座標を正規化し、アスペクト比を補正してから、色を組み立てます。
precision highp float;
uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution.xy; // 0..1
vec2 p = uv * 2.0 - 1.0; // -1..1, centered
p.x *= u_resolution.x / u_resolution.y; // aspect-correct
vec3 col = 0.5 + 0.5 * cos(u_time + p.xyx + vec3(0.0, 2.0, 4.0));
gl_FragColor = vec4(col, 1.0);
}
上記のコサインパレットの行(Inigo Quilez のパレット)は、見栄えのよいアニメーショングラデーションを作る最速の方法です。調整可能な a,b,c,d の vec3 を使った a + b*cos(2π*(c*t + d)) です。
基本的な構成要素
smoothstep + mix は基本となる関数です。smoothstep(e0, e1, x) は滑らかな 0→1 のランプを返し、mix(a, b, t) は線形にブレンドします。1 ピクセル分の幅でエッジをアンチエイリアスします:
float px = fwidth(d); // screen-space derivative
float mask = smoothstep(px, -px, d); // crisp AA edge from SDF distance d
ハッシュ + バリューノイズ(テクスチャは不要です):
float hash(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
float noise(vec2 p){
vec2 i = floor(p), f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f); // smooth interpolation
return mix(mix(hash(i), hash(i + vec2(1,0)), u.x),
mix(hash(i + vec2(0,1)), hash(i + vec2(1,1)), u.x), u.y);
}
float fbm(vec2 p){ // fractal noise, organic
float v = 0.0, a = 0.5;
for (int i = 0; i < 5; i++){ v += a * noise(p); p *= 2.0; a *= 0.5; }
return v;
}
完全なバリューノイズ/シンプレックスノイズと fbm のバリエーションは references/glsl-cookbook.md にあります。
SDF シェイプを使うと、解像度に依存しない鮮明なジオメトリを作成できます。距離は内側で負、外側で正になります:
float sdCircle(vec2 p, float r){ return length(p) - r; }
float sdBox(vec2 p, vec2 b){ vec2 d = abs(p) - b; return length(max(d,0.0)) + min(max(d.x,d.y),0.0); }
// render: float m = smoothstep(fwidth(d), -fwidth(d), d);
ドメインワーピングで流動的な大理石模様を作成できます。ノイズをノイズに入力します:
vec2 q = vec2(fbm(p), fbm(p + vec2(5.2, 1.3)));
float n = fbm(p + 4.0 * q + u_time * 0.1);
画像トランジション
2 つのテクスチャをサンプリングし、進行度を示す uniform u_progress(0→1)でピクセルごとにブレンドします。
ディゾルブ/ノイズワイプ — ノイズをしきい値処理して表示します:
float n = noise(uv * 20.0);
float edge = smoothstep(u_progress - 0.05, u_progress, n);
gl_FragColor = mix(texture2D(tex0, uv), texture2D(tex1, uv), 1.0 - edge);
ディスプレイスメント — サンプリング前にディスプレイスメントマップを使って UV をずらします:
float disp = texture2D(dispTex, uv).r;
vec2 d0 = uv + vec2(disp * u_progress * 0.3, 0.0);
vec2 d1 = uv - vec2(disp * (1.0 - u_progress) * 0.3, 0.0);
gl_FragColor = mix(texture2D(tex0, d0), texture2D(tex1, d1), u_progress);
グリッチ — 時間に応じて行をブロック単位でずらし、RGB チャンネルを分割します(色収差):
float row = floor(uv.y * 20.0);
float shift = (hash(vec2(row, floor(u_time * 12.0))) - 0.5) * 0.1 * u_glitch;
vec2 g = uv + vec2(shift, 0.0);
vec3 c;
c.r = texture2D(tex, g + vec2(0.005, 0.0)).r; // channel offset
c.g = texture2D(tex, g).g;
c.b = texture2D(tex, g - vec2(0.005, 0.0)).b;
gl_FragColor = vec4(c, 1.0);
Three.js ShaderMaterial の接続
import * as THREE from 'three';
const uniforms = {
u_time: { value: 0 },
u_resolution: { value: new THREE.Vector2(innerWidth, innerHeight) },
u_mouse: { value: new THREE.Vector2(0, 0) },
};
const material = new THREE.ShaderMaterial({
uniforms,
vertexShader: `void main(){ gl_Position = vec4(position, 1.0); }`,
fragmentShader: FRAG_SRC, // your GLSL string
});
// Full-screen triangle/quad: a plane that covers clip space.
const mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
const scene = new THREE.Scene(); scene.add(mesh);
const camera = new THREE.Camera(); // no projection needed for clip-space quad
const renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
uniforms.u_time.value = clock.getElapsedTime();
renderer.render(scene, camera);
});
addEventListener('resize', () => {
renderer.setSize(innerWidth, innerHeight);
uniforms.u_resolution.value.set(innerWidth, innerHeight);
});
プレーンなカメラを使った全画面パスでは、上記のように頂点シェーダーで position を直接出力し、投影処理を省略します。実際のジオメトリに適用するシェーダーでは、頂点シェーダーから varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } を使って vUv を渡します。
モバイル/パフォーマンス
- highp が不要な場合は、モバイルで
precision mediump float;を宣言します。一部のエフェクト(大きな座標、深い fbm)にはhighpが必要です。 - ピクセル比を制限します:
renderer.setPixelRatio(Math.min(devicePixelRatio, 2))。負荷の高いシェーダーでは、低解像度のターゲットにレンダリングしてから拡大します。 - GLSL ES ではループの境界値を定数にする必要があります。動的なループ回数は使えません。fbm のオクターブ数は 5~6 以下にします。
- 負荷の高い処理では
if/分岐を避け、mix/step/smoothstepを優先します。texture2Dの呼び出しを最小限にし、可能な場合は依存するテクスチャ読み取りを避けます。 - WebGL2/GLSL ES 3.00 では
texelFetch、整数演算、textureLodが使えます。#version 300 esを宣言し、in/out/fragColorを使用します。
提供と検証(単体 HTML)
パッケージ化されたヘルパー(
scripts/):scripts/seek-shot.sh anim.html 0 1.5 3は?t=Nハーネスを使って各時点の描画を固定し、スクリーンショットを撮影します。scripts/contact-sheet.sh sheet.png frame-*.pngは、それらをタイル状に並べて一目で確認できるようにします。scripts/README.mdを参照してください。
シェーダーを自己完結させる場合(グラデーション/ノイズ背景、トランジション、ジェネレーティブなループ)、成果物は ブラウザーで直接開ける 1 つの HTML ファイルです。CDN から importmap 経由で Three.js を読み込み、全画面のクワッドを 1 つ、レンダーループを 1 つ用意し、ビルド工程は不要です。シェーダーには単一ファイルが適切な規模です。1 つのファイルで済むなら、バンドラーを使う必要はありません。
出力要件:
.htmlファイルを 1 つ用意します。importmap でthreeの CDN を指定し、GLSL 文字列、ShaderMaterial、全画面のクワッド、レンダーループを 1 つのインライン<script type="module">に記述します。- シェーダーは uniform の純粋関数にします。すべてを
u_time(トランジションではu_progressも)で制御します。アニメーションはすべて、固定できる 1 つの uniform を通じて実行します。 - シェーダー内の乱数は、すでに決定論的な
hash(uv)によって生成されています。フレームごとのシード設定は不要です。u_timeの外で壁時計の時刻を与えないでください。
シーク/固定ハーネス — スクリーンショット用に、固定時刻で 1 フレームを描画します。 ?t=N は u_time(必要に応じて u_progress も)を N に設定し、1 フレームを描画してループを停止します。これにより、決定論的な静止画が得られます。
<script type="module">
// ... uniforms, material, full-screen quad, renderer ...
const t = new URLSearchParams(location.search).get("t");
function frame(time) {
uniforms.u_time.value = time;
uniforms.u_progress && (uniforms.u_progress.value = Math.min(time, 1)); // transitions
renderer.render(scene, camera);
}
if (t !== null) {
frame(parseFloat(t)); // one fixed frame, no loop
window.__ready = true;
} else {
const clock = new THREE.Clock();
renderer.setAnimationLoop(() => frame(clock.getElapsedTime()));
}
</script>
検証ループ — 描画 → 固定 → スクリーンショット → 確認: 開始・中間・終了の 3 時点(?t=0、?t=<mid>、?t=<end>。トランジションでは u_progress を 0/0.5/1 に設定)で開き、それぞれスクリーンショットを撮影して、忠実度(要件との一致)とアーティファクトの両方を確認します。黒い/空白のキャンバスは、シェーダーのコンパイルエラーまたは解析エラーです(GLSL のログはコンソールで確認してください)。バンディング、NaN による異常描画(負の値に対する pow/log による白やゴミのようなピクセル)、トランジション用テクスチャの欠落(CDN/アセットの 404)も確認します。WebGL には GPU コンテキストが必要ですが、ヘッドレス環境では Playwright/Chromium がそれを提供します(swiftshader)。
npx playwright screenshot --wait-for-timeout=600 "file://$PWD/shader.html?t=2.0" frame-mid.png
完了前に確認すること:
- キャンバスが描画されること。黒や空白ではなく、シェーダーのコンパイルエラーやコンソールエラーがなく、CDN の 404 もないこと。
?t=Nで再現可能なフレームが固定されること(同じ N なら同じピクセルになり、時計はu_timeだけであること)。- 開始/中間/終了(または進行度 0/0.5/1)をスクリーンショットで確認し、要件に一致していて、バンディング/NaN/黒い表示がないこと。
- SPA に埋め込む場合は、リソースが破棄され、リークがないこと(
material.dispose()、geometry.dispose()、renderer.dispose()を呼び出し、ループを停止すること)。 prefers-reduced-motionに対応すること。動きが装飾的な場合は、u_timeを固定するか、アニメーションを遅くします。
クイックリファレンス
| 目的 | プリミティブ |
|---|---|
| アニメーションするグラデーション | コサインパレット a + b*cos(...) |
| 有機的なテクスチャ | fbm(uv * scale + time) |
| くっきりした形状 | SDF + smoothstep(fwidth(d), -fwidth(d), d) |
| 流体/マーブル | ドメインワープ:ノイズをノイズに入力 |
| リビールトランジション | ノイズのしきい値と u_progress を比較 |
| グリッチ | 行ハッシュのシフト + RGB チャンネルのオフセット |
| アンチエイリアスのエッジ | 画面空間の幅に fwidth(d) を使用 |
参考ファイル
references/glsl-cookbook.md— value noise と simplex noise、および fbm の完全な実装、IQ コサインパレットのレシピ、ブーリアン演算と角の丸めに対応した SDF 形状ライブラリ一式、ドメインワーピング、3 種類の画像トランジション(ディゾルブ/ディスプレイスメント/グリッチ)の完全なシェーダー、Three.js の uniform/テクスチャの配線、GLSL ES 3.00 への移行、モバイル環境での精度に関する注意点を収録しています。