SpotLightNode.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import AnalyticLightNode from './AnalyticLightNode.js';
  2. import LightsNode from './LightsNode.js';
  3. import getDistanceAttenuation from '../functions/light/getDistanceAttenuation.js';
  4. import getDirectionVector from '../functions/light/getDirectionVector.js';
  5. import { uniform, smoothstep, positionView, objectViewPosition } from '../shadernode/ShaderNodeElements.js';
  6. import { Vector3, SpotLight } from 'three';
  7. class SpotLightNode extends AnalyticLightNode {
  8. constructor( light = null ) {
  9. super( light );
  10. this.directionNode = uniform( new Vector3() );
  11. this.coneCosNode = uniform( 0 );
  12. this.penumbraCosNode = uniform( 0 );
  13. this.cutoffDistanceNode = uniform( 0 );
  14. this.decayExponentNode = uniform( 0 );
  15. }
  16. update( frame ) {
  17. super.update( frame );
  18. const { light } = this;
  19. getDirectionVector( light, frame.camera, this.directionNode.value );
  20. this.coneCosNode.value = Math.cos( light.angle );
  21. this.penumbraCosNode.value = Math.cos( light.angle * ( 1 - light.penumbra ) );
  22. this.cutoffDistanceNode.value = light.distance;
  23. this.decayExponentNode.value = light.decay;
  24. }
  25. getSpotAttenuation( angleCosine ) {
  26. const { coneCosNode, penumbraCosNode } = this;
  27. return smoothstep( coneCosNode, penumbraCosNode, angleCosine );
  28. }
  29. construct( builder ) {
  30. const { colorNode, cutoffDistanceNode, decayExponentNode, light } = this;
  31. const lVector = objectViewPosition( light ).sub( positionView );
  32. const lightDirection = lVector.normalize();
  33. const angleCos = lightDirection.dot( this.directionNode );
  34. const spotAttenuation = this.getSpotAttenuation( angleCos );
  35. const lightDistance = lVector.length();
  36. const lightAttenuation = getDistanceAttenuation.call( {
  37. lightDistance,
  38. cutoffDistance: cutoffDistanceNode,
  39. decayExponent: decayExponentNode
  40. } );
  41. const finalColor = colorNode.mul( spotAttenuation ).mul( lightAttenuation );
  42. const lightColor = spotAttenuation.greaterThan( 0 ).cond( finalColor, 0 );
  43. const lightingModelFunctionNode = builder.context.lightingModelNode;
  44. const reflectedLight = builder.context.reflectedLight;
  45. if ( lightingModelFunctionNode && lightingModelFunctionNode.direct ) {
  46. lightingModelFunctionNode.direct.call( {
  47. lightDirection,
  48. lightColor,
  49. reflectedLight
  50. }, builder );
  51. }
  52. }
  53. }
  54. LightsNode.setReference( SpotLight, SpotLightNode );
  55. export default SpotLightNode;