NgtpLUT
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-lut', template: ` <ngt-canvas [camera]="{ position: [0, 0, 4], fov: 50 }"> <app-postprocessing-wrapper *canvasContent> <app-scene-graph /> </app-postprocessing-wrapper> </ngt-canvas> `, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'lut-demo relative block h-full' }, imports: [NgtCanvas, PostprocessingWrapper, SceneGraph],})export default class LUT { static clientProviders = [provideNgtRenderer()];}import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, viewChild } from '@angular/core';import { beforeRender, loaderResource, NgtArgs } from 'angular-three';import { NgtpEffectComposer, NgtpLUT } from 'angular-three-postprocessing';import * as THREE from 'three';import { LUTCubeLoader } from 'three-stdlib';
import cubicleCube from '@common-assets/cubicle-99.CUBE' with { loader: 'file' };
@Component({ selector: 'app-scene-graph', template: ` <!-- Colorful scene to demonstrate LUT color grading --> <ngt-mesh #mesh1 [position]="[-1.2, 0.5, 0]"> <ngt-sphere-geometry *args="[0.5, 32, 32]" /> <ngt-mesh-standard-material color="#ff6b6b" [metalness]="0.3" [roughness]="0.4" /> </ngt-mesh>
<ngt-mesh #mesh2 [position]="[0, 0, 0]"> <ngt-torus-knot-geometry *args="[0.4, 0.15, 100, 16]" /> <ngt-mesh-standard-material color="#4ecdc4" [metalness]="0.4" [roughness]="0.3" /> </ngt-mesh>
<ngt-mesh #mesh3 [position]="[1.2, -0.3, 0]"> <ngt-dodecahedron-geometry *args="[0.5, 0]" /> <ngt-mesh-standard-material color="#ffe66d" [metalness]="0.3" [roughness]="0.4" /> </ngt-mesh>
<!-- Ground plane --> <ngt-mesh [rotation]="[-Math.PI / 2, 0, 0]" [position]="[0, -1.2, 0]"> <ngt-plane-geometry *args="[8, 8]" /> <ngt-mesh-standard-material color="#e0e0e0" [metalness]="0.1" [roughness]="0.8" /> </ngt-mesh>
<ngtp-effect-composer> @if (lutResult.value(); as lut) { <ngtp-lut [options]="{ lut, tetrahedralInterpolation: true }" /> } </ngtp-effect-composer> `, schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgtArgs, NgtpEffectComposer, NgtpLUT],})export class SceneGraph { protected readonly Math = Math;
// Load the LUT cube file protected lutResult = loaderResource( () => LUTCubeLoader, () => cubicleCube, );
private mesh1Ref = viewChild.required<ElementRef<THREE.Mesh>>('mesh1'); private mesh2Ref = viewChild.required<ElementRef<THREE.Mesh>>('mesh2'); private mesh3Ref = viewChild.required<ElementRef<THREE.Mesh>>('mesh3');
constructor() { beforeRender(({ clock }) => { const t = clock.getElapsedTime(); const mesh1 = this.mesh1Ref().nativeElement; const mesh2 = this.mesh2Ref().nativeElement; const mesh3 = this.mesh3Ref().nativeElement;
mesh1.rotation.y = t * 0.3; mesh2.rotation.x = t * 0.4; mesh2.rotation.y = t * 0.5; mesh3.rotation.x = t * 0.3; mesh3.rotation.y = t * 0.4; }); }}The LUT effect applies a 3D Look-Up Table for professional color grading, allowing you to apply complex color transformations used in film and photography.
import { NgtpEffectComposer, NgtpLUT } from 'angular-three-postprocessing';import { textureResource } from 'angular-three-soba/loaders';
@Component({ template: ` @if (lut(); as lut) { <ngtp-effect-composer> <ngtp-lut [options]="{ lut }" /> </ngtp-effect-composer> } `, imports: [NgtpEffectComposer, NgtpLUT],})export class SceneGraph { lut = textureResource(() => '/path/to/lut.png');}Options
Section titled “Options”| Property | Type | Default | Description |
|---|---|---|---|
lut | Texture | required | The LUT texture |
blendFunction | BlendFunction | BlendFunction.SRC | How the effect blends |
tetrahedralInterpolation | boolean | false | Use tetrahedral interpolation for quality |
Loading CUBE Files
Section titled “Loading CUBE Files”For .cube LUT files, you can use a custom loader:
import { LUTCubeLoader } from 'postprocessing';import { loaderResource } from 'angular-three';
@Component({ template: ` @if (lutResult(); as result) { <ngtp-effect-composer> <ngtp-lut [options]="{ lut: result.texture3D }" /> </ngtp-effect-composer> } `, imports: [NgtpEffectComposer, NgtpLUT],})export class SceneGraph { lutResult = loaderResource(LUTCubeLoader, () => '/path/to/color-grade.cube');}Example: Cinematic Color Grade
Section titled “Example: Cinematic Color Grade”@Component({ template: ` <ngt-mesh> <ngt-sphere-geometry *args="[1, 32, 32]" /> <ngt-mesh-standard-material color="white" /> </ngt-mesh>
@if (lut(); as lut) { <ngtp-effect-composer> <ngtp-lut [options]="{ lut, tetrahedralInterpolation: true }" /> </ngtp-effect-composer> } `, imports: [NgtpEffectComposer, NgtpLUT, NgtArgs], schemas: [CUSTOM_ELEMENTS_SCHEMA],})export class SceneGraph { lut = textureResource(() => '/luts/cinematic.png');}- LUT textures are typically 256x16 or 512x512 PNG images
.cubefiles are industry-standard and provide better quality- Enable
tetrahedralInterpolationfor smoother color transitions - Many free LUTs are available online for various looks (cinematic, vintage, etc.)
- LUTs can dramatically change the mood of your scene with minimal performance cost
- Combine with other effects like vignette and bloom for complete color grading