LightProbeGridHelper.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {
  2. InstancedBufferAttribute,
  3. InstancedMesh,
  4. Matrix4,
  5. NodeMaterial,
  6. SphereGeometry,
  7. Vector3
  8. } from 'three/webgpu';
  9. import { array, attribute, Fn, getShIrradianceAt, normalWorld, texture3D, uniform, vec3, vec4 } from 'three/tsl';
  10. /**
  11. * Visualizes a {@link LightProbeGrid} by rendering a sphere at each probe
  12. * position, shaded with the probe's L2 spherical harmonics. Uses a single
  13. * `InstancedMesh` draw call for all probes.
  14. *
  15. * This helper can only be used with {@link WebGPURenderer}.
  16. * When using {@link WebGLRenderer}, import from `LightProbeGridHelperWebGL.js`.
  17. *
  18. * ```js
  19. * const helper = new LightProbeGridHelper( probes );
  20. * scene.add( helper );
  21. * ```
  22. *
  23. * @private
  24. * @augments InstancedMesh
  25. * @three_import import { LightProbeGridHelper } from 'three/addons/helpers/LightProbeGridHelper.js';
  26. */
  27. class LightProbeGridHelper extends InstancedMesh {
  28. /**
  29. * Constructs a new irradiance probe grid helper.
  30. *
  31. * @param {LightProbeGrid} probes - The probe grid to visualize.
  32. * @param {number} [sphereSize=0.12] - The radius of each probe sphere.
  33. */
  34. constructor( probes, sphereSize = 0.12 ) {
  35. const geometry = new SphereGeometry( sphereSize, 16, 16 );
  36. const material = new NodeMaterial();
  37. const res = probes.resolution;
  38. const count = res.x * res.y * res.z;
  39. super( geometry, material, count );
  40. /**
  41. * The probe grid to visualize.
  42. *
  43. * @type {LightProbeGrid}
  44. */
  45. this.probes = probes;
  46. this.type = 'LightProbeGridHelper';
  47. // Atlas and resolution are swappable uniforms, so the shading node builds once.
  48. this._atlas = texture3D( probes.texture );
  49. this._resolution = uniform( new Vector3() );
  50. const nz = this._resolution.z;
  51. const paddedSlices = nz.add( 2.0 );
  52. const atlasDepth = paddedSlices.mul( 7.0 );
  53. material.fragmentNode = Fn( () => {
  54. const uvw = attribute( 'instanceUVW', 'vec3' );
  55. const uvZBase = uvw.z.mul( nz ).add( 1.0 );
  56. const slice = ( t ) => this._atlas.sample( vec3( uvw.xy, uvZBase.add( paddedSlices.mul( t ) ).div( atlasDepth ) ) );
  57. const s0 = slice( 0 ), s1 = slice( 1 ), s2 = slice( 2 ), s3 = slice( 3 );
  58. const s4 = slice( 4 ), s5 = slice( 5 ), s6 = slice( 6 );
  59. const sh = array( [
  60. s0.xyz,
  61. vec3( s0.w, s1.xy ),
  62. vec3( s1.zw, s2.x ),
  63. s2.yzw,
  64. s3.xyz,
  65. vec3( s3.w, s4.xy ),
  66. vec3( s4.zw, s5.x ),
  67. s5.yzw,
  68. s6.xyz
  69. ] );
  70. return vec4( getShIrradianceAt( normalWorld, sh ).max( vec3( 0.0 ) ), 1.0 );
  71. } )();
  72. this.update();
  73. }
  74. /**
  75. * Rebuilds instance matrices and UVW attributes from the current probe grid,
  76. * and rebinds the shading node to its atlas. Call this after changing
  77. * `probes` or after re-baking.
  78. */
  79. update() {
  80. const probes = this.probes;
  81. const res = probes.resolution;
  82. const count = res.x * res.y * res.z;
  83. // Resize instance matrix buffer if needed.
  84. if ( this.instanceMatrix.count !== count ) {
  85. this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 );
  86. }
  87. this.count = count;
  88. const uvwArray = new Float32Array( count * 3 );
  89. const matrix = new Matrix4();
  90. const probePos = new Vector3();
  91. let i = 0;
  92. for ( let iz = 0; iz < res.z; iz ++ ) {
  93. for ( let iy = 0; iy < res.y; iy ++ ) {
  94. for ( let ix = 0; ix < res.x; ix ++ ) {
  95. // Remap to texel centers (must match LightProbeGridNode).
  96. uvwArray[ i * 3 ] = ( ix + 0.5 ) / res.x;
  97. uvwArray[ i * 3 + 1 ] = ( iy + 0.5 ) / res.y;
  98. uvwArray[ i * 3 + 2 ] = ( iz + 0.5 ) / res.z;
  99. probes.getProbePosition( ix, iy, iz, probePos );
  100. matrix.makeTranslation( probePos.x, probePos.y, probePos.z );
  101. this.setMatrixAt( i, matrix );
  102. i ++;
  103. }
  104. }
  105. }
  106. this.instanceMatrix.needsUpdate = true;
  107. this.geometry.setAttribute( 'instanceUVW', new InstancedBufferAttribute( uvwArray, 3 ) );
  108. this._atlas.value = probes.texture;
  109. this._resolution.value.copy( res );
  110. }
  111. /**
  112. * Frees the GPU-related resources allocated by this instance. Call this
  113. * method whenever this instance is no longer used in your app.
  114. */
  115. dispose() {
  116. this.geometry.dispose();
  117. this.material.dispose();
  118. }
  119. }
  120. export { LightProbeGridHelper };
粤ICP备19079148号