threejs-postprocessing について
three.js r186 の描画にポストプロセスを適用する開発者向けのリファレンススキルです。WebGL の EffectComposer パスチェーンと WebGPU の TSL レンダーパイプラインを、一般的な画像効果の例とともに解説します。
主な機能
- 01EffectComposer のパスチェーン設定
- 02ブルーム・被写界深度・SSAO
- 03アウトライン・粒子・グリッチ・ピクセル化
- 04カスタム ShaderPass エフェクト
- 05WebGPU TSL レンダーパイプライン
- 06リサイズと出力カラー処理
活用シーン
- 01描画シーンへのブルーム追加
- 02被写界深度や環境遮蔽の適用
- 03カスタム画像エフェクトの作成
- 04WebGPURenderer でのポストプロセス
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cloudai-x/ threejs-skills --skill threejs-postprocessingインストール後、Claude Code で/threejs-postprocessingを入力して読み込まれたか確認します。
詳細
Three.js ポストプロセッシング
three.js r186 を対象としています。例では WebGLRenderer を使用します。WebGPU と TSL のセクションでは WebGPURenderer を使用します。
クイックスタート
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 { OutputPass } from "three/addons/postprocessing/OutputPass.js";
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 0, 5);
// A bright emissive sphere gives bloom something to pick up
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 16),
new THREE.MeshStandardMaterial({ color: 0x222222, emissive: 0xffffff, emissiveIntensity: 2 }),
);
scene.add(mesh);
// The composer replaces renderer.render(). Render the scene, then effects,
// then OutputPass last: it applies renderer.toneMapping and outputColorSpace once.
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
// bloomPass.strength, .radius and .threshold are live properties; threshold is the luminance above which things glow
composer.addPass(bloomPass);
composer.addPass(new OutputPass());
function animate() {
composer.render(); // not renderer.render()
}
renderer.setAnimationLoop(animate);
パスの順序
コンポーザーのバッファーはリニアカラーを保持します。トーンマッピングと sRGB 変換は、レンダラーの設定を使用して OutputPass で一度だけ行われます。パスは次の順序で構成します:RenderPass → リニア空間のエフェクト(ブルーム、被写界深度、SSAO、アウトラインなど)→ エッジを滑らかにしたいエフェクトの後に SMAAPass → OutputPass → FXAAPass(three/addons/postprocessing/FXAAPass.js)。FXAA を使用する場合に限り、FXAA には sRGB 入力が必要なため追加します。FXAA を使用しない場合は、OutputPass が最後のパスになります。
SSAOPass は前のパスが生成した画像を暗くするため、その前に RenderPass を置く必要があります。RenderPixelatedPass はシーン自体をレンダリングするため、RenderPass の代わりに使用します。pass.enabled = false を設定すると、チェーンを再構築せずにパスをスキップできます。
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 { SMAAPass } from "three/addons/postprocessing/SMAAPass.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85));
composer.addPass(new SMAAPass()); // no arguments; the composer sizes it
composer.addPass(new OutputPass());
コンポーザーの設定
- コンポーザーは構築時にレンダラーのピクセル比をコピーします。コンポーザーの構築前に
renderer.setPixelRatio()を呼び出すか、構築後にcomposer.setPixelRatio()を呼び出してください。 - コンポーザーのバッファーはマルチサンプルではありません。レンダラーに
antialias: trueを設定しても、コンポーザーのバッファーには適用されません。シーンのレンダリングとコンポーザーのバッファーに MSAA を使用するには、samplesを指定した独自のバッファー(CSS ピクセル単位のサイズ)を渡し、コンポーザーのサイズを一度設定します。
import * as THREE from "three";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
const composer = new EffectComposer(
renderer,
new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
type: THREE.HalfFloatType,
samples: 4, // MSAA on the composer's two buffers
}),
);
composer.setSize(window.innerWidth, window.innerHeight); // scales the buffer by the pixel ratio
- リサイズ時は、
camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); composer.setSize(w, h);を呼び出してください。コンポーザーがバッファーとすべてのパスを、ピクセル比も含めてリサイズします。パスを手動でリサイズしないでください。 composer.render()は独自の内部タイマーを使用して、時間ベースのパス(FilmPass)を制御します。時間を自分で管理する場合に限り、秒単位のデルタを渡してください。- コンポーザーは毎フレーム、最後に有効なパスに
renderToScreenを設定します。以下の選択的ブルームのように結果を画面に出力しない場合に限り、composer.renderToScreen = falseを設定してください。
選択的ブルーム
レイヤーで指定したメッシュだけにブルームを適用します。それ以外はすべて黒く描画して画面外にレンダリングするブルーム専用コンポーザーを作成し、そのテクスチャを、クリーンなシーンをレンダリングする最終コンポーザーに合成します。scene.background はメッシュではないため、bloomComposer.render() の前後で null に設定してください。そうしないと、最終画像に背景がもう一度追加されます。リサイズ時は、両方のコンポーザーで setSize を呼び出してください。
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 { OutputPass } from "three/addons/postprocessing/OutputPass.js";
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
const BLOOM_LAYER = 1;
const bloomLayer = new THREE.Layers();
bloomLayer.set(BLOOM_LAYER); // test mask: matches any object enabled on this layer
mesh.layers.enable(BLOOM_LAYER); // add the layer; set() would remove the mesh from layer 0
// Paint non-bloomed meshes black, then restore them
const darkMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
const materials = new Map();
function darkenNonBloomed(object) {
if (object.isMesh && bloomLayer.test(object.layers) === false) {
materials.set(object, object.material);
object.material = darkMaterial;
}
}
function restoreMaterial(object) {
if (materials.has(object)) {
object.material = materials.get(object);
materials.delete(object);
}
}
// Bloom-only composer; with renderToScreen off, its result stays in renderTarget2
const bloomComposer = new EffectComposer(renderer);
bloomComposer.renderToScreen = false;
bloomComposer.addPass(new RenderPass(scene, camera));
bloomComposer.addPass(new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85));
// Final composer: the clean scene plus the bloom texture, mixed by a ShaderPass
const finalComposer = new EffectComposer(renderer);
finalComposer.addPass(new RenderPass(scene, camera));
finalComposer.addPass(
new ShaderPass(
new THREE.ShaderMaterial({
uniforms: {
baseTexture: { value: null }, // filled from the previous pass
bloomTexture: { value: bloomComposer.renderTarget2.texture },
},
vertexShader: /* glsl */ `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: /* glsl */ `
uniform sampler2D baseTexture;
uniform sampler2D bloomTexture;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(baseTexture, vUv) + texture2D(bloomTexture, vUv);
}
`,
}),
"baseTexture",
),
);
finalComposer.addPass(new OutputPass());
function animate() {
const background = scene.background;
scene.background = null; // not a mesh: it would bloom and be added twice
scene.traverse(darkenNonBloomed); // black scene: only marked meshes bloom
bloomComposer.render();
scene.traverse(restoreMaterial);
scene.background = background;
finalComposer.render(); // clean scene + bloom to the screen
}
renderer.setAnimationLoop(animate);
被写界深度
import { BokehPass } from "three/addons/postprocessing/BokehPass.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera)); // BokehPass reads this color image
const bokehPass = new BokehPass(scene, camera, {
focus: 10.0, // view-space depth that stays sharp, in world units
aperture: 0.025, // how far out of focus things blur
maxblur: 0.01, // cap on the blur radius
});
composer.addPass(bokehPass);
composer.addPass(new OutputPass());
// Refocus at runtime: distanceTo matches the view-space depth for objects near the view center
bokehPass.uniforms.focus.value = camera.position.distanceTo(mesh.position);
アンビエントオクルージョン
import { SSAOPass } from "three/addons/postprocessing/SSAOPass.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
const composer = new EffectComposer(renderer);
// SSAOPass darkens the previous image, so the scene render must come first
composer.addPass(new RenderPass(scene, camera));
const ssaoPass = new SSAOPass(scene, camera, window.innerWidth, window.innerHeight);
ssaoPass.kernelRadius = 16;
ssaoPass.minDistance = 0.005;
ssaoPass.maxDistance = 0.1;
composer.addPass(ssaoPass);
composer.addPass(new OutputPass());
// Debug views: ssaoPass.output = SSAOPass.OUTPUT.SSAO (or Blur, Depth, Normal); the default shows the shaded scene
アウトライン
import * as THREE from "three";
import { OutlinePass } from "three/addons/postprocessing/OutlinePass.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
outlinePass.selectedObjects = [mesh]; // only these are outlined
outlinePass.edgeStrength = 3;
outlinePass.visibleEdgeColor.set(0xffffff);
outlinePass.hiddenEdgeColor.set(0x190a05); // parts hidden behind other objects
composer.addPass(outlinePass);
composer.addPass(new OutputPass());
その他のパス
フィルムグレイン、グリッチ、ハーフトーン、ビネットは、チェーンの途中にそのまま追加できるフィルターです。ColorCorrectionShader はビネットと同様に使用します。powRGB/mulRGB/addRGB のユニフォームベクトルを持つ ShaderPass です。デフォルトの powRGB(2, 2, 2)は画像を暗くするため、中立にするには(1, 1, 1)から始めてください。
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
import { FilmPass } from "three/addons/postprocessing/FilmPass.js";
import { GlitchPass } from "three/addons/postprocessing/GlitchPass.js";
import { HalftonePass } from "three/addons/postprocessing/HalftonePass.js";
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
import { VignetteShader } from "three/addons/shaders/VignetteShader.js";
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const filmPass = new FilmPass(0.35, false); // grain intensity, grayscale; animates from the composer's delta
composer.addPass(filmPass);
const glitchPass = new GlitchPass();
glitchPass.goWild = true; // glitch every frame; false (the default) glitches at random intervals
composer.addPass(glitchPass);
const halftonePass = new HalftonePass({ shape: 1, radius: 4, scatter: 0, greyscale: false }); // shape: 1 dot, 2 ellipse, 3 line, 4 square, 5 diamond
composer.addPass(halftonePass);
const vignettePass = new ShaderPass(VignetteShader); // uniforms: offset and darkness
vignettePass.uniforms.offset.value = 1.0;
vignettePass.uniforms.darkness.value = 1.0;
composer.addPass(vignettePass);
composer.addPass(new OutputPass());
RenderPixelatedPass はシーン自体をレンダリングするため、RenderPass の後に続けるのではなく、その代わりに使用します。
import { RenderPixelatedPass } from "three/addons/postprocessing/RenderPixelatedPass.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
const composer = new EffectComposer(renderer);
const pixelatedPass = new RenderPixelatedPass(6, scene, camera); // 6 = pixel size
pixelatedPass.normalEdgeStrength = 0.3; // edges where the surface normal changes (creases)
pixelatedPass.depthEdgeStrength = 0.4; // depth-discontinuity edges
composer.addPass(pixelatedPass);
composer.addPass(new OutputPass());
カスタムパス
ShaderMaterial 形式のシェーダーオブジェクトは、どれでも ShaderPass でラップできます。tDiffuse ユニフォームには前のパスの出力が渡されます。リニア空間で動作するカスタムエフェクトは OutputPass の前に置きます。FXAA のように sRGB 入力が必要な場合に限り、その後に置いてください。
import * as THREE from "three";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
const WaveShader = {
uniforms: {
tDiffuse: { value: null },
time: { value: 0 },
intensity: { value: 1.0 },
},
vertexShader: /* glsl */ `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: /* glsl */ `
uniform sampler2D tDiffuse;
uniform float time;
uniform float intensity;
varying vec2 vUv;
void main() {
vec2 uv = vUv;
uv.x += sin(uv.y * 10.0 + time) * 0.01 * intensity; // wave distortion
gl_FragColor = texture2D(tDiffuse, uv);
}
`,
};
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const wavePass = new ShaderPass(WaveShader);
composer.addPass(wavePass);
composer.addPass(new OutputPass());
// Drive time uniforms from a Timer
const timer = new THREE.Timer();
timer.connect(document); // keeps deltas sane in inactive tabs
function animate() {
timer.update();
wavePass.uniforms.time.value = timer.getElapsed();
composer.render();
}
renderer.setAnimationLoop(animate);
レンダラー組み込みエフェクト(コンポーザーなし)
renderer.setEffects() は通常の renderer.render() の後に内部 HDR バッファー上で通常のパスを実行します。コンポーザーは不要で、トーンマッピングと出力色空間はエフェクトの後に自動的に適用されます。outputBufferType: THREE.HalfFloatType(または FloatType)が必要です。OutputPass と FXAAPass は使用せず、antialias: true によってアンチエイリアスを適用し、renderer.setSize() でリサイズします。この場合、エフェクトにはデルタ時間が渡されません。FilmPass の時間ユニフォームが NaN になり、グレインが失われるため、時間駆動のパスは EffectComposer で実行してください。
import * as THREE from "three";
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
const renderer = new THREE.WebGLRenderer({ antialias: true, outputBufferType: THREE.HalfFloatType });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
renderer.setEffects([bloomPass]);
function animate() {
renderer.render(scene, camera); // bloom runs on the internal HDR buffers after the scene render
}
renderer.setAnimationLoop(animate);
WebGPU と TSL
WebGPURenderer では、ポストプロセスは RenderPipeline 上の TSL ノードグラフであり、パスチェーンではありません。pass() でシーンをレンダリングし、three/addons/tsl/display/*.js からエフェクトを構築して、最終ノードを renderPipeline.outputNode に割り当てます。pass() はレンダラーの MSAA サンプル数を使用するため、antialias: true が機能します。ただし、ao() のプリパスでは pass(scene, camera, { samples: 0 }) が必要で、traa では antialias を無効にしたレンダラーが必要です。また、pass() は renderer.setSize() に追従するため、パイプライン側でリサイズを呼び出す必要はありません。
import * as THREE from "three/webgpu";
import { pass } from "three/tsl";
import { bloom } from "three/addons/tsl/display/BloomNode.js";
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init(); // before any other use of the renderer
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111); // the WebGPU canvas is transparent by default
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 0, 5);
scene.add(new THREE.Mesh(new THREE.SphereGeometry(1, 32, 16), new THREE.MeshBasicNodeMaterial({ color: 0xffffff })));
const renderPipeline = new THREE.RenderPipeline(renderer);
const scenePass = pass(scene, camera);
const scenePassColor = scenePass.getTextureNode("output");
// bloom() returns only the glow, so add it to the scene color
renderPipeline.outputNode = scenePassColor.add(bloom(scenePassColor, 1.5, 0.4, 0.85));
function animate() {
renderPipeline.render(); // instead of renderer.render()
}
renderer.setAnimationLoop(animate);
フィルターは完成した画像を返し、出力ノードを置き換えます。dof、film、rgbShift、chromaticAberration、afterImage、dotScreen、lut3D、smaa(リニア入力)はシーンのカラーノードを受け取り、pixelationPass(scene, camera, 6) はシーン自体をレンダリングします。また、traa には velocity MRT と antialias を無効にしたレンダラーが必要です。bloom と同様に、outline(scene, camera, { selectedObjects }) が返すのはエッジマスク(.visibleEdge、.hiddenEdge)のみです。これらに色を付けてシーンカラーに加えます。ssao(depthNode, normalNode, camera) が返すのは .r にあるオクルージョン係数のみで、シーンカラーに乗算します。
import * as THREE from "three/webgpu";
import { pass, renderOutput } from "three/tsl";
import { dof } from "three/addons/tsl/display/DepthOfFieldNode.js";
import { fxaa } from "three/addons/tsl/display/FXAANode.js";
const renderPipeline = new THREE.RenderPipeline(renderer);
const scenePass = pass(scene, camera);
const scenePassColor = scenePass.getTextureNode("output");
// A filter replaces the image: node, viewZ, focus distance, focal length, bokeh scale
const dofNode = dof(scenePassColor, scenePass.getViewZNode(), 4.5, 1.2, 2.5);
// FXAA runs in sRGB: disable the automatic output transform, then convert the DOF result before FXAA
renderPipeline.outputColorTransform = false;
renderPipeline.outputNode = fxaa(renderOutput(dofNode));
// After the first frame, a changed outputNode or outputColorTransform only takes effect with:
renderPipeline.needsUpdate = true;
ao() には、別途用意したシングルサンプルのプリパスから得られる深度とビュー空間法線が必要です。AO は赤チャンネルに格納されています。
import * as THREE from "three/webgpu";
import { pass, mrt, normalView, screenUV, builtinAOContext } from "three/tsl";
import { ao } from "three/addons/tsl/display/GTAONode.js";
const renderPipeline = new THREE.RenderPipeline(renderer);
// Pre-pass with view-space normals in the output MRT attachment
const prePass = pass(scene, camera, { samples: 0 }); // ao() cannot read a multisampled depth texture
prePass.setMRT(mrt({ output: normalView }));
const aoPass = ao(prePass.getTextureNode("depth"), prePass.getTextureNode(), camera);
// Apply the AO to a normal scene pass
const scenePass = pass(scene, camera);
scenePass.contextNode = builtinAOContext(aoPass.getTextureNode().sample(screenUV).r);
renderPipeline.outputNode = scenePass;
カスタムエフェクトは GLSL の ShaderPass ではなく、シーンテクスチャ上のプレーンな TSL です。これは Custom Passes にある波のエフェクトで、time ノードでアニメーションします。
import * as THREE from "three/webgpu";
import { pass, screenUV, sin, time, vec2 } from "three/tsl";
const renderPipeline = new THREE.RenderPipeline(renderer);
const scenePassColor = pass(scene, camera).getTextureNode("output");
const offset = vec2(sin(screenUV.y.mul(10).add(time)).mul(0.01), 0);
renderPipeline.outputNode = scenePassColor.sample(screenUV.add(offset));
選択的ブルームでは、ここでレイヤーを使う必要はありません。three/tsl から emissive をパスの MRT に追加し、scenePass.setMRT(mrt({ output, emissive })) を設定して、そのアタッチメントだけにブルームを適用します。renderPipeline.outputNode = scenePassColor.add(bloom(scenePass.getTextureNode("emissive")))。
よくある間違い
three/examples/jsm/postprocessing/EffectComposer.js→three/addons/postprocessing/EffectComposer.js:examples/jsm の指定子はインポートマップで解決されません。composer.addPass(new ShaderPass(GammaCorrectionShader))→composer.addPass(new OutputPass()):GammaCorrectionShader は sRGB 伝達関数のみを適用しますが、OutputPass はレンダラーのトーンマッピング、露出、出力色空間を適用し、すべてのパスチェーンの最後に置きます。FXAAPass など、sRGB 入力を必要とするパスだけをその後に続けます。composer.addPass(bloomPass); composer.render();→composer.addPass(bloomPass); composer.addPass(new OutputPass()); composer.render();:コンポーザーのバッファーはリニアなので、OutputPass がないとトーンマッピングと sRGB 変換が省略されるか、一貫しない形で適用されます。composer.addPass(fxaaPass); composer.addPass(outputPass);→composer.addPass(outputPass); composer.addPass(fxaaPass);:FXAA は sRGB 入力を必要とするため OutputPass の後に置きます。一方、SMAA はリニア空間で動作するため、その前に実行する必要があります。new ShaderPass(FXAAShader)→new FXAAPass():FXAAPass はコンポーザーのサイズ(ピクセル比を含む)から独自に解像度ユニフォームを設定します。手動で設定した値は HiDPI 画面では正しくありません。new FilmPass(0.35, 0.5, 648, false)→new FilmPass(0.35, false):シグネチャは(intensity、grayscale)です。走査線の引数はありません。new HalftonePass(width, height, params)→new HalftonePass(params):r175 で width と height の引数が削除されました。オプションは単一の params オブジェクトに指定します。new SMAAPass(width, height)→new SMAAPass():r175 でサイズ引数が削除されました。パスのサイズはコンポーザーが設定します。new UnrealBloomPass(new THREE.Vector2(window.innerWidth / 2, window.innerHeight / 2), 1.5, 0.4, 0.85)→new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85):addPass はパスをコンポーザーのサイズにただちに変更するため、半解像度にする方法は元に戻されます。bloomPass.resolution.set(width, height)→composer.setSize(width, height):UnrealBloomPass が解像度を読み取るのはコンストラクター内だけです。コンポーザーがパスとそのミップをリサイズします。bloomPass.renderToScreen = false→bloomComposer.renderToScreen = false:コンポーザーはフレームごとに、有効なすべてのパスの renderToScreen を再設定します。そのため、結果を画面に出力しないようにするにはコンポーザーレベルのスイッチを使います。requestAnimationFrame(animate)→renderer.setAnimationLoop(animate):両レンダラーのドキュメントでは手動ループよりこちらを推奨しており、WebXR セッションが開始するとフレームをそのセッションに渡します。clock.getElapsedTime()→timer.getElapsed():Clock は r183 で非推奨になりました。THREE.Timer を作成し、timer.connect(document) を一度呼び出してから、毎フレーム値を読み取る前に timer.update() を呼び出してください。composer.render(); renderer.render(scene, camera);→bloomComposer.render(); finalComposer.render();:2回目の renderer.render はキャンバスをクリアし、コンポーザーの出力を消去します。選択的なブルームでは、2つ目のコンポーザーを通じてブルームテクスチャを合成します。const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setEffects([bloomPass]);→const renderer = new THREE.WebGLRenderer({ antialias: true, outputBufferType: THREE.HalfFloatType }); renderer.setEffects([bloomPass]);:setEffects() には HalfFloatType または FloatType の出力バッファーが必要です。デフォルトではエラーがログに出力され、エフェクトは適用されません。renderer.setEffects([bloomPass, new OutputPass(), new FXAAPass()])→renderer.setEffects([bloomPass]):組み込みの処理経路ではエフェクトの後にトーンマッピングと色空間変換を一度適用します。そのため、リスト内に OutputPass を含めると二重に適用され(警告が表示されます)、OutputPass を含めずに FXAAPass を使うとリニア入力になります。代わりにantialias: trueでアンチエイリアスを適用してください。import { bloom } from "three/tsl"→import { bloom } from "three/addons/tsl/display/BloomNode.js":three/tsl がエクスポートするのは pass、mrt、renderOutput です。エフェクトノードは addons にあります。new THREE.PostProcessing(renderer)→new THREE.RenderPipeline(renderer):r183 で PostProcessing から名前が変更されました。古い名前は非推奨のエイリアスとして残っていますが、警告が表示されます。renderPipeline.outputNode = bloom(scenePassColor)→renderPipeline.outputNode = scenePassColor.add(bloom(scenePassColor)):bloom() が返すのはグローだけなので、単独ではシーンが描画されません。renderPipeline.outputNode = fxaa(scenePassColor)→renderPipeline.outputColorTransform = false; renderPipeline.outputNode = fxaa(renderOutput(scenePass));:FXAA は sRGB 入力を必要としますが、デフォルトの outputColorTransform は FXAA の後にトーンマッピングと色空間変換を適用します。renderPipeline.outputNode = scenePassColor.add(dof(scenePassColor, scenePass.getViewZNode()))→renderPipeline.outputNode = dof(scenePassColor, scenePass.getViewZNode()):dof が完成した画像を返すため、シーンの色を加えるとシーンが二重に描画されます。await renderPipeline.renderAsync()→renderPipeline.render():renderAsync は r181 で非推奨になりました。セットアップ時に一度await renderer.init()を呼び出し、その後は同期的にレンダリングしてください。aoPass.distanceFallOff.value = 2→aoPass.radius.value = 0.2:distanceExponent と distanceFallOff は r186 で非推奨になり、AO に影響しなくなりました。そのため、代わりに radius と scale を調整してください。new EffectComposer(webgpuRenderer)→new THREE.RenderPipeline(webgpuRenderer):EffectComposer とそのパスは WebGLRenderer でのみ動作します。WebGPURenderer のエフェクトは RenderPipeline 上の TSL ノードです。
関連項目
threejs-fundamentals— レンダラーの作成、サイズ設定、色空間、アニメーションループ。threejs-shaders— カスタムエフェクト向けの GLSL ShaderMaterial の作成と、TSL ノード言語。threejs-textures— レンダーターゲットと、ポストプロセス処理した画像をテクスチャとして使用する方法。