SuperAPI AI Directory

threejs-core-renderer

General & other

View repository

Updated 10-16 06:18

About this skill

threejs-core-renderer

Quick Reference

Standard Initialization Pattern

import {
  WebGLRenderer, SRGBColorSpace, ACESFilmicToneMapping,
  PCFSoftShadowMap, PerspectiveCamera, Scene
} from 'three';

const renderer = new WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.outputColorSpace = SRGBColorSpace; renderer.toneMapping = ACESFilmicToneMapping; renderer.toneMappingExposure = 1.0; renderer.shadowMap.enabled = true; renderer.shadowMap.type = PCFSoftShadowMap; document.body.appendChild(renderer.domElement);

const scene = new Scene(); const camera = new PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);

renderer.setAnimationLoop((time) => { renderer.render(scene, camera); });


### Constructor Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `canvas` | `HTMLCanvasElement` | new canvas | Existing canvas element |
| `context` | `WebGLRenderingContext` | new context | Existing WebGL context |
| `precision` | `'highp' \| 'mediump' \| 'lowp'` | `'highp'` | Shader precision |
| `alpha` | `boolean` | `false` | Transparent canvas background |
| `premultipliedAlpha` | `boolean` | `true` | Premultiplied alpha blending |
| `antialias` | `boolean` | `false` | MSAA anti-aliasing |
| `stencil` | `boolean` | `true` | Stencil buffer |
| `preserveDrawingBuffer` | `boolean` | `false` | Required for screenshots |
| `powerPreference` | `'high-performance' \| 'low-power' \| 'default'` | `'default'` | GPU selection hint |
| `failIfMajorPerformanceCaveat` | `boolean` | `false` | Fail if software renderer |
| `depth` | `boolean` | `true` | Depth buffer |
| `logarithmicDepthBuffer` | `boolean` | `false` | Fix Z-fighting for large scenes |

**NEVER** change `antialias` after construction -- it CANNOT be modified post-creation.

### Key Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `domElement` | `HTMLCanvasElement` | -- | The canvas. ALWAYS append to DOM |
| `shadowMap.enabled` | `boolean` | `false` | MUST set `true` for any shadows |
| `shadowMap.type` | `ShadowMapType` | `PCFShadowMap` | Shadow filtering algorithm |
| `toneMapping` | `ToneMapping` | `NoToneMapping` | HDR tone mapping algorithm |
| `toneMappingExposure` | `number` | `1` | Exposure for tone mapping |
| `outputColorSpace` | `string` | `SRGBColorSpace` | Output color space |
| `autoClear` | `boolean` | `true` | Clear before each render call |
| `sortObjects` | `boolean` | `true` | Automatic draw order sorting |
| `clippingPlanes` | `Plane[]` | `[]` | Global clipping planes |
| `localClippingEnabled` | `boolean` | `false` | Enable per-material clipping |
| `info` | `object` | -- | Render stats (calls, triangles, memory) |

### Critical Warnings

**NEVER** forget to cap pixel ratio -- uncapped `devicePixelRatio` on 3x+ screens causes 9x+ pixel load. ALWAYS use `Math.min(window.devicePixelRatio, 2)`.

**NEVER** change `shadowMap.type` after the first render -- it forces full shader recompilation. ALWAYS set it once at initialization.

**NEVER** use `preserveDrawingBuffer: true` in production render loops -- it disables buffer-swap optimizations. ONLY enable it when screenshots are needed.

**NEVER** set `near: 0` on cameras -- it causes Z-fighting everywhere. ALWAYS keep `far / near` ratio under 10,000.

**NEVER** forget `camera.updateProjectionMatrix()` after modifying `fov`, `aspect`, `near`, `far`, or `zoom` -- the projection matrix is NOT auto-updated.

**NEVER** use raw `requestAnimationFrame` in Three.js r160+ -- ALWAYS use `renderer.setAnimationLoop()` which handles WebXR sessions automatically.

**NEVER** skip `renderer.dispose()` on cleanup -- it leaks WebGL contexts, programs, textures, and framebuffers.

---

## Color Management

Three.js r160+ uses a linear workflow with automatic sRGB conversion on output.

### Color Space Rules

| Texture Type | colorSpace | Examples |
|-------------|-----------|----------|
| Color / albedo | `SRGBColorSpace` | Diffuse maps, emissive maps |
| Data | `LinearSRGBColorSpace` | Normal maps, roughness, metalness, AO, displacement |

**Rule:** Color textures are ALWAYS `SRGBColorSpace`. Data textures are ALWAYS `LinearSRGBColorSpace`. Mixing these up produces washed-out or over-saturated renders.

### Tone Mapping Decision Tree

| Constant | When to Use |
|----------|------------|
| `NoToneMapping` | Non-photorealistic rendering, UI overlays, unlit scenes |
| `LinearToneMapping` | Basic HDR clamping with minimal color transformation |
| `ReinhardToneMapping` | General-purpose, preserves color hues well |
| `CineonToneMapping` | Cinematic film stock look |
| `ACESFilmicToneMapping` | **Default choice for PBR.** Industry-standard filmic curve |
| `AgXToneMapping` | Better than ACES for saturated colors. Avoids ACES hue shift on bright blues/reds. (r160+) |
| `NeutralToneMapping` | When color accuracy is paramount, minimal artistic transformation |

**ALWAYS** use `ACESFilmicToneMapping` or `AgXToneMapping` for PBR workflows. `AgXToneMapping` is preferred when saturated colors must remain accurate.

---

## Shadow Map Configuration

```javascript
import { PCFSoftShadowMap } from 'three';

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = PCFSoftShadowMap;
Type Quality Performance Notes
BasicShadowMap Low (hard edges) Fastest No filtering
PCFShadowMap Medium Medium Default. Percentage-Closer Filtering
PCFSoftShadowMap High (soft edges) Slower Bilinear PCF. Most popular choice
VSMShadowMap High (very soft) Slowest Can exhibit light bleeding artifacts

Render Loop and Resize

setAnimationLoop (preferred)

renderer.setAnimationLoop((time) => {
  // time is DOMHighResTimeStamp in milliseconds
  renderer.render(scene, camera);
});

// Stop the loop
renderer.setAnimationLoop(null);

Window Resize Handler

window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

ALWAYS update camera.aspect AND call updateProjectionMatrix() before setSize().


Camera System

PerspectiveCamera

import { PerspectiveCamera } from 'three';

const camera = new PerspectiveCamera(
  50,                                        // fov (vertical, degrees)
  window.innerWidth / window.innerHeight,    // aspect ratio
  0.1,                                       // near
  1000                                       // far
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);

After modifying fov, aspect, near, far, or zoom, you MUST call camera.updateProjectionMatrix().

OrthographicCamera

import { OrthographicCamera } from 'three';

const frustumSize = 10;
const aspect = window.innerWidth / window.innerHeight;
const camera = new OrthographicCamera(
  -frustumSize * aspect / 2,   // left
   frustumSize * aspect / 2,   // right
   frustumSize / 2,            // top
  -frustumSize / 2,            // bottom
  0.1, 1000
);

ALWAYS update all six frustum parameters (left, right, top, bottom, near, far) on resize, then call updateProjectionMatrix().

ArrayCamera

Renders multiple viewports in a single render() call. Each sub-camera has camera.viewport = new Vector4(x, y, width, height). Used for split-screen and VR stereo.

CubeCamera

Renders the scene from all 6 directions into a WebGLCubeRenderTarget. Used for dynamic environment maps. NEVER call cubeCamera.update() every frame for static environments -- it renders the scene 6 times per call.


Render Targets

import { WebGLRenderTarget } from 'three';

const renderTarget = new WebGLRenderTarget(1024, 1024);
renderer.setRenderTarget(renderTarget);
renderer.render(scene, camera);
renderer.setRenderTarget(null); // restore default framebuffer
// renderTarget.texture is now usable as a regular Texture

ALWAYS call renderTarget.dispose() when no longer needed.


Shader Compilation

// Synchronous -- blocks the thread
renderer.compile(scene, camera);

// Asynchronous -- non-blocking, prevents frame drops (r160+)
await renderer.compileAsync(scene, camera);

ALWAYS call compileAsync() after loading assets but before the first visible render to prevent jank from just-in-time shader compilation.


Viewport and Scissor

// Render to a sub-region of the canvas
renderer.setViewport(x, y, width, height);
renderer.setScissor(x, y, width, height);
renderer.setScissorTest(true);
renderer.render(scene, camera);

// Reset to full canvas
renderer.setScissorTest(false);
renderer.setViewport(0, 0, canvas.width, canvas.height);

Clipping Planes

import { Plane, Vector3 } from 'three';

// Global clipping (affects all objects)
renderer.clippingPlanes = [new Plane(new Vector3(0, -1, 0), 1)];

// Per-material clipping (MUST enable localClippingEnabled)
renderer.localClippingEnabled = true;
material.clippingPlanes = [plane];
material.clipIntersection = false; // false = union, true = intersection
material.clipShadows = true;       // also clip shadow geometry

Cleanup and Disposal

renderer.setAnimationLoop(null);
renderer.dispose();
renderer.domElement.remove();

ALWAYS call dispose() when removing a renderer. This releases the WebGL context, all compiled shader programs, textures, and framebuffers.


WebGPU Renderer

Three.js includes an experimental WebGPURenderer with TSL (Three Shading Language) node-based materials. For WebGPU-specific guidance, see the threejs-impl-webgpu skill.


Reference Links

Official Sources