Skip to content

LightProbeGrid: Add toJSON / fromJSON serialization - #33658

Open
mrdoob wants to merge 1 commit into
devfrom
lightprobegrid-serialization
Open

LightProbeGrid: Add toJSON / fromJSON serialization#33658
mrdoob wants to merge 1 commit into
devfrom
lightprobegrid-serialization

Conversation

@mrdoob

@mrdoob mrdoob commented May 27, 2026

Copy link
Copy Markdown
Owner

Related issue: #33125

Description

Adds toJSON() / static fromJSON() to LightProbeGrid so a baked grid can be saved to a JSON file and reconstructed without a renderer or a re-bake.

Coefficients are stored as fixed-point integers (value * 4096, rounded), which gzips well and stays human-readable.

webgl_lightprobes_sponza

The Sponza example has new light settings and gains Load bake / Save bake buttons.

image

https://raw.githack.com/mrdoob/three.js/34309a2353ef3d667939d37baf74e60e5a7e2109/examples/webgl_lightprobes_sponza.html

webgl_lightprobes_sponza_baked

I've also added a new example that loads a pre-baked 32×16×16 grid (~504 KB VRAM) from examples/probes/sponza-lightprobes-bake.json:

Screen.Recording.2026-05-27.at.15.33.27.mov

https://raw.githack.com/mrdoob/three.js/34309a2353ef3d667939d37baf74e60e5a7e2109/examples/webgl_lightprobes_sponza_baked.html

Adds toJSON() / static fromJSON() so a baked grid can be saved to a JSON
file and reconstructed (texture and all) without a renderer or a re-bake.
Coefficients are stored as fixed-point integers (value * 4096, rounded),
which gzips well and stays human-readable — Sponza's 10x7x7 grid is
~17 KB raw, ~6.7 KB gzipped.

The Sponza example gains Save / Load buttons, and a new
webgl_lightprobes_sponza_baked example loads a pre-baked grid from
examples/probes/sponza-lightprobes-bake.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mrdoob mrdoob added this to the r185 milestone May 27, 2026
@mrdoob
mrdoob requested a review from Mugen87 May 27, 2026 06:41
sun.setFromSphericalCoords( 1, phi, theta );
sky.material.uniforms[ 'sunPosition' ].value.copy( sun );

const probes = LightProbeGrid.fromJSON( probesJSON );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON is currently saved in a standalone file.

Are you planning to integrate this in the JSON Scene/Object Format? Since probe grids are part of the scene graph, one might expect scene.toJSON() also serializes LightProbeGrid. Granted the serialized probe grid JSON isn't small but we also embed texture images as Base64 into the Scene/Object JSON.

@mrdoob mrdoob May 27, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think eventually it will make sense to have this as part of scene graph serialization, but I'm not 100% sure about the data format and didn't want to commit to core yet.

{

const batchPixels = new Float32Array( 9 * totalProbes * 4 );
renderer.readRenderTargetPixels( batchTarget, 0, 0, 9, totalProbes, batchPixels );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this new code, bake() now always performs the readback even if the app never serializes the grid.

Would it be possible to defer this operation and only do this in toJSON()?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I thought it was related to the bounce code added in #33657... I'll investigate.


const texture = new Data3DTexture( atlas, nx, ny, atlasDepth );
texture.format = RGBAFormat;
texture.type = HalfFloatType;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be FloatType instead?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to store the data in a compact way. We tried a few options and Float16 values scaled by 4096 seemed to be the most compact for JSON without really losing quality.

Feel free to do a pass on it and see if FloatType works too. You'll need to compare the file size of the resulting JSON.


if ( this.coefficients === null ) {

throw new Error( 'THREE.LightProbeGrid: toJSON() requires a baked grid. Call bake() first.' );

@Mugen87 Mugen87 May 27, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you have already documented the requirements for using toJSON(), do we still need this additional if statement?

I'm okay with keeping it, it's just that usually we don't validate user input like that.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah. Claude was probably overly cautious here.

@mrdoob mrdoob modified the milestones: r185, r186 Jun 24, 2026
@Mugen87

Mugen87 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

@mrdoob If you don't find the time to finish this PR, are you okay if I continue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants