three
Version:
JavaScript 3D library
103 lines (71 loc) • 2.71 kB
JavaScript
import SpotLightNode from './SpotLightNode.js';
import { texture } from '../accessors/TextureNode.js';
import { textureSize } from '../accessors/TextureSizeNode.js';
import { float, vec2 } from '../tsl/TSLBase.js';
import { atan } from '../math/MathNode.js';
import { remap } from '../utils/Remap.js';
/**
* An IES version of the default spot light node.
*
* @augments SpotLightNode
*/
class IESSpotLightNode extends SpotLightNode {
static get type() {
return 'IESSpotLightNode';
}
/**
* Constructs a new IES spot light node.
*
* @param {?SpotLight} [light=null] - The spot light source.
*/
constructor( light = null ) {
super( light );
/**
* The texture node representing the IES texture.
*
* @type {?TextureNode}
* @default null
*/
this._iesTextureNode = null;
}
/**
* Overwrites the default implementation to compute an IES conform spot attenuation.
*
* @param {NodeBuilder} builder - The node builder.
* @param {Node<float>} angleCosine - The angle to compute the spot attenuation for.
* @return {Node<float>} The spot attenuation.
*/
getSpotAttenuation( builder, angleCosine ) {
const iesMap = this.light.iesMap;
let spotAttenuation = null;
if ( iesMap && iesMap.isTexture === true ) {
// the light space coordinate used for projected maps, centered on the forward axis twist calculation
const lightCoord = this.getLightCoord( builder ).sub( 0.5 );
this._iesTextureNode = texture( iesMap );
// get the width of half a texel in uv
const texelInset = float( 0.5 ).div( vec2( textureSize( this._iesTextureNode ) ) );
// the twist angle around the light's forward axis, mapping from [0, 359]deg texels
// offset by half a texel so we start at the center of the first texel
const twistAngle = remap( atan( lightCoord.y, lightCoord.x ), - Math.PI, Math.PI ).add( texelInset.y );
// the tilt angle off the forward axis spanning from [0, 180]deg
// inset by half a texel on each side so we're clamping to the center of the extreme texels
const tiltAngle = remap( angleCosine.acos(), 0, Math.PI, texelInset.x, float( 1 ).sub( texelInset.x ) );
spotAttenuation = this._iesTextureNode.sample( vec2( tiltAngle, twistAngle ) ).level( 0 ).r;
} else {
spotAttenuation = super.getSpotAttenuation( builder, angleCosine );
}
return spotAttenuation;
}
/**
* Overwritten to update the IES spot light texture.
*
* @param {NodeFrame} frame - A reference to the current node frame.
*/
update( frame ) {
super.update( frame );
if ( this._iesTextureNode !== null && this.light.iesMap ) {
this._iesTextureNode.value = this.light.iesMap;
}
}
}
export default IESSpotLightNode;