| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- import {
- InstancedBufferAttribute,
- InstancedMesh,
- Matrix4,
- NodeMaterial,
- SphereGeometry,
- Vector3
- } from 'three/webgpu';
- import { array, attribute, Fn, getShIrradianceAt, normalWorld, texture3D, uniform, vec3, vec4 } from 'three/tsl';
- /**
- * Visualizes a {@link LightProbeGrid} by rendering a sphere at each probe
- * position, shaded with the probe's L2 spherical harmonics. Uses a single
- * `InstancedMesh` draw call for all probes.
- *
- * This helper can only be used with {@link WebGPURenderer}.
- * When using {@link WebGLRenderer}, import from `LightProbeGridHelperWebGL.js`.
- *
- * ```js
- * const helper = new LightProbeGridHelper( probes );
- * scene.add( helper );
- * ```
- *
- * @private
- * @augments InstancedMesh
- * @three_import import { LightProbeGridHelper } from 'three/addons/helpers/LightProbeGridHelper.js';
- */
- class LightProbeGridHelper extends InstancedMesh {
- /**
- * Constructs a new irradiance probe grid helper.
- *
- * @param {LightProbeGrid} probes - The probe grid to visualize.
- * @param {number} [sphereSize=0.12] - The radius of each probe sphere.
- */
- constructor( probes, sphereSize = 0.12 ) {
- const geometry = new SphereGeometry( sphereSize, 16, 16 );
- const material = new NodeMaterial();
- const res = probes.resolution;
- const count = res.x * res.y * res.z;
- super( geometry, material, count );
- /**
- * The probe grid to visualize.
- *
- * @type {LightProbeGrid}
- */
- this.probes = probes;
- this.type = 'LightProbeGridHelper';
- // Atlas and resolution are swappable uniforms, so the shading node builds once.
- this._atlas = texture3D( probes.texture );
- this._resolution = uniform( new Vector3() );
- const nz = this._resolution.z;
- const paddedSlices = nz.add( 2.0 );
- const atlasDepth = paddedSlices.mul( 7.0 );
- material.fragmentNode = Fn( () => {
- const uvw = attribute( 'instanceUVW', 'vec3' );
- const uvZBase = uvw.z.mul( nz ).add( 1.0 );
- const slice = ( t ) => this._atlas.sample( vec3( uvw.xy, uvZBase.add( paddedSlices.mul( t ) ).div( atlasDepth ) ) );
- const s0 = slice( 0 ), s1 = slice( 1 ), s2 = slice( 2 ), s3 = slice( 3 );
- const s4 = slice( 4 ), s5 = slice( 5 ), s6 = slice( 6 );
- const sh = array( [
- s0.xyz,
- vec3( s0.w, s1.xy ),
- vec3( s1.zw, s2.x ),
- s2.yzw,
- s3.xyz,
- vec3( s3.w, s4.xy ),
- vec3( s4.zw, s5.x ),
- s5.yzw,
- s6.xyz
- ] );
- return vec4( getShIrradianceAt( normalWorld, sh ).max( vec3( 0.0 ) ), 1.0 );
- } )();
- this.update();
- }
- /**
- * Rebuilds instance matrices and UVW attributes from the current probe grid,
- * and rebinds the shading node to its atlas. Call this after changing
- * `probes` or after re-baking.
- */
- update() {
- const probes = this.probes;
- const res = probes.resolution;
- const count = res.x * res.y * res.z;
- // Resize instance matrix buffer if needed.
- if ( this.instanceMatrix.count !== count ) {
- this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 );
- }
- this.count = count;
- const uvwArray = new Float32Array( count * 3 );
- const matrix = new Matrix4();
- const probePos = new Vector3();
- let i = 0;
- for ( let iz = 0; iz < res.z; iz ++ ) {
- for ( let iy = 0; iy < res.y; iy ++ ) {
- for ( let ix = 0; ix < res.x; ix ++ ) {
- // Remap to texel centers (must match LightProbeGridNode).
- uvwArray[ i * 3 ] = ( ix + 0.5 ) / res.x;
- uvwArray[ i * 3 + 1 ] = ( iy + 0.5 ) / res.y;
- uvwArray[ i * 3 + 2 ] = ( iz + 0.5 ) / res.z;
- probes.getProbePosition( ix, iy, iz, probePos );
- matrix.makeTranslation( probePos.x, probePos.y, probePos.z );
- this.setMatrixAt( i, matrix );
- i ++;
- }
- }
- }
- this.instanceMatrix.needsUpdate = true;
- this.geometry.setAttribute( 'instanceUVW', new InstancedBufferAttribute( uvwArray, 3 ) );
- this._atlas.value = probes.texture;
- this._resolution.value.copy( res );
- }
- /**
- * Frees the GPU-related resources allocated by this instance. Call this
- * method whenever this instance is no longer used in your app.
- */
- dispose() {
- this.geometry.dispose();
- this.material.dispose();
- }
- }
- export { LightProbeGridHelper };
|