簡単で効果大! Three.jsのポストプロセスで映える3D表現

3DコンテンツをJavaScriptであつかうThree.jsでは、3Dシーンに印象的な効果を加えるポストプロセスの機能があります。

ポストプロセスとは、レンダリングされた画像や映像に後からエフェクトを加える処理です。グリッチやノイズなどの演出を加えたり、色を変化させたり、独特のにじみを加えたりと、さまざまな種類があります。

この記事ではポストプロセスを使い3Dシーンにエフェクトを追加する方法を紹介します。以下のデモはオリジナルの3Dシーンにポストプロセスを加えたものです。見た目の大幅な変化とは裏腹に、実装は意外とシンプルなので試したことのない方はぜひチャレンジしてみてください。

ポストプロセスを使用したエフェクト

ポストプロセスの事例

まずはポストプロセスが効果的に使われているウェブサイトを紹介します。

WindLand - By Neotix』は、Three.jsを使った美しい3Dビジュアルが印象的なウェブサイトです(注意: 音が出ます)。

このウェブサイトでは、Three.jsのEffectComposerを使用してポストプロセスを実装しています。昼のシーンではBokehPassエフェクトを使用して、遠くにある背景をぼかすことで奥行きを表現しています。 画面右上の月のマークをクリックすると夜のシーンに変更できます。夜のシーンでは、UnrealBloomPassエフェクトでビルの窓から光があふれるような演出を行っています。

WindLandのポストプロセス

このようにポストプロセスを活用することで、単なる3Dオブジェクトの配置だけでは表現できない質感や雰囲気を作り出せます。

制作者のAnderson Mancini氏の記事『Case Study: Windland — An Immersive Three.js Experience』では、ポストプロセスだけでないThree.jsのテクニックが解説されています。興味のある方はぜひ読んでみてください。

Three.jsで簡単に実装してみる

ポストプロセスは奥の深い技術ですが、Three.jsの公式アドオンを利用すると主要なエフェクトを簡単に導入できます。ここからは実際に実装してみます。

WebGPUを使用したポストプロセス

デモではWebGPUを使用して実装します。WebGPUは従来のWebGLとは違い、GPUを効率的に使うことでパフォーマンスの向上が期待できます。

Three.jsではWebGPUとWebGLで使用するモジュールが異なります。さきほど紹介したWindlandの事例はWebGLを使った手法です。

シンプルなシーンにポストプロセスを追加する

立方体をアニメーションさせているシーンにポストプロセスを追加してみましょう。関連するところを抜粋して説明します。すべてのコードを見たい方は以下のリンクから確認してください。

  1. 通常のThree.jsと同じようにシーンやカメラを追加した後、three/webgpuからインポートしたPostProcessingクラスにrendererを渡してインスタンス化します。
  2. scenecamerapass()関数に渡してscenePassを作成します。
  3. 作成したscenePassからテクスチャノードを取得します。
  4. 公式のアドオンから好きなエフェクトを選んでインポートします。今回はdotScreen()関数を使用します。
  5. 3で作成したscenePassColordotScreen()関数に渡し、戻り値をpostProcessingoutputNodeに代入します。
  6. アニメーション関数内でpostProcessing.render()関数を呼び出して画面を描画します。
import { PostProcessing } from "three/webgpu";
import { pass } from "three/tsl";
import { dotScreen } from "three/examples/jsm/tsl/display/DotScreenNode.js"; // 4.

  // 省略
  // カメラ、シーン、boxオブジェクト、レンダラーを設定する

  // ポストプロセス
  const postProcessing = new PostProcessing(renderer); // 1.
  const scenePass = pass(scene, camera); // 2.
  const scenePassColor = scenePass.getTextureNode(); // 3.
  const dotScreenPass = dotScreen(scenePassColor); // 5.
  postProcessing.outputNode = dotScreenPass; // 5.

  // 省略

  const animate = () => {
    // レンダリングする
    postProcessing.render(); // 6.
  };

たったこれだけでポストプロセスの実装が完了しました! いわゆるハーフトーンと呼ばれる、新聞のような雰囲気のシーンになりました。パラメーターを変更すると描画結果が変わります。ためしにscaleの値を0.1に変更するとドットが大きくなりました。

const dotScreenPass = dotScreen(scenePassColor);
dotScreenPass.scale.value = 0.1; // ✨これを追加

シンプルなシーンのdotScreenエフェクト

さまざまなポストプロセス

もう少し複雑なシーンに追加したデモも見てみましょう。

Chromatic Aberration + Depth of Fieldエフェクト

Chromatic Aberration(色収差)は画面の暗い部分と明るい部分の境目にRGBシフトのような色の差を発生させるエフェクトです。草や海の境目部分に赤や黄色の色のブレが見られます。現実のカメラレンズを通した映像のような効果が得られます。

Depth of Fieldはカメラの焦点のように、近いものにピントが合い、遠いものはぼかしを加えます(被写界深度)。

▼抜粋

// 省略
import { chromaticAberration } from "three/addons/tsl/display/ChromaticAberrationNode.js";
import { dof } from "three/addons/tsl/display/DepthOfFieldNode.js";

/**
 * Chromatic Aberration + Depth of Fieldエフェクトの作成
 * @param node
 * @param viewZ
 */
export const createChromatic = (node: ShaderNodeObject<TextureNode>, viewZ: ShaderNodeObject<Node>) => {
  const centerVector = new THREE.Vector2(0.4, 0.4);
  const centerNode = uniform(centerVector);
  // 色収差
  const chromaticPass = chromaticAberration(node, float(0.2), centerNode);
  // 被写界深度
  // 焦点をカメラの近くに固定し、遠くなるほどボケさせる
  const dofPass = dof(chromaticPass, viewZ, 1);
  return dofPass;
};

Chromatic Aberration + Depth of Fieldエフェクト

Bloomエフェクト

シーンの中の明るい場所から光があふれているような表現ができます。デモでは比較的明るい草むらの花が光っているように見えます。

※明るい場所と暗い場所をはっきりさせるため、エフェクトを実行する前に全体の色味やライトの強さなどを変更する処理を追加しています。

▼抜粋

// 省略
import { bloom } from "three/examples/jsm/tsl/display/BloomNode.js";

/**
 * Bloomエフェクトの作成
 * @param node
 */
export const createBloom = (node: ShaderNodeObject<TextureNode>) => {
  const bloomPass = bloom(node, 0.3, 0, 0);
  bloomPass.smoothWidth.value = 0.3;
  return bloomPass;
};

Bloomエフェクト

Pixelationエフェクト

昔のゲーム画面のようなドット絵にできるエフェクトです。レトロな雰囲気がかわいいですよね。

▼抜粋

// 省略
import { pixelationPass } from "three/examples/jsm/tsl/display/PixelationPassNode.js";

/**
 * Pixelationエフェクトの作成
 * @param node
 */
export const createPixelation = (scene: THREE.Scene, camera: THREE.PerspectiveCamera) => {
  const size = uniform(5);
  const normalStrength = uniform(0.5);
  const edgeSharpness = uniform(1);
  const pixelation = pixelationPass(scene, camera, size, normalStrength, edgeSharpness);
  return pixelation;
};

Pixelationエフェクト

Sepia + Film + Gaussian Blurエフェクト

Sepiaはその名の通り画面全体をセピア色に変化させます。昔の映像の雰囲気を出すために、Sepiaに加えてGaussian Blurエフェクトでぼかし、Filmエフェクトでざらざら感を追加しています。

このように複数のエフェクトの組み合わせも簡単です。

// 省略
import { sepia as sepiaPass } from "three/examples/jsm/tsl/display/Sepia.js";
import { film as filmPass } from "three/examples/jsm/tsl/display/FilmNode.js";
import { gaussianBlur as gaussianBlurPass } from "three/examples/jsm/tsl/display/GaussianBlurNode.js";

/**
 *  Sepia + Film + Gaussian Blurエフェクトの作成
 * @param node
 */
export const createSepia = (node: ShaderNodeObject<TextureNode>) => {
  const sepia = sepiaPass(node);
  const blur = gaussianBlurPass(sepia, 0.5);
  const film = filmPass(blur);
  return film;
}

Sepia、Blur、Filmエフェクト

もっと複雑なエフェクトを作りたい

今回はあらかじめ用意してあるアドオンを使ったエフェクトを紹介しました。もっと複雑なエフェクトを作りたい場合はコードを書くことで実現できます。

Three.jsでは、WebGPUの場合はTSL言語を使い、WebGLの場合はGLSL言語で記述するのが一般的です。この記事では紹介しませんが、『WebGPU対応のThree.jsのはじめ方』はTSLでの実装方法を、『WebGLのシェーダーGLSLでの画像処理の作り方(モノクロ、セピア、モザイク、渦巻き)』ではGLSLでの実装方法を解説しています。興味がある方はぜひ読んでみてください。

コラム:react-postprocessing

ReactでThree.jsを使うためのライブラリにReact Three Fiberがあります。ICS MEDIAでは『React Three Fiber入門 ReactとThree.jsで始める3D表現』で紹介しています。

React Three Fiberでポストプロセスを実装する際はreact-postprocessingを導入するのが手軽です。今回紹介したようなエフェクトをはじめ他にも魅力的なエフェクトがたくさんあるのでオススメです。

ただし、react-postprocessingは2025年11月現在WebGLのみに対応しており、WebGPUには対応していません。 WebGPUが主流になれば対応する可能性もありますが、現時点では未定です。

まとめ

Three.jsのポストプロセスについて紹介しました。ポストプロセスを活用すると3Dシーンに印象的な演出を加えられます。アドオンを使えば簡単に実装できるので、自分好みの表現を探してみましょう!

SNSでシェアしよう
シェアいただくと、サイト運営の励みになります!
X(旧Twitter)へポスト
はてなブックマークへ投稿
URLをコピー
北川 杏子

アパレル、事務を経てエンジニアに転身。フルスタックエンジニアとしてバックエンド、フロントエンド両方の開発を経験したのちICSに入社。特技は英語。

この担当の記事一覧