NgtpToneMapping
import { ChangeDetectionStrategy, Component } from '@angular/core';import { PostprocessingWrapper } from '@postprocessing/wrapper.ts';import { NgtCanvas, provideNgtRenderer } from 'angular-three/dom';import { SceneGraph } from './scene-graph';
@Component({ selector: 'app-tone-mapping', template: ` <ngt-canvas [camera]="{ position: [0, 0, 5], fov: 50 }"> <app-postprocessing-wrapper *canvasContent [lights]="false" background="#111"> <app-scene-graph /> </app-postprocessing-wrapper> </ngt-canvas> `, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'tone-mapping-demo relative block h-full' }, imports: [NgtCanvas, PostprocessingWrapper, SceneGraph],})export default class ToneMapping { static clientProviders = [provideNgtRenderer()];}import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, viewChild } from '@angular/core';import { beforeRender, NgtArgs } from 'angular-three';import { NgtpEffectComposer, NgtpToneMapping } from 'angular-three-postprocessing';import { ToneMappingMode } from 'postprocessing';import * as THREE from 'three';
@Component({ selector: 'app-scene-graph', template: ` <!-- High-intensity lights to demonstrate HDR tone mapping --> <ngt-ambient-light [intensity]="0.2" /> <ngt-point-light [position]="[3, 3, 3]" [intensity]="50" color="#ff9500" /> <ngt-point-light [position]="[-3, 2, 2]" [intensity]="30" color="#00a8ff" /> <ngt-spot-light [position]="[0, 5, 0]" [intensity]="40" [angle]="0.5" [penumbra]="0.5" />
<!-- Reflective metallic sphere --> <ngt-mesh #sphere [position]="[0, 0, 0]"> <ngt-sphere-geometry *args="[1, 64, 64]" /> <ngt-mesh-standard-material color="white" [metalness]="1" [roughness]="0.1" /> </ngt-mesh>
<!-- Secondary objects --> <ngt-mesh [position]="[-2, -0.5, -1]"> <ngt-torus-knot-geometry *args="[0.3, 0.1, 100, 16]" /> <ngt-mesh-standard-material color="white" [metalness]="0.8" [roughness]="0.2" /> </ngt-mesh>
<ngt-mesh [position]="[2, 0.5, -1]"> <ngt-dodecahedron-geometry *args="[0.5, 0]" /> <ngt-mesh-standard-material color="white" [metalness]="0.9" [roughness]="0.15" /> </ngt-mesh>
<!-- Ground plane --> <ngt-mesh [rotation]="[-Math.PI / 2, 0, 0]" [position]="[0, -1.5, 0]"> <ngt-plane-geometry *args="[10, 10]" /> <ngt-mesh-standard-material color="#222" [metalness]="0.5" [roughness]="0.5" /> </ngt-mesh>
<ngtp-effect-composer> <!-- ACES Filmic tone mapping for cinematic look --> <ngtp-tone-mapping [options]="{ mode: ToneMappingMode.ACES_FILMIC }" /> </ngtp-effect-composer> `, schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgtArgs, NgtpEffectComposer, NgtpToneMapping],})export class SceneGraph { protected readonly Math = Math; protected readonly ToneMappingMode = ToneMappingMode;
private sphereRef = viewChild.required<ElementRef<THREE.Mesh>>('sphere');
constructor() { beforeRender(({ clock }) => { const t = clock.getElapsedTime(); this.sphereRef().nativeElement.rotation.y = t * 0.3; }); }}The tone mapping effect converts High Dynamic Range (HDR) values to Low Dynamic Range (LDR) for display. This is essential when using HDR rendering or physically-based lighting.
import { NgtpEffectComposer, NgtpToneMapping } from 'angular-three-postprocessing';import { ToneMappingMode } from 'postprocessing';
@Component({ template: ` <ngtp-effect-composer> <ngtp-tone-mapping [options]="{ mode: ToneMappingMode.ACES_FILMIC }" /> </ngtp-effect-composer> `, imports: [NgtpEffectComposer, NgtpToneMapping],})export class SceneGraph { protected ToneMappingMode = ToneMappingMode;}Options
Section titled “Options”| Property | Type | Default | Description |
|---|---|---|---|
mode | ToneMappingMode | ToneMappingMode.ACES_FILMIC | Tone mapping algorithm |
Tone Mapping Modes
Section titled “Tone Mapping Modes”import { ToneMappingMode } from 'postprocessing';
// Available modes:ToneMappingMode.LINEAR; // No tone mappingToneMappingMode.REINHARD; // Simple ReinhardToneMappingMode.REINHARD2; // Modified ReinhardToneMappingMode.REINHARD2_ADAPTIVE; // Adaptive ReinhardToneMappingMode.UNCHARTED2; // Uncharted 2 filmicToneMappingMode.OPTIMIZED_CINEON; // Optimized cineonToneMappingMode.ACES_FILMIC; // ACES filmic (default)ToneMappingMode.AGX; // AgX tone mappingToneMappingMode.NEUTRAL; // Neutral tone mappingExample: Cinematic Look
Section titled “Example: Cinematic Look”import { ToneMappingMode } from 'postprocessing';
@Component({ template: ` <ngt-mesh> <ngt-sphere-geometry *args="[1, 64, 64]" /> <ngt-mesh-standard-material color="white" [metalness]="1" [roughness]="0" /> </ngt-mesh>
<ngtp-effect-composer> <ngtp-tone-mapping [options]="{ mode: ToneMappingMode.ACES_FILMIC }" /> </ngtp-effect-composer> `, imports: [NgtpEffectComposer, NgtpToneMapping, NgtArgs], schemas: [CUSTOM_ELEMENTS_SCHEMA],})export class SceneGraph { protected ToneMappingMode = ToneMappingMode;}Important Note
Section titled “Important Note”When using the effect composer, Three.js’s built-in tone mapping is automatically disabled. Use this effect to re-enable tone mapping within the post-processing pipeline.
ACES_FILMICis the most popular choice for cinematic looksAGXprovides good results for high-contrast scenesREINHARDis computationally cheaper but less sophisticatedLINEARdisables tone mapping (useful for specific artistic effects)- Tone mapping should typically be one of the last effects in the chain
- Essential when using HDR environment maps or high-intensity lights