Skip to content

Commit

Permalink
Node: Add Lut3DNode. (#1089)
Browse files Browse the repository at this point in the history
* Node: Add Lut3DNode.

* Update three.js

* Add examples

* Update

* Updates

* Updates

* Update patch and delete examples
  • Loading branch information
Methuselah96 authored Jul 21, 2024
1 parent 7b963c6 commit 33e7d0e
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 2 deletions.
120 changes: 120 additions & 0 deletions examples-testing/changes.patch
Original file line number Diff line number Diff line change
Expand Up @@ -14367,6 +14367,126 @@ index d83642f2..96b1bc0e 100644

init();

diff --git a/examples-testing/examples/webgpu_postprocessing_3dlut.ts b/examples-testing/examples/webgpu_postprocessing_3dlut.ts
index 85346b5d..e4d5bb15 100644
--- a/examples-testing/examples/webgpu_postprocessing_3dlut.ts
+++ b/examples-testing/examples/webgpu_postprocessing_3dlut.ts
@@ -1,20 +1,30 @@
-import * as THREE from 'three';
-import { pass, texture3D, uniform } from 'three/tsl';
+import * as THREE from 'three/webgpu';
+import { Lut3DNode, ShaderNodeObject, pass, texture3D, uniform } from 'three/tsl';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
-import { LUTCubeLoader } from 'three/addons/loaders/LUTCubeLoader.js';
-import { LUT3dlLoader } from 'three/addons/loaders/LUT3dlLoader.js';
-import { LUTImageLoader } from 'three/addons/loaders/LUTImageLoader.js';
+import { LUTCubeLoader, LUTCubeResult } from 'three/addons/loaders/LUTCubeLoader.js';
+import { LUT3dlLoader, LUT3dlResult } from 'three/addons/loaders/LUT3dlLoader.js';
+import { LUTImageLoader, LUTImageResult } from 'three/addons/loaders/LUTImageLoader.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

-const params = {
+const params: { lut: keyof typeof lutMap; intensity: number } = {
lut: 'Bourbon 64.CUBE',
intensity: 1,
};

-const lutMap = {
+const lutMap: {
+ 'Bourbon 64.CUBE': LUTCubeResult | Promise<LUTCubeResult> | null;
+ 'Chemical 168.CUBE': LUTCubeResult | Promise<LUTCubeResult> | null;
+ 'Clayton 33.CUBE': LUTCubeResult | Promise<LUTCubeResult> | null;
+ 'Cubicle 99.CUBE': LUTCubeResult | Promise<LUTCubeResult> | null;
+ 'Remy 24.CUBE': LUTCubeResult | Promise<LUTCubeResult> | null;
+ 'Presetpro-Cinematic.3dl': LUT3dlResult | Promise<LUT3dlResult> | null;
+ NeutralLUT: LUTImageResult | Promise<LUTImageResult> | null;
+ 'B&WLUT': LUTImageResult | Promise<LUTImageResult> | null;
+ NightLUT: LUTImageResult | Promise<LUTImageResult> | null;
+} = {
'Bourbon 64.CUBE': null,
'Chemical 168.CUBE': null,
'Clayton 33.CUBE': null,
@@ -26,9 +36,9 @@ const lutMap = {
NightLUT: null,
};

-let gui;
-let camera, scene, renderer;
-let postProcessing, lutPass;
+let gui: GUI;
+let camera: THREE.PerspectiveCamera, scene: THREE.Scene, renderer: THREE.WebGPURenderer;
+let postProcessing: THREE.PostProcessing, lutPass: ShaderNodeObject<Lut3DNode>;

init();

@@ -61,11 +71,13 @@ async function init() {

for (const name in lutMap) {
if (/\.CUBE$/i.test(name)) {
- lutMap[name] = lutCubeLoader.loadAsync('luts/' + name);
+ (lutMap as unknown as Record<string, Promise<LUTCubeResult>>)[name] = lutCubeLoader.loadAsync(
+ 'luts/' + name,
+ );
} else if (/\LUT$/i.test(name)) {
- lutMap[name] = lutImageLoader.loadAsync(`luts/${name}.png`);
+ (lutMap as Record<string, Promise<LUTImageResult>>)[name] = lutImageLoader.loadAsync(`luts/${name}.png`);
} else {
- lutMap[name] = lut3dlLoader.loadAsync('luts/' + name);
+ (lutMap as Record<string, Promise<LUT3dlResult>>)[name] = lut3dlLoader.loadAsync('luts/' + name);
}
}

@@ -73,7 +85,9 @@ async function init() {
await Promise.all(pendings);

for (const name in lutMap) {
- lutMap[name] = await lutMap[name];
+ (lutMap as Record<string, LUTCubeResult | LUT3dlResult | LUTImageResult>)[name] = await (
+ lutMap as Record<string, Promise<LUTCubeResult | LUT3dlResult | LUTImageResult>>
+ )[name];
}

renderer = new THREE.WebGPURenderer();
@@ -93,9 +107,8 @@ async function init() {

const outputPass = scenePassColor.toneMapping(THREE.ACESFilmicToneMapping).linearTosRGB();

- lutPass = outputPass.lut3D();
- lutPass.lutNode = texture3D(lutMap[params.lut]);
- lutPass.intensityNode = uniform(1);
+ const lut = lutMap[params.lut] as LUTCubeResult | LUT3dlResult | LUTImageResult;
+ lutPass = outputPass.lut3D(texture3D(lut.texture3D), lut.texture3D.image.width, uniform(1));

postProcessing.outputNode = lutPass;

@@ -108,8 +121,7 @@ async function init() {
controls.update();

gui = new GUI();
- gui.width = 350;
- gui.add(params, 'lut', Object.keys(lutMap));
+ gui.add(params, 'lut', Object.keys(lutMap) as (keyof typeof lutMap)[]);
gui.add(params, 'intensity').min(0).max(1);

window.addEventListener('resize', onWindowResize);
@@ -125,11 +137,11 @@ function onWindowResize() {
//

function animate() {
- lutPass.intensityNode.value = params.intensity;
+ lutPass.intensityNode!.value = params.intensity;

if (lutMap[params.lut]) {
- const lut = lutMap[params.lut];
- lutPass.lutNode.value = lut.texture3D;
+ const lut = lutMap[params.lut] as LUTCubeResult | LUT3dlResult | LUTImageResult;
+ lutPass.lutNode!.value = lut.texture3D;
lutPass.size.value = lut.texture3D.image.width;
}

diff --git a/examples-testing/examples/webgpu_postprocessing_afterimage.ts b/examples-testing/examples/webgpu_postprocessing_afterimage.ts
index 4c3a1d66..4337b2c8 100644
--- a/examples-testing/examples/webgpu_postprocessing_afterimage.ts
Expand Down
18 changes: 18 additions & 0 deletions types/three/examples/jsm/loaders/LUTImageLoader.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Data3DTexture, Loader, Texture } from "three";

export interface LUTImageResult {
size: number;
texture3D: Data3DTexture;
}

export class LUTImageLoader extends Loader<LUTImageResult> {
flip: boolean;

constructor(flipVertical?: boolean);

getImageData(texture: Texture): ImageData;

horz2Vert(texture: Texture): ImageData;

parse(dataArray: Uint8ClampedArray, size: number): LUTImageResult;
}
1 change: 1 addition & 0 deletions types/three/src/nodes/Nodes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ export { default as DotScreenNode, dotScreen } from "./display/DotScreenNode.js"
export { default as FilmNode, film } from "./display/FilmNode.js";
export { default as FrontFacingNode, faceDirection, frontFacing } from "./display/FrontFacingNode.js";
export { default as GaussianBlurNode, gaussianBlur } from "./display/GaussianBlurNode.js";
export { default as Lut3DNode, lut3D } from "./display/Lut3DNode.js";
export { default as NormalMapNode, normalMap } from "./display/NormalMapNode.js";
export { default as PosterizeNode, posterize } from "./display/PosterizeNode.js";
export { default as RGBShiftNode, rgbShift } from "./display/RGBShiftNode.js";
Expand Down
2 changes: 1 addition & 1 deletion types/three/src/nodes/accessors/Texture3DNode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default class Texture3DNode extends TextureNode {
}

export const texture3D: (
value: CubeTexture,
value: Texture,
uvNode?: NodeRepresentation,
levelNode?: NodeRepresentation,
) => ShaderNodeObject<Texture3DNode>;
30 changes: 30 additions & 0 deletions types/three/src/nodes/display/Lut3DNode.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Data3DTexture } from "../../textures/Data3DTexture.js";
import Texture3DNode from "../accessors/Texture3DNode.js";
import Node from "../core/Node.js";
import TempNode from "../core/TempNode.js";
import UniformNode from "../core/UniformNode.js";
import { NodeRepresentation, ShaderNodeObject } from "../shadernode/ShaderNode.js";

declare class Lut3DNode extends TempNode {
inputNode: Node;
lutNode: Texture3DNode;
size: ShaderNodeObject<UniformNode<number>>;
intensityNode: UniformNode<number>;

constructor(inputNode: Node, lutNode: UniformNode<Data3DTexture>, size: number, intensityNode: UniformNode<number>);
}

export const lut3D: (
node: NodeRepresentation,
lut: NodeRepresentation,
size: number,
intensity: NodeRepresentation,
) => ShaderNodeObject<Lut3DNode>;

declare module "../shadernode/ShaderNode.js" {
interface NodeElements {
lut3D: typeof lut3D;
}
}

export default Lut3DNode;

0 comments on commit 33e7d0e

Please sign in to comment.