|
@@ -0,0 +1,84 @@
|
|
|
+import AnalyticLightNode from './AnalyticLightNode.js';
|
|
|
+import LightsNode from './LightsNode.js';
|
|
|
+import getDistanceAttenuation from '../functions/light/getDistanceAttenuation.js';
|
|
|
+import getDirectionVector from '../functions/light/getDirectionVector.js';
|
|
|
+import { uniform, smoothstep, positionView, objectViewPosition } from '../shadernode/ShaderNodeElements.js';
|
|
|
+
|
|
|
+import { Vector3, SpotLight } from 'three';
|
|
|
+
|
|
|
+const getSpotAttenuation = ( coneCosine, penumbraCosine, angleCosine ) => smoothstep( coneCosine, penumbraCosine, angleCosine );
|
|
|
+
|
|
|
+class SpotLightNode extends AnalyticLightNode {
|
|
|
+
|
|
|
+ constructor( light = null ) {
|
|
|
+
|
|
|
+ super( light );
|
|
|
+
|
|
|
+ this.directionNode = uniform( new Vector3() );
|
|
|
+
|
|
|
+ this.coneCosNode = uniform( 0 );
|
|
|
+ this.penumbraCosNode = uniform( 0 );
|
|
|
+
|
|
|
+ this.cutoffDistanceNode = uniform( 0 );
|
|
|
+ this.decayExponentNode = uniform( 0 );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ update( frame ) {
|
|
|
+
|
|
|
+ super.update( frame );
|
|
|
+
|
|
|
+ const { light } = this;
|
|
|
+
|
|
|
+ getDirectionVector( light, frame.camera, this.directionNode.value );
|
|
|
+
|
|
|
+ this.coneCosNode.value = Math.cos( light.angle );
|
|
|
+ this.penumbraCosNode.value = Math.cos( light.angle * ( 1 - light.penumbra ) );
|
|
|
+
|
|
|
+ this.cutoffDistanceNode.value = light.distance;
|
|
|
+ this.decayExponentNode.value = light.decay;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ construct( builder ) {
|
|
|
+
|
|
|
+ const { colorNode, cutoffDistanceNode, decayExponentNode, light } = this;
|
|
|
+
|
|
|
+ const lVector = objectViewPosition( light ).sub( positionView );
|
|
|
+
|
|
|
+ const lightDirection = lVector.normalize();
|
|
|
+ const angleCos = lightDirection.dot( this.directionNode )
|
|
|
+ const spotAttenuation = getSpotAttenuation( this.coneCosNode, this.penumbraCosNode, angleCos );
|
|
|
+
|
|
|
+ const lightDistance = lVector.length();
|
|
|
+
|
|
|
+ const lightAttenuation = getDistanceAttenuation.call( {
|
|
|
+ lightDistance,
|
|
|
+ cutoffDistance: cutoffDistanceNode,
|
|
|
+ decayExponent: decayExponentNode
|
|
|
+ } );
|
|
|
+
|
|
|
+ const finalColor = colorNode.mul( spotAttenuation ).mul( lightAttenuation );
|
|
|
+
|
|
|
+ const lightColor = spotAttenuation.greaterThan( 0 ).cond( finalColor, 0 );
|
|
|
+
|
|
|
+ const lightingModelFunctionNode = builder.context.lightingModelNode;
|
|
|
+ const reflectedLight = builder.context.reflectedLight;
|
|
|
+
|
|
|
+ if ( lightingModelFunctionNode?.direct ) {
|
|
|
+
|
|
|
+ lightingModelFunctionNode.direct.call( {
|
|
|
+ lightDirection,
|
|
|
+ lightColor,
|
|
|
+ reflectedLight
|
|
|
+ }, builder );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+LightsNode.setReference( SpotLight, SpotLightNode );
|
|
|
+
|
|
|
+export default SpotLightNode;
|