1
0

PointLightDataNode.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { Color, Node, Vector3, Vector4 } from 'three/webgpu';
  2. import { Loop, NodeUpdateType, getDistanceAttenuation, positionView, renderGroup, uniform, uniformArray, vec3 } from 'three/tsl';
  3. const _position = /*@__PURE__*/ new Vector3();
  4. const warn = ( message ) => {
  5. console.warn( `THREE.PointLightDataNode: ${ message }` );
  6. };
  7. /**
  8. * Batched data node for point lights in dynamic lighting mode.
  9. *
  10. * @augments Node
  11. */
  12. class PointLightDataNode extends Node {
  13. static get type() {
  14. return 'PointLightDataNode';
  15. }
  16. constructor( maxCount = 16 ) {
  17. super();
  18. this.maxCount = maxCount;
  19. this._lights = [];
  20. this._colors = [];
  21. this._positionsAndCutoff = [];
  22. this._decays = [];
  23. for ( let i = 0; i < maxCount; i ++ ) {
  24. this._colors.push( new Color() );
  25. this._positionsAndCutoff.push( new Vector4() );
  26. this._decays.push( new Vector4() );
  27. }
  28. this.colorsNode = uniformArray( this._colors, 'color' ).setGroup( renderGroup );
  29. this.positionsAndCutoffNode = uniformArray( this._positionsAndCutoff, 'vec4' ).setGroup( renderGroup );
  30. this.decaysNode = uniformArray( this._decays, 'vec4' ).setGroup( renderGroup );
  31. this.countNode = uniform( 0, 'int' ).setGroup( renderGroup );
  32. this.updateType = NodeUpdateType.RENDER;
  33. }
  34. setLights( lights ) {
  35. if ( lights.length > this.maxCount ) {
  36. warn( `${ lights.length } lights exceed the configured max of ${ this.maxCount }. Excess lights are ignored.` );
  37. }
  38. this._lights = lights;
  39. return this;
  40. }
  41. update( { camera } ) {
  42. const count = Math.min( this._lights.length, this.maxCount );
  43. this.countNode.value = count;
  44. for ( let i = 0; i < count; i ++ ) {
  45. const light = this._lights[ i ];
  46. this._colors[ i ].copy( light.color ).multiplyScalar( light.intensity );
  47. _position.setFromMatrixPosition( light.matrixWorld );
  48. _position.applyMatrix4( camera.matrixWorldInverse );
  49. const positionAndCutoff = this._positionsAndCutoff[ i ];
  50. positionAndCutoff.x = _position.x;
  51. positionAndCutoff.y = _position.y;
  52. positionAndCutoff.z = _position.z;
  53. positionAndCutoff.w = light.distance;
  54. this._decays[ i ].x = light.decay;
  55. }
  56. }
  57. setup( builder ) {
  58. const surfacePosition = builder.context.positionView || positionView;
  59. const { lightingModel, reflectedLight } = builder.context;
  60. const dynDiffuse = vec3( 0 ).toVar( 'dynPointDiffuse' );
  61. const dynSpecular = vec3( 0 ).toVar( 'dynPointSpecular' );
  62. Loop( this.countNode, ( { i } ) => {
  63. const positionAndCutoff = this.positionsAndCutoffNode.element( i );
  64. const lightViewPosition = positionAndCutoff.xyz;
  65. const cutoffDistance = positionAndCutoff.w;
  66. const decayExponent = this.decaysNode.element( i ).x;
  67. const lightVector = lightViewPosition.sub( surfacePosition ).toVar();
  68. const lightDirection = lightVector.normalize().toVar();
  69. const lightDistance = lightVector.length();
  70. const attenuation = getDistanceAttenuation( {
  71. lightDistance,
  72. cutoffDistance,
  73. decayExponent
  74. } );
  75. const lightColor = this.colorsNode.element( i ).mul( attenuation ).toVar();
  76. lightingModel.direct( {
  77. lightDirection,
  78. lightColor,
  79. lightNode: { light: {}, shadowNode: null },
  80. reflectedLight: { directDiffuse: dynDiffuse, directSpecular: dynSpecular }
  81. }, builder );
  82. } );
  83. reflectedLight.directDiffuse.addAssign( dynDiffuse );
  84. reflectedLight.directSpecular.addAssign( dynSpecular );
  85. }
  86. }
  87. export default PointLightDataNode;
粤ICP备19079148号