Просмотр исходного кода

SpotLightNode: Add custom attenuation using `spotLight.attenuationNode` (#31013)

* add custom attenuation

* cleanup
sunag 11 месяцев назад
Родитель
Сommit
5ab06a3a6e

+ 32 - 3
examples/webgpu_lights_spotlight.html

@@ -26,6 +26,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
+			import { Fn, vec2, length, abs, max, min, div, mul, clamp, acos } from 'three/tsl';
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
@@ -86,13 +87,35 @@
 
 				}
 
+				const boxAttenuationFn = Fn( ( [ lightNode ], builder ) => {
+
+					const sdBox = Fn( ( [ p, b ] ) => {
+
+						const d = vec2( abs( p ).sub( b ) ).toVar();
+
+						return length( max( d, 0.0 ) ).add( min( max( d.x, d.y ), 0.0 ) );
+
+					} );
+
+					const penumbraCos = lightNode.penumbraCosNode;
+					const spotLightCoord = lightNode.getSpotLightCoord( builder );
+					const coord = spotLightCoord.xyz.div( spotLightCoord.w );
+
+					const boxDist = sdBox( coord.xy.sub( vec2( 0.5 ) ), vec2( 0.5 ) );
+					const angleFactor = div( 1.0, acos( penumbraCos ).sub( 1.0 ) );
+					const attenuation = clamp( mul( 2.0, boxDist ).mul( angleFactor ), 0.0, 1.0 );
+
+					return attenuation;
+
+				} );
+
 				spotLight = new THREE.SpotLight( 0xffffff, 100 );
+				spotLight.map = textures[ 'disturb.jpg' ];
 				spotLight.position.set( 2.5, 5, 2.5 );
 				spotLight.angle = Math.PI / 6;
 				spotLight.penumbra = 1;
 				spotLight.decay = 2;
 				spotLight.distance = 0;
-				spotLight.map = textures[ 'disturb.jpg' ];
 
 				spotLight.castShadow = true;
 				spotLight.shadow.mapSize.width = 1024;
@@ -150,7 +173,8 @@
 					penumbra: spotLight.penumbra,
 					decay: spotLight.decay,
 					focus: spotLight.shadow.focus,
-					shadows: true
+					shadows: true,
+					customAttenuation: false
 				};
 
 				gui.add( params, 'map', textures ).onChange( function ( val ) {
@@ -202,7 +226,6 @@
 
 				} );
 
-
 				gui.add( params, 'shadows' ).onChange( function ( val ) {
 
 					renderer.shadowMap.enabled = val;
@@ -219,6 +242,12 @@
 
 				} );
 
+				gui.add( params, 'customAttenuation' ).name( 'custom attenuation' ).onChange( function ( val ) {
+
+					spotLight.attenuationNode = val ? boxAttenuationFn : null;
+
+				} );
+
 				gui.open();
 
 			}

+ 3 - 2
src/nodes/lighting/LightsNode.js

@@ -125,9 +125,10 @@ class LightsNode extends Node {
 
 			if ( light.isSpotLight === true ) {
 
-				const hashValue = ( light.map !== null ) ? light.map.id : - 1;
+				const hashMap = ( light.map !== null ) ? light.map.id : - 1;
+				const hashAttenuation = ( light.attenuationNode ) ? light.attenuationNode.id : - 1;
 
-				hashData.push( hashValue );
+				hashData.push( hashMap, hashAttenuation );
 
 			}
 

+ 20 - 2
src/nodes/lighting/SpotLightNode.js

@@ -91,6 +91,23 @@ class SpotLightNode extends AnalyticLightNode {
 
 	}
 
+	getSpotLightCoord( builder ) {
+
+		const properties = builder.getNodeProperties( this );
+		let projectionUV = properties.projectionUV;
+
+		if ( projectionUV === undefined ) {
+
+			projectionUV = lightProjectionUV( this.light, builder.context.positionWorld );
+
+			properties.projectionUV = projectionUV;
+
+		}
+
+		return projectionUV;
+
+	}
+
 	setupDirect( builder ) {
 
 		const { colorNode, cutoffDistanceNode, decayExponentNode, light } = this;
@@ -99,7 +116,8 @@ class SpotLightNode extends AnalyticLightNode {
 
 		const lightDirection = lightVector.normalize();
 		const angleCos = lightDirection.dot( lightTargetDirection( light ) );
-		const spotAttenuation = this.getSpotAttenuation( angleCos );
+
+		const spotAttenuation = light.attenuationNode ? light.attenuationNode( this ) : this.getSpotAttenuation( angleCos );
 
 		const lightDistance = lightVector.length();
 
@@ -113,7 +131,7 @@ class SpotLightNode extends AnalyticLightNode {
 
 		if ( light.map ) {
 
-			const spotLightCoord = lightProjectionUV( light, builder.context.positionWorld );
+			const spotLightCoord = this.getSpotLightCoord( builder );
 			const projectedTexture = texture( light.map, spotLightCoord.xy ).onRenderUpdate( () => light.map );
 
 			const inSpotLightMap = spotLightCoord.mul( 2. ).sub( 1. ).abs().lessThan( 1. ).all();

+ 1 - 0
src/nodes/tsl/TSLCore.js

@@ -619,6 +619,7 @@ export const Fn = ( jsFunc, layout = null ) => {
 	};
 
 	fn.shaderNode = shaderNode;
+	fn.id = shaderNode.id;
 
 	fn.setLayout = ( layout ) => {
 

粤ICP备19079148号