Buckets:
| import ShadowNode from './ShadowNode.js'; | |
| import { uniform } from '../core/UniformNode.js'; | |
| import { float, vec2, If, Fn, nodeObject } from '../tsl/TSLBase.js'; | |
| import { reference } from '../accessors/ReferenceNode.js'; | |
| import { texture } from '../accessors/TextureNode.js'; | |
| import { max, abs, sign } from '../math/MathNode.js'; | |
| import { sub, div } from '../math/OperatorNode.js'; | |
| import { renderGroup } from '../core/UniformGroupNode.js'; | |
| import { Vector2 } from '../../math/Vector2.js'; | |
| import { Vector4 } from '../../math/Vector4.js'; | |
| import { Color } from '../../math/Color.js'; | |
| import { BasicShadowMap } from '../../constants.js'; | |
| const _clearColor = /*@__PURE__*/ new Color(); | |
| // cubeToUV() maps a 3D direction vector suitable for cube texture mapping to a 2D | |
| // vector suitable for 2D texture mapping. This code uses the following layout for the | |
| // 2D texture: | |
| // | |
| // xzXZ | |
| // y Y | |
| // | |
| // Y - Positive y direction | |
| // y - Negative y direction | |
| // X - Positive x direction | |
| // x - Negative x direction | |
| // Z - Positive z direction | |
| // z - Negative z direction | |
| // | |
| // Source and test bed: | |
| // https://gist.github.com/tschw/da10c43c467ce8afd0c4 | |
| export const cubeToUV = /*@__PURE__*/ Fn( ( [ pos, texelSizeY ] ) => { | |
| const v = pos.toVar(); | |
| // Number of texels to avoid at the edge of each square | |
| const absV = abs( v ); | |
| // Intersect unit cube | |
| const scaleToCube = div( 1.0, max( absV.x, max( absV.y, absV.z ) ) ); | |
| absV.mulAssign( scaleToCube ); | |
| // Apply scale to avoid seams | |
| // two texels less per square (one texel will do for NEAREST) | |
| v.mulAssign( scaleToCube.mul( texelSizeY.mul( 2 ).oneMinus() ) ); | |
| // Unwrap | |
| // space: -1 ... 1 range for each square | |
| // | |
| // #X## dim := ( 4 , 2 ) | |
| // # # center := ( 1 , 1 ) | |
| const planar = vec2( v.xy ).toVar(); | |
| const almostATexel = texelSizeY.mul( 1.5 ); | |
| const almostOne = almostATexel.oneMinus(); | |
| If( absV.z.greaterThanEqual( almostOne ), () => { | |
| If( v.z.greaterThan( 0.0 ), () => { | |
| planar.x.assign( sub( 4.0, v.x ) ); | |
| } ); | |
| } ).ElseIf( absV.x.greaterThanEqual( almostOne ), () => { | |
| const signX = sign( v.x ); | |
| planar.x.assign( v.z.mul( signX ).add( signX.mul( 2.0 ) ) ); | |
| } ).ElseIf( absV.y.greaterThanEqual( almostOne ), () => { | |
| const signY = sign( v.y ); | |
| planar.x.assign( v.x.add( signY.mul( 2.0 ) ).add( 2.0 ) ); | |
| planar.y.assign( v.z.mul( signY ).sub( 2.0 ) ); | |
| } ); | |
| // Transform to UV space | |
| // scale := 0.5 / dim | |
| // translate := ( center + 0.5 ) / dim | |
| return vec2( 0.125, 0.25 ).mul( planar ).add( vec2( 0.375, 0.75 ) ).flipY(); | |
| } ).setLayout( { | |
| name: 'cubeToUV', | |
| type: 'vec2', | |
| inputs: [ | |
| { name: 'pos', type: 'vec3' }, | |
| { name: 'texelSizeY', type: 'float' } | |
| ] | |
| } ); | |
| export const BasicPointShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, bd3D, dp, texelSize } ) => { | |
| return texture( depthTexture, cubeToUV( bd3D, texelSize.y ) ).compare( dp ); | |
| } ); | |
| export const PointShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, bd3D, dp, texelSize, shadow } ) => { | |
| const radius = reference( 'radius', 'float', shadow ).setGroup( renderGroup ); | |
| const offset = vec2( - 1.0, 1.0 ).mul( radius ).mul( texelSize.y ); | |
| return texture( depthTexture, cubeToUV( bd3D.add( offset.xyy ), texelSize.y ) ).compare( dp ) | |
| .add( texture( depthTexture, cubeToUV( bd3D.add( offset.yyy ), texelSize.y ) ).compare( dp ) ) | |
| .add( texture( depthTexture, cubeToUV( bd3D.add( offset.xyx ), texelSize.y ) ).compare( dp ) ) | |
| .add( texture( depthTexture, cubeToUV( bd3D.add( offset.yyx ), texelSize.y ) ).compare( dp ) ) | |
| .add( texture( depthTexture, cubeToUV( bd3D, texelSize.y ) ).compare( dp ) ) | |
| .add( texture( depthTexture, cubeToUV( bd3D.add( offset.xxy ), texelSize.y ) ).compare( dp ) ) | |
| .add( texture( depthTexture, cubeToUV( bd3D.add( offset.yxy ), texelSize.y ) ).compare( dp ) ) | |
| .add( texture( depthTexture, cubeToUV( bd3D.add( offset.xxx ), texelSize.y ) ).compare( dp ) ) | |
| .add( texture( depthTexture, cubeToUV( bd3D.add( offset.yxx ), texelSize.y ) ).compare( dp ) ) | |
| .mul( 1.0 / 9.0 ); | |
| } ); | |
| const pointShadowFilter = /*@__PURE__*/ Fn( ( { filterFn, depthTexture, shadowCoord, shadow } ) => { | |
| // for point lights, the uniform @vShadowCoord is re-purposed to hold | |
| // the vector from the light to the world-space position of the fragment. | |
| const lightToPosition = shadowCoord.xyz.toVar(); | |
| const lightToPositionLength = lightToPosition.length(); | |
| const cameraNearLocal = uniform( 'float' ).setGroup( renderGroup ).onRenderUpdate( () => shadow.camera.near ); | |
| const cameraFarLocal = uniform( 'float' ).setGroup( renderGroup ).onRenderUpdate( () => shadow.camera.far ); | |
| const bias = reference( 'bias', 'float', shadow ).setGroup( renderGroup ); | |
| const mapSize = uniform( shadow.mapSize ).setGroup( renderGroup ); | |
| const result = float( 1.0 ).toVar(); | |
| If( lightToPositionLength.sub( cameraFarLocal ).lessThanEqual( 0.0 ).and( lightToPositionLength.sub( cameraNearLocal ).greaterThanEqual( 0.0 ) ), () => { | |
| // dp = normalized distance from light to fragment position | |
| const dp = lightToPositionLength.sub( cameraNearLocal ).div( cameraFarLocal.sub( cameraNearLocal ) ).toVar(); // need to clamp? | |
| dp.addAssign( bias ); | |
| // bd3D = base direction 3D | |
| const bd3D = lightToPosition.normalize(); | |
| const texelSize = vec2( 1.0 ).div( mapSize.mul( vec2( 4.0, 2.0 ) ) ); | |
| // percentage-closer filtering | |
| result.assign( filterFn( { depthTexture, bd3D, dp, texelSize, shadow } ) ); | |
| } ); | |
| return result; | |
| } ); | |
| const _viewport = /*@__PURE__*/ new Vector4(); | |
| const _viewportSize = /*@__PURE__*/ new Vector2(); | |
| const _shadowMapSize = /*@__PURE__*/ new Vector2(); | |
| /** | |
| * Represents the shadow implementation for point light nodes. | |
| * | |
| * @augments ShadowNode | |
| */ | |
| class PointShadowNode extends ShadowNode { | |
| static get type() { | |
| return 'PointShadowNode'; | |
| } | |
| /** | |
| * Constructs a new point shadow node. | |
| * | |
| * @param {PointLight} light - The shadow casting point light. | |
| * @param {?PointLightShadow} [shadow=null] - An optional point light shadow. | |
| */ | |
| constructor( light, shadow = null ) { | |
| super( light, shadow ); | |
| } | |
| /** | |
| * Overwrites the default implementation to return point light shadow specific | |
| * filtering functions. | |
| * | |
| * @param {number} type - The shadow type. | |
| * @return {Function} The filtering function. | |
| */ | |
| getShadowFilterFn( type ) { | |
| return type === BasicShadowMap ? BasicPointShadowFilter : PointShadowFilter; | |
| } | |
| /** | |
| * Overwrites the default implementation so the unaltered shadow position is used. | |
| * | |
| * @param {NodeBuilder} builder - A reference to the current node builder. | |
| * @param {Node<vec3>} shadowPosition - A node representing the shadow position. | |
| * @return {Node<vec3>} The shadow coordinates. | |
| */ | |
| setupShadowCoord( builder, shadowPosition ) { | |
| return shadowPosition; | |
| } | |
| /** | |
| * Overwrites the default implementation to only use point light specific | |
| * shadow filter functions. | |
| * | |
| * @param {NodeBuilder} builder - A reference to the current node builder. | |
| * @param {Object} inputs - A configuration object that defines the shadow filtering. | |
| * @param {Function} inputs.filterFn - This function defines the filtering type of the shadow map e.g. PCF. | |
| * @param {Texture} inputs.shadowTexture - A reference to the shadow map's texture. | |
| * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. | |
| * @param {Node<vec3>} inputs.shadowCoord - Shadow coordinates which are used to sample from the shadow map. | |
| * @param {LightShadow} inputs.shadow - The light shadow. | |
| * @return {Node<float>} The result node of the shadow filtering. | |
| */ | |
| setupShadowFilter( builder, { filterFn, shadowTexture, depthTexture, shadowCoord, shadow } ) { | |
| return pointShadowFilter( { filterFn, shadowTexture, depthTexture, shadowCoord, shadow } ); | |
| } | |
| /** | |
| * Overwrites the default implementation with point light specific | |
| * rendering code. | |
| * | |
| * @param {NodeFrame} frame - A reference to the current node frame. | |
| */ | |
| renderShadow( frame ) { | |
| const { shadow, shadowMap, light } = this; | |
| const { renderer, scene } = frame; | |
| const shadowFrameExtents = shadow.getFrameExtents(); | |
| _shadowMapSize.copy( shadow.mapSize ); | |
| _shadowMapSize.multiply( shadowFrameExtents ); | |
| shadowMap.setSize( _shadowMapSize.width, _shadowMapSize.height ); | |
| _viewportSize.copy( shadow.mapSize ); | |
| // | |
| const previousAutoClear = renderer.autoClear; | |
| const previousClearColor = renderer.getClearColor( _clearColor ); | |
| const previousClearAlpha = renderer.getClearAlpha(); | |
| renderer.autoClear = false; | |
| renderer.setClearColor( shadow.clearColor, shadow.clearAlpha ); | |
| renderer.clear(); | |
| const viewportCount = shadow.getViewportCount(); | |
| for ( let vp = 0; vp < viewportCount; vp ++ ) { | |
| const viewport = shadow.getViewport( vp ); | |
| const x = _viewportSize.x * viewport.x; | |
| const y = _shadowMapSize.y - _viewportSize.y - ( _viewportSize.y * viewport.y ); | |
| _viewport.set( | |
| x, | |
| y, | |
| _viewportSize.x * viewport.z, | |
| _viewportSize.y * viewport.w | |
| ); | |
| shadowMap.viewport.copy( _viewport ); | |
| shadow.updateMatrices( light, vp ); | |
| renderer.render( scene, shadow.camera ); | |
| } | |
| // | |
| renderer.autoClear = previousAutoClear; | |
| renderer.setClearColor( previousClearColor, previousClearAlpha ); | |
| } | |
| } | |
| export default PointShadowNode; | |
| /** | |
| * TSL function for creating an instance of `PointShadowNode`. | |
| * | |
| * @tsl | |
| * @function | |
| * @param {PointLight} light - The shadow casting point light. | |
| * @param {?PointLightShadow} [shadow=null] - An optional point light shadow. | |
| * @return {PointShadowNode} The created point shadow node. | |
| */ | |
| export const pointShadow = ( light, shadow ) => nodeObject( new PointShadowNode( light, shadow ) ); | |
Xet Storage Details
- Size:
- 9.65 kB
- Xet hash:
- 65a1dcf57afecf4f9f795d77394aea6b0020727c76d38a7df1fc4de5e5bbe41d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.