WithCodeMedia-1-pc
previous arrowprevious arrow
next arrownext arrow

WithCodeMedia-1-sp
previous arrowprevious arrow
next arrownext arrow

Three.jsとGLSLシェーダーで背景アニメーションを実装する方法【完全ガイド】

この記事でわかること

  • Three.jsのGLSLシェーダーがCSSアニメーションより高品質な理由(CPU vs GPU)
  • 頂点シェーダーとフラグメントシェーダーの役割と、GLSL構文の必須8ルール
  • 波・グラデーション・ノイズ背景の3エフェクトを動くコード付きで完全解説
  • ブルーム・フィルムグレインなどポストプロセッシングの組み込み方
  • スマートフォンでも60fpsを維持するためのパフォーマンス最適化7ポイント
生徒

Three.jsで動きのある背景アニメーションを作りたいのですが、ShaderMaterialってどう使えばいいんでしょうか。GLSLは難しそうで…

ペン博士

よーく聞くんだぞ。GLSLシェーダーは最初は難しく見えるが、「頂点シェーダー」と「フラグメントシェーダー」の2つの役割さえ掴めば、あとは数式を少し知るだけで驚くほど高度な背景エフェクトが作れるんじゃ。今日は波・グラデーション・ノイズを使った3種類の実践エフェクトをコード付きで徹底解説するぞい!

結論から言うと、Three.jsのGLSLシェーダーを使えばCSSでは不可能な滑らかな波・流体・ノイズ背景をGPUの並列処理で実現できます。シェーダー背景を導入したポートフォリオサイトや企業LPでは、訪問者の滞在時間が平均30〜40%伸びたという報告もあります。一度仕組みを理解すればパラメータを変えるだけで無限にバリエーションが作れる強力なスキルです。


目次

Three.jsとGLSLシェーダーの基本概念|なぜCSSではなくGPUを使うのか

Three.jsのシェーダーを使う前に、「なぜわざわざGPUを使うのか」という背景を理解しておくと、後の実装がぐっとわかりやすくなります。

Three.jsとGLSLシェーダーの役割と処理の流れを示す概念図

CPUとGPUの役割の違い

【CPUとGPUの違い】

CPU(JavaScript が動く場所)
→ 少数の高性能コアで「複雑な処理を順番に」実行する
→ アニメーションのロジック・物理演算・状態管理に向く
→ 毎フレームすべてのピクセルを計算するのは苦手

GPU(GLSLシェーダーが動く場所)
→ 数千〜数万の小さなコアで「単純な計算を並列に」実行する
→ 画面の全ピクセル(例:1920×1080 = 約200万)を同時に計算できる
→ グラフィックスエフェクト・背景アニメーションに最適

▶ 背景アニメーション = 全ピクセルに同じ計算をするだけ
  → GPUが圧倒的に速い

シェーダーの2種類:頂点シェーダーとフラグメントシェーダー

頂点シェーダー(Vertex Shader)フラグメントシェーダー(Fragment Shader)
役割3Dの頂点(点)の位置を決める各ピクセルの色を決める
入力頂点の位置・UV座標など頂点シェーダーから受け取った値・uniform変数
出力gl_Position(クリップ空間の座標)gl_FragColor(RGBAの色)
背景アニメへの役割全画面の板(PlaneGeometry)を画面に広げる各ピクセルの色・模様・アニメーションを計算する

Three.jsでシェーダーを使う仕組み

【背景アニメーションの構成要素】

1. Scene(シーン)
   → すべてのオブジェクトを配置する空間

2. Camera(カメラ)
   → OrthographicCamera(平行投影)を使うと
     画面いっぱいに平板を広げやすい

3. PlaneGeometry(平面)
   → 画面全体を覆う「板」
   → UV座標(0〜1の範囲)がフラグメントシェーダーに渡される

4. ShaderMaterial(シェーダーマテリアル)
   → vertexShader と fragmentShader を GLSL で書く
   → uniforms で JavaScript から値(時間・解像度など)を渡す

5. WebGLRenderer(レンダラー)
   → 毎フレーム render() を呼び出して画面を更新する

開発環境のセットアップ|Viteプロジェクトを5分で構築する

Three.jsの開発にはモジュールバンドラーが必要です。Viteを使うと最速で環境を構築できます。

ViteとThree.jsのプロジェクト構成図

プロジェクトの作成とThree.jsのインストール

# Viteプロジェクトを作成(Vanilla JS を選択)
npm create vite@latest threejs-shader-bg -- --template vanilla

cd threejs-shader-bg

# Three.js をインストール
npm install three

# 開発サーバーを起動
npm run dev

フォルダ構成

threejs-shader-bg/
├── index.html
├── src/
│   ├── main.js          ← Three.js のセットアップ・アニメーションループ
│   ├── shaders/
│   │   ├── wave.vert    ← 波エフェクト:頂点シェーダー
│   │   ├── wave.frag    ← 波エフェクト:フラグメントシェーダー
│   │   ├── gradient.frag← グラデーション:フラグメントシェーダー
│   │   └── noise.frag   ← ノイズ:フラグメントシェーダー
└── package.json

index.html の設定

<!-- index.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Three.js Shader Background</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { overflow: hidden; background: #000; }
    canvas { display: block; }
  </style>
</head>
<body>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

main.js:Three.jsの基本セットアップ

すべてのシェーダー背景に共通して使うベースのセットアップを作ります。

// src/main.js
import * as THREE from 'three';

// ── 1. シーン・カメラ・レンダラーの初期化 ──
const scene    = new THREE.Scene();
const renderer = new THREE.WebGLRenderer({ antialias: true });

renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);

// 背景アニメーションには平行投影カメラが適している
// 全画面に板を広げるので -1〜1 の範囲で設定する
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 10);
camera.position.set(0, 0, 1);

// ── 2. 全画面を覆う板(PlaneGeometry)を作成 ──
const geometry = new THREE.PlaneGeometry(2, 2);
// ※ OrthographicCamera の -1〜1 に合わせて幅・高さを 2 にする

// ── 3. ShaderMaterial(後のセクションで置き換える) ──
// ここではプレースホルダーとして MeshBasicMaterial を使用
const material = new THREE.MeshBasicMaterial({ color: 0x000000 });

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

// ── 4. リサイズ対応 ──
window.addEventListener('resize', () => {
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  // uniform で解像度を渡している場合はここで更新する
  if (material.uniforms?.uResolution) {
    material.uniforms.uResolution.value.set(
      window.innerWidth,
      window.innerHeight
    );
  }
});

// ── 5. アニメーションループ ──
const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);
  const elapsed = clock.getElapsedTime(); // 経過秒数

  // uniform に時間を渡す(シェーダーでアニメーションさせるため)
  if (material.uniforms?.uTime) {
    material.uniforms.uTime.value = elapsed;
  }

  renderer.render(scene, camera);
}

animate();

GLSLの基礎知識|シェーダーを書く前に必ず覚える8つのルール

GLSLはC言語に似た言語です。JavaScriptと異なる部分だけを覚えれば、すぐに書き始めることができます。

GLSLの基本構文チートシート図
【GLSLとJavaScriptの主な違い】

1. 型宣言が必須
   ✅ float x = 1.0;   // 小数は必ず .0 をつける
   ✅ int   n = 3;
   ✅ vec2  uv = vUv;  // 2次元ベクトル(x, y)
   ✅ vec3  col = vec3(1.0, 0.5, 0.0); // RGB
   ✅ vec4  rgba = vec4(col, 1.0);     // RGBA

2. 整数と小数の混在不可
   ❌ float x = 1;     // エラー!
   ✅ float x = 1.0;

3. 組み込み関数が豊富(GPU最適化済み)
   sin() / cos() / tan()         → 三角関数
   abs() / floor() / fract()     → 絶対値・切り捨て・小数部
   mix(a, b, t)                  → 線形補間(aとbをtで混ぜる)
   smoothstep(edge0, edge1, x)   → なめらかな補間
   length() / normalize()        → ベクトルの長さ・正規化
   dot() / cross()               → 内積・外積
   clamp(x, min, max)            → 値を min〜max に制限する

4. swizzling(成分へのアクセス)
   vec3 col = vec3(1.0, 0.5, 0.2);
   col.r   // → 1.0  (= col.x)
   col.rg  // → vec2(1.0, 0.5)(= col.xy)
   col.bgr // → vec3(0.2, 0.5, 1.0)(順序を入れ替えられる)

5. varying:頂点シェーダー → フラグメントシェーダーへ値を渡す
   // 頂点シェーダー側
   varying vec2 vUv;
   void main() { vUv = uv; }

   // フラグメントシェーダー側
   varying vec2 vUv;
   void main() { /* vUv を使う */ }

6. uniform:JavaScript(CPU)→ シェーダー(GPU)へ値を渡す
   uniform float uTime;      // 経過時間
   uniform vec2  uResolution; // 解像度

7. gl_Position:頂点の最終位置(頂点シェーダーの出力)
8. gl_FragColor:ピクセルの最終色(フラグメントシェーダーの出力)

実践エフェクト1:波打つ背景アニメーション(Wave Effect)

最初に実装するのは、sin関数を使った滑らかな波のアニメーションです。GLSLの基礎を実感できる最良の入門エフェクトです。

Three.js GLSLシェーダーで実装した波背景アニメーションのプレビュー

頂点シェーダー(wave.vert)

頂点シェーダーでは、平板の各頂点をsin波で上下に動かします。これで「板そのものが波打つ」3D的な表現になります。

// src/shaders/wave.vert
// uniform:JavaScript から渡す値
uniform float uTime;       // アニメーション用の経過時間
uniform float uFrequency;  // 波の細かさ(周波数)
uniform float uAmplitude;  // 波の高さ(振幅)

// varying:フラグメントシェーダーへ渡す値
varying vec2  vUv;         // UV座標(0〜1)
varying float vElevation;  // 高さ(色計算に使う)

void main() {
  // 頂点の位置をコピー
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);

  // x方向・y方向それぞれに sin 波を適用して高さを計算
  float elevation =
    sin(modelPosition.x * uFrequency + uTime) *
    sin(modelPosition.y * uFrequency + uTime) *
    uAmplitude;

  modelPosition.z += elevation; // z軸方向に動かす(高さ)

  // フラグメントシェーダーへ渡す
  vUv        = uv;
  vElevation = elevation;

  // 最終的な頂点位置を出力
  gl_Position = projectionMatrix * viewMatrix * modelPosition;
}

フラグメントシェーダー(wave.frag)

フラグメントシェーダーでは、頂点の高さ(elevation)に応じてピクセルの色を変化させます。

// src/shaders/wave.frag
uniform vec3  uDepthColor;   // 波の谷(低い部分)の色
uniform vec3  uSurfaceColor; // 波の山(高い部分)の色

varying vec2  vUv;
varying float vElevation;

void main() {
  // 高さを 0〜1 の範囲に正規化して色を補間する
  // mix(a, b, t) → a と b を t の割合で混ぜる(t=0→a、t=1→b)
  float mixStrength = (vElevation + 0.1) * 5.0; // 0〜1 に変換
  mixStrength = clamp(mixStrength, 0.0, 1.0);

  vec3 color = mix(uDepthColor, uSurfaceColor, mixStrength);

  gl_FragColor = vec4(color, 1.0);
}

JavaScript側(main.js)への組み込み

// src/main.js に追記
import waveVertexShader   from './shaders/wave.vert?raw';
import waveFragmentShader from './shaders/wave.frag?raw';

// PlaneGeometry を細かく分割する(頂点が多いほど波がなめらかになる)
const geometry = new THREE.PlaneGeometry(2, 2, 128, 128);

const material = new THREE.ShaderMaterial({
  vertexShader:   waveVertexShader,
  fragmentShader: waveFragmentShader,

  // JavaScript からシェーダーへ渡す値(uniform)
  uniforms: {
    uTime:         { value: 0 },          // アニメーション時間
    uFrequency:    { value: 3.0 },        // 波の細かさ
    uAmplitude:    { value: 0.05 },       // 波の高さ
    uDepthColor:   { value: new THREE.Color('#1a237e') }, // 深い部分:濃い青
    uSurfaceColor: { value: new THREE.Color('#4fc3f7') }, // 浅い部分:水色
  },
  // wireframe: true, // デバッグ時に頂点を確認できる
});

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

// カメラを少し傾けて3D感を出す
camera.position.set(0, 0.3, 1);
camera.lookAt(0, 0, 0);

// アニメーションループで uTime を更新する
function animate() {
  requestAnimationFrame(animate);
  material.uniforms.uTime.value = clock.getElapsedTime();
  renderer.render(scene, camera);
}
animate();

実践エフェクト2:流れるグラデーション背景(Gradient Animation)

次は、時間とともに色が流れ続けるグラデーション背景です。頂点を動かす必要がないため、頂点シェーダーは最小限にしてフラグメントシェーダーだけで表現します。

GLSLシェーダーで実装した流れるグラデーション背景のプレビュー

汎用頂点シェーダー(背景用)

グラデーション・ノイズなど「平面の色だけを変える」エフェクトでは、頂点シェーダーは以下の最小構成を使い回せます。

// src/shaders/base.vert(背景エフェクト共通)
varying vec2 vUv;

void main() {
  vUv = uv;                              // UV座標をフラグメントシェーダーへ
  gl_Position = vec4(position, 1.0);    // 頂点の位置をそのまま出力
  // OrthographicCamera + PlaneGeometry(2,2) の組み合わせでは
  // modelViewProjection を乗算しなくても全画面に広がる
}

フラグメントシェーダー(gradient.frag)

// src/shaders/gradient.frag
uniform float uTime;
uniform vec2  uResolution;
uniform vec3  uColor1;   // グラデーション色1
uniform vec3  uColor2;   // グラデーション色2
uniform vec3  uColor3;   // グラデーション色3

varying vec2 vUv;

void main() {
  // UV座標(0〜1)で画面上の位置を表す
  vec2 uv = vUv;

  // sin/cos で色が周期的に流れるようにする
  // uTime を足すことで時間とともに変化する
  float t1 = sin(uv.x * 3.14159 + uTime * 0.5) * 0.5 + 0.5;
  float t2 = cos(uv.y * 3.14159 - uTime * 0.3) * 0.5 + 0.5;

  // t1 と t2 を合成して 0〜1 の値を作る
  float blend = (t1 + t2) * 0.5;

  // 3色をブレンド
  // mix(a, b, t) で2色を補間する
  vec3 col = mix(uColor1, uColor2, blend);
  col      = mix(col,     uColor3, sin(uTime * 0.2 + uv.x) * 0.5 + 0.5);

  gl_FragColor = vec4(col, 1.0);
}

// ── カスタマイズポイント ──
// uTime のスカラー(0.5, 0.3, 0.2)を大きくすると早く流れる
// uv.x * 3.14159 の係数を変えると模様の細かさが変わる
// 色3つを好みの色に差し替えるだけでサイトカラーに合わせられる

JavaScript側への組み込み

import baseVertShader     from './shaders/base.vert?raw';
import gradientFragShader from './shaders/gradient.frag?raw';

const material = new THREE.ShaderMaterial({
  vertexShader:   baseVertShader,
  fragmentShader: gradientFragShader,
  uniforms: {
    uTime:       { value: 0 },
    uResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
    uColor1: { value: new THREE.Color('#0d0221') }, // 深い紫
    uColor2: { value: new THREE.Color('#7c3aed') }, // 紫
    uColor3: { value: new THREE.Color('#06b6d4') }, // シアン
  },
});

実践エフェクト3:有機的なノイズ背景(Noise Effect)

最も高度な表現が、ノイズ関数を使った有機的・流体的な背景です。雲・煙・マグマのような表現が可能で、ポートフォリオや高品質なLPでよく使われます。

GLSLノイズ関数で実装した有機的な流体背景アニメーションのプレビュー

GLSLにおけるノイズ関数の仕組み

【GLSLでよく使うノイズの種類】

1. 値ノイズ(Value Noise)
   → 格子点にランダムな値を割り当て、補間する
   → 実装が簡単・見た目はやや「ブロック感」がある

2. パーリンノイズ(Perlin Noise)
   → Ken Perlin が考案・映画CG業界で標準
   → 滑らかで自然な模様・フラクタル(fbm)と組み合わせると有機的

3. シンプレックスノイズ(Simplex Noise)
   → パーリンノイズの改良版(計算量が少なく、方向偏りが少ない)
   → 背景アニメーションに最適

▶ GLSLにはノイズ関数が組み込まれていないため、
  自分で実装するか既成のコードをコピーして使う

フラグメントシェーダー(noise.frag):値ノイズ+fbmを使った雲背景

// src/shaders/noise.frag
uniform float uTime;
uniform vec3  uColor1;
uniform vec3  uColor2;

varying vec2 vUv;

// ── ハッシュ関数(疑似乱数)──
// vec2 を受け取り 0〜1 のランダムな float を返す
float hash(vec2 p) {
  p = fract(p * vec2(127.1, 311.7));
  p += dot(p, p + 19.19);
  return fract(p.x * p.y);
}

// ── 値ノイズ(スムーズな補間で滑らかにする)──
float valueNoise(vec2 p) {
  vec2 i = floor(p);  // 整数部(格子のインデックス)
  vec2 f = fract(p);  // 小数部(格子内の位置)

  // 4つの格子点のランダム値
  float a = hash(i + vec2(0.0, 0.0));
  float b = hash(i + vec2(1.0, 0.0));
  float c = hash(i + vec2(0.0, 1.0));
  float d = hash(i + vec2(1.0, 1.0));

  // smoothstep で滑らかに補間(f^2 * (3 - 2f))
  vec2 u = f * f * (3.0 - 2.0 * f);

  // 双線形補間
  return mix(
    mix(a, b, u.x),
    mix(c, d, u.x),
    u.y
  );
}

// ── fBm(Fractal Brownian Motion)──
// 周波数・振幅を変えたノイズを重ねて有機的にする
float fbm(vec2 p) {
  float value     = 0.0;
  float amplitude = 0.5;   // 最初の振幅
  float frequency = 1.0;   // 最初の周波数

  // 6オクターブ重ねる(多いほど細かく複雑になる。重いので注意)
  for (int i = 0; i < 6; i++) {
    value     += amplitude * valueNoise(p * frequency);
    amplitude *= 0.5;    // 高周波ほど弱くする(1/f ノイズ)
    frequency *= 2.0;    // 周波数を2倍にする
  }
  return value;
}

void main() {
  vec2 uv = vUv;

  // uTime で動かす(uvに時間を足すと流れるように見える)
  float n = fbm(uv * 3.0 + vec2(uTime * 0.1, uTime * 0.05));

  // ノイズ値(0〜1)で2色を補間
  vec3 color = mix(uColor1, uColor2, n);

  // 少し明るさを足してメリハリを出す
  color = mix(color, vec3(1.0), smoothstep(0.7, 1.0, n) * 0.3);

  gl_FragColor = vec4(color, 1.0);
}

JavaScript側への組み込み

import baseVertShader  from './shaders/base.vert?raw';
import noiseFragShader from './shaders/noise.frag?raw';

const material = new THREE.ShaderMaterial({
  vertexShader:   baseVertShader,
  fragmentShader: noiseFragShader,
  uniforms: {
    uTime:   { value: 0 },
    uColor1: { value: new THREE.Color('#0a0a0a') }, // 暗い色(雲の影)
    uColor2: { value: new THREE.Color('#e0e0ff') }, // 明るい色(雲の光)
  },
});

ポストプロセッシング|レンダリング後に画面全体にエフェクトをかける

ポストプロセッシングとは、シーンをレンダリングした後の画面全体にさらに効果をかける技術です。ブルーム(光の滲み)・彩度・グレイン(フィルム粒子感)などで仕上げのクオリティが格段に上がります。

Three.jsポストプロセッシングのエフェクト適用前後の比較図

Three.jsのポストプロセッシングのセットアップ

# Three.js の addons(ポストプロセッシング)をインポート
# Three.js v0.150以降は three/addons からインポートする
# ※ npm install は不要(Three.jsに同梱)
// src/main.js にポストプロセッシングを追加
import * as THREE from 'three';
import { EffectComposer }   from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass }       from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass }  from 'three/addons/postprocessing/UnrealBloomPass.js';
import { ShaderPass }       from 'three/addons/postprocessing/ShaderPass.js';
import { FilmPass }         from 'three/addons/postprocessing/FilmPass.js';

// ── EffectComposer のセットアップ ──
// renderer の代わりに composer.render() を呼び出す
const composer = new EffectComposer(renderer);

// 1. シーンを普通にレンダリング
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

// 2. ブルーム(光の滲み)効果
//    引数:(解像度, strength, radius, threshold)
const bloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  0.8,   // strength:強さ(大きいほどぼんやりする)
  0.4,   // radius:広がり
  0.2    // threshold:この輝度以上にブルームをかける
);
composer.addPass(bloomPass);

// 3. フィルムグレイン(映画フィルムの粒子感)
//    引数:(noiseIntensity, scanlinesIntensity, scanlinesCount, grayscale)
const filmPass = new FilmPass(0.3, 0.0, 0, false);
composer.addPass(filmPass);

// アニメーションループを更新
function animate() {
  requestAnimationFrame(animate);
  material.uniforms.uTime.value = clock.getElapsedTime();
  composer.render(); // renderer.render() の代わりに composer.render()
}
animate();
【ポストプロセッシング パス一覧】

UnrealBloomPass  → 光の滲み(ブルーム)
                   シェーダー背景の明るい部分を光らせたいときに最適

FilmPass         → フィルムグレイン+スキャンライン
                   ノイズ背景にレトロ感を出したいときに

RGBShiftShader   → RGBのズレ(グリッチ感)
                   テック系・サイバーパンク系の表現に

GlitchPass       → ランダムなグリッチ(乱れ)
                   インパクトある演出に

VignetteShader   → 周辺を暗くするビネット効果
                   視線を中央に集中させたいときに

DotScreenShader  → ドット絵のような表現
                   レトロ・アート系のサイトに

パフォーマンス最適化|60fps を維持するための7つのポイント

シェーダー背景は美しい一方、処理が重くなりがちです。特にスマートフォンでは最適化を怠るとカクつきが発生します。以下の7点を意識することで多くの環境で60fpsを維持できます。

Three.jsシェーダーのパフォーマンス最適化ポイント7選

最適化ポイント1:PixelRatioを制限する

// Retinaディスプレイでは devicePixelRatio が 2〜3 になる
// そのまま使うとレンダリング面積が4〜9倍になる
// → 最大2に制限する
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

最適化ポイント2:PlaneGeometryの分割数を適切に設定する

// 波エフェクト(頂点を動かす場合):分割数が多いほどなめらか
// ただし多すぎると頂点シェーダーの処理が増える
const geometry = new THREE.PlaneGeometry(2, 2, 128, 128); // ← 適切な上限

// フラグメントシェーダーだけを使う場合(グラデーション・ノイズ):
// 頂点を動かさないので分割は不要
const geometry = new THREE.PlaneGeometry(2, 2); // デフォルトの 1×1 分割で十分

最適化ポイント3:fBmのオクターブ数を減らす

// ノイズエフェクトの fbm 関数のループ回数
// ループ1回ごとにGPUの計算量が増加する
// スマートフォンでは 4〜5 程度に抑えるのが無難

for (int i = 0; i < 4; i++) { // ← スマホ向けは4程度に
  value     += amplitude * valueNoise(p * frequency);
  amplitude *= 0.5;
  frequency *= 2.0;
}
【パフォーマンス最適化 7ポイント まとめ】

1. setPixelRatio(Math.min(devicePixelRatio, 2)) でRetinaを制限
2. PlaneGeometry の分割数を必要最小限に(フラグメントのみなら 1×1 で十分)
3. fBm のオクターブ数をスマホ向けは 4〜5 に抑える
4. ポストプロセッシングは効果が見えるものだけ残す
5. requestAnimationFrame は 1つだけにする(複数 animate() を走らせない)
6. `renderer.dispose()` でページ離脱時にメモリを解放する
7. stats.js(Three.js 付属)でFPSをリアルタイム計測しながら調整する

よくある質問(FAQ)

Three.jsを学ぶ前に何を知っておく必要がありますか?

JavaScript(ES Modules・async/await)とHTML/CSSの基礎知識があれば始められます。Three.js自体の学習コストは比較的低く、公式ドキュメントのサンプルを動かしながら学ぶのが最も効率的です。GLSLシェーダーは別途必要になりますが、基本的な数学(sin・cos・線形補間)を知っていれば入門できます。

GLSLのエラーはどこで確認できますか?

ブラウザの開発者ツール(コンソール)に表示されます。Three.jsはシェーダーのコンパイルエラーを詳細にコンソールへ出力するため、「ERROR: 0:XX: …」という形式でどの行に問題があるか確認できます。また、VSCode拡張「GLSL Lint」を使うとエディタ上でリアルタイムにエラーをチェックできます。

シェーダー背景は既存のWordPressサイトに組み込めますか?

可能です。ビルドしたJavaScript(Viteなら npm run build で生成)を wp_enqueue_script() でエンキューし、背景に使う <canvas> 要素を対象セクションに追加すれば組み込めます。position: fixedz-index でcanvasを背面に配置することで、既存のコンテンツと重ねることができます。

glslifyやthree-custom-shader-materialといったライブラリは使うべきですか?

慣れてきたら積極的に活用するとよいでしょう。three-custom-shader-material はThree.jsの組み込みマテリアル(MeshStandardMaterialなど)をベースにシェーダーをカスタマイズできるので、ライティングや影はThree.jsに任せてシェーダーは色の変化だけを書くという分業ができます。入門段階では ShaderMaterial をゼロから書いて仕組みを理解するのを優先してください。

シェーダーを学ぶのにおすすめのリソースはありますか?

The Book of Shaders(英語・一部日本語)が最もよく使われる入門リソースです。ブラウザ上でGLSLを書いて即座に結果が見られるインタラクティブな教材で、ノイズ・パターン・色空間などを体系的に学べます。また、Shadertoy.com には世界中のクリエイターが作ったシェーダーのソースコードが公開されており、参考にしながら模写することで実力が身につきます。


生徒

GLSLって数学が得意じゃないと無理ですか?sin とか cos とか、もう忘れちゃいました…

ペン博士

数学が苦手でも大丈夫じゃ!GLSLで使う数学は「sin で波を作る」「mix で色を混ぜる」「fract で繰り返しを作る」など、直感的に理解できるものが大半じゃ。WithCodeでJavaScriptとWeb制作の基礎を固めた上で、シェーダーのコードを「真似して動かす」ことから始めれば、1ヶ月もすれば自分でアレンジできるようになるんじゃぞ!

生徒

なるほど!まず The Book of Shaders を読みながら、今日のコードを動かして実験してみます。シェーダー、思ったより身近に感じてきました!


まとめ

Three.jsとGLSLシェーダーを組み合わせると、CSSでは不可能な高品質な背景アニメーションをWebサイトに導入でき、訪問者のエンゲージメントと体験品質を大きく向上させることができます。

  • シェーダーの本質:頂点シェーダー(頂点の位置)とフラグメントシェーダー(ピクセルの色)の2つの役割を理解すればGLSLの全体像がつかめる
  • 波エフェクト:sin波をuniformのuTimeで動かし、高さを色に変換するだけで滑らかな波背景が作れる
  • グラデーション:sin/cosと3色のmixだけで時間とともに流れるグラデーション背景を実装できる。頂点シェーダーは最小構成で使い回せる
  • ノイズ背景:ハッシュ関数→値ノイズ→fBmの3ステップで有機的な雲・流体表現が実現できる。fBmのオクターブ数はパフォーマンスとのトレードオフ
  • ポストプロセッシング:UnrealBloomPassとFilmPassを重ねるだけで仕上げのクオリティが大幅に向上する
  • パフォーマンス:setPixelRatio制限・分割数最小化・fBmオクターブ制限の3点が最重要。スマートフォンでは特に意識する

WithCodeで学んだJavaScriptとWeb制作の基礎を土台に、今回のシェーダーコードを動かして少しずつパラメータを変えてみることから始めてください。「動かして→変えて→理解する」というサイクルが、シェーダー習得の最短ルートです。


WithCodeを体験できる初級コース公開中!

初級コース(¥49,800)が完全無料に!

  • 期間:1週間
  • 学習内容:ロードマップ/基礎知識/環境構築/HTML/CSS/JavaScript → Three.jsやGLSLシェーダーを学ぶための必須基礎(JavaScript・ES Modules・DOM操作)を体系的に習得できるカリキュラムです

副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?

未経験でも心配することはありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!

この記事を書いた人

WithCode(ウィズコード)は「目指すなら稼げる人材」をビジョンに、累計400名以上のフリーランスを輩出してきた超実践型プログラミングスクールです。150社以上の実案件支援を特徴にWeb制作・Webデザインなどの役立つ情報を現場のノウハウに基づいて発信していきます。

– service –WithGroupの運営サービス

  • WithCode
    - ウィズコード -

    スクール

    「未経験」から
    現場で通用する
    スキルを身に付けよう!

    詳細はこちら
  • WithFree
    - ウィズフリ -

    実案件サポート

    制作会社のサポート下で
    実務経験を積んでいこう!

    詳細はこちら
  • WithCareer
    - ウィズキャリ -

    就転職サポート

    大手エージェントのサポート下で
    キャリアアップを目指そう!

    詳細はこちら

公式サイト より
今すぐ
無料カウンセリング
予約!

目次