LightProbeGridHelper.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import {
  2. InstancedBufferAttribute,
  3. InstancedMesh,
  4. Matrix4,
  5. ShaderMaterial,
  6. SphereGeometry,
  7. Vector3
  8. } from 'three';
  9. /**
  10. * Visualizes an {@link LightProbeGrid} by rendering a sphere at each
  11. * probe position, shaded with the probe's L1 spherical harmonics.
  12. *
  13. * Uses a single `InstancedMesh` draw call for all probes.
  14. *
  15. * ```js
  16. * const helper = new LightProbeGridHelper( probes );
  17. * scene.add( helper );
  18. * ```
  19. *
  20. * @augments InstancedMesh
  21. * @three_import import { LightProbeGridHelper } from 'three/addons/helpers/LightProbeGridHelper.js';
  22. */
  23. class LightProbeGridHelper extends InstancedMesh {
  24. /**
  25. * Constructs a new irradiance probe grid helper.
  26. *
  27. * @param {LightProbeGrid} probes - The probe grid to visualize.
  28. * @param {number} [sphereSize=0.12] - The radius of each probe sphere.
  29. */
  30. constructor( probes, sphereSize = 0.12 ) {
  31. const geometry = new SphereGeometry( sphereSize, 16, 16 );
  32. const material = new ShaderMaterial( {
  33. uniforms: {
  34. probesSH: { value: null },
  35. probesResolution: { value: new Vector3() },
  36. },
  37. vertexShader: /* glsl */`
  38. attribute vec3 instanceUVW;
  39. varying vec3 vWorldNormal;
  40. varying vec3 vUVW;
  41. void main() {
  42. vUVW = instanceUVW;
  43. vWorldNormal = normalize( mat3( modelMatrix ) * normal );
  44. gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4( position, 1.0 );
  45. }
  46. `,
  47. fragmentShader: /* glsl */`
  48. precision highp sampler3D;
  49. uniform sampler3D probesSH;
  50. uniform vec3 probesResolution;
  51. varying vec3 vWorldNormal;
  52. varying vec3 vUVW;
  53. void main() {
  54. // Atlas UV mapping — must match lightprobes_pars_fragment.glsl.js
  55. float nz = probesResolution.z;
  56. float paddedSlices = nz + 2.0;
  57. float atlasDepth = 7.0 * paddedSlices;
  58. float uvZBase = vUVW.z * nz + 1.0;
  59. vec4 s0 = texture( probesSH, vec3( vUVW.xy, ( uvZBase ) / atlasDepth ) );
  60. vec4 s1 = texture( probesSH, vec3( vUVW.xy, ( uvZBase + paddedSlices ) / atlasDepth ) );
  61. vec4 s2 = texture( probesSH, vec3( vUVW.xy, ( uvZBase + 2.0 * paddedSlices ) / atlasDepth ) );
  62. vec4 s3 = texture( probesSH, vec3( vUVW.xy, ( uvZBase + 3.0 * paddedSlices ) / atlasDepth ) );
  63. vec4 s4 = texture( probesSH, vec3( vUVW.xy, ( uvZBase + 4.0 * paddedSlices ) / atlasDepth ) );
  64. vec4 s5 = texture( probesSH, vec3( vUVW.xy, ( uvZBase + 5.0 * paddedSlices ) / atlasDepth ) );
  65. vec4 s6 = texture( probesSH, vec3( vUVW.xy, ( uvZBase + 6.0 * paddedSlices ) / atlasDepth ) );
  66. // Unpack 9 vec3 SH L2 coefficients
  67. vec3 c0 = s0.xyz;
  68. vec3 c1 = vec3( s0.w, s1.xy );
  69. vec3 c2 = vec3( s1.zw, s2.x );
  70. vec3 c3 = s2.yzw;
  71. vec3 c4 = s3.xyz;
  72. vec3 c5 = vec3( s3.w, s4.xy );
  73. vec3 c6 = vec3( s4.zw, s5.x );
  74. vec3 c7 = s5.yzw;
  75. vec3 c8 = s6.xyz;
  76. vec3 n = normalize( vWorldNormal );
  77. float x = n.x, y = n.y, z = n.z;
  78. // band 0
  79. vec3 result = c0 * 0.886227;
  80. // band 1,
  81. result += c1 * 2.0 * 0.511664 * y;
  82. result += c2 * 2.0 * 0.511664 * z;
  83. result += c3 * 2.0 * 0.511664 * x;
  84. // band 2,
  85. result += c4 * 2.0 * 0.429043 * x * y;
  86. result += c5 * 2.0 * 0.429043 * y * z;
  87. result += c6 * ( 0.743125 * z * z - 0.247708 );
  88. result += c7 * 2.0 * 0.429043 * x * z;
  89. result += c8 * 0.429043 * ( x * x - y * y );
  90. gl_FragColor = vec4( max( result, vec3( 0.0 ) ), 1.0 );
  91. #include <tonemapping_fragment>
  92. #include <colorspace_fragment>
  93. }
  94. `
  95. } );
  96. const res = probes.resolution;
  97. const count = res.x * res.y * res.z;
  98. super( geometry, material, count );
  99. /**
  100. * The probe grid to visualize.
  101. *
  102. * @type {LightProbeGrid}
  103. */
  104. this.probes = probes;
  105. this.type = 'LightProbeGridHelper';
  106. this.update();
  107. }
  108. /**
  109. * Rebuilds instance matrices and UVW attributes from the current probe grid.
  110. * Call this after changing `probes` or after re-baking.
  111. */
  112. update() {
  113. const probes = this.probes;
  114. const res = probes.resolution;
  115. const count = res.x * res.y * res.z;
  116. // Resize instance matrix buffer if needed
  117. if ( this.instanceMatrix.count !== count ) {
  118. this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 );
  119. }
  120. this.count = count;
  121. const uvwArray = new Float32Array( count * 3 );
  122. const matrix = new Matrix4();
  123. const probePos = new Vector3();
  124. let i = 0;
  125. for ( let iz = 0; iz < res.z; iz ++ ) {
  126. for ( let iy = 0; iy < res.y; iy ++ ) {
  127. for ( let ix = 0; ix < res.x; ix ++ ) {
  128. // Remap to texel centers (must match lightprobes_pars_fragment.glsl.js)
  129. uvwArray[ i * 3 ] = ( ix + 0.5 ) / res.x;
  130. uvwArray[ i * 3 + 1 ] = ( iy + 0.5 ) / res.y;
  131. uvwArray[ i * 3 + 2 ] = ( iz + 0.5 ) / res.z;
  132. probes.getProbePosition( ix, iy, iz, probePos );
  133. matrix.makeTranslation( probePos.x, probePos.y, probePos.z );
  134. this.setMatrixAt( i, matrix );
  135. i ++;
  136. }
  137. }
  138. }
  139. this.instanceMatrix.needsUpdate = true;
  140. this.geometry.setAttribute( 'instanceUVW', new InstancedBufferAttribute( uvwArray, 3 ) );
  141. // Update texture uniforms
  142. this.material.uniforms.probesSH.value = probes.texture;
  143. this.material.uniforms.probesResolution.value.copy( probes.resolution );
  144. }
  145. /**
  146. * Frees the GPU-related resources allocated by this instance. Call this
  147. * method whenever this instance is no longer used in your app.
  148. */
  149. dispose() {
  150. this.geometry.dispose();
  151. this.material.dispose();
  152. }
  153. }
  154. export { LightProbeGridHelper };
粤ICP备19079148号