-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WebGPURenderer: RectAreaLight support (#28580)
* WebGPURenderer: RectAreaLight support * detect float32 texture filetring support * cache runtime availbility checks * remove testing code * update screenshot * excude from test --------- Co-authored-by: aardgoose <[email protected]>
- Loading branch information
Showing
11 changed files
with
422 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import { tslFn, If, mat3, vec2, vec3 } from '../../shadernode/ShaderNode.js'; | ||
import { max } from '../../math/MathNode.js'; | ||
|
||
// Rect Area Light | ||
|
||
// Real-Time Polygonal-Light Shading with Linearly Transformed Cosines | ||
// by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt | ||
// code: https://github.com/selfshadow/ltc_code/ | ||
|
||
const LTC_Uv = tslFn( ( { N, V, roughness } ) => { | ||
|
||
const LUT_SIZE = 64.0; | ||
const LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; | ||
const LUT_BIAS = 0.5 / LUT_SIZE; | ||
|
||
const dotNV = N.dot( V ).saturate(); | ||
|
||
// texture parameterized by sqrt( GGX alpha ) and sqrt( 1 - cos( theta ) ) | ||
const uv = vec2( roughness, dotNV.oneMinus().sqrt() ); | ||
|
||
uv.assign( uv.mul( LUT_SCALE ).add( LUT_BIAS ) ); | ||
|
||
return uv; | ||
|
||
} ).setLayout( { | ||
name: 'LTC_Uv', | ||
type: 'vec2', | ||
inputs: [ | ||
{ name: 'N', type: 'vec3' }, | ||
{ name: 'V', type: 'vec3' }, | ||
{ name: 'roughness', type: 'float' } | ||
] | ||
} ); | ||
|
||
const LTC_ClippedSphereFormFactor = tslFn( ( { f } ) => { | ||
|
||
// Real-Time Area Lighting: a Journey from Research to Production (p.102) | ||
// An approximation of the form factor of a horizon-clipped rectangle. | ||
|
||
const l = f.length(); | ||
|
||
return max( l.mul( l ).add( f.z ).div( l.add( 1.0 ) ), 0 ); | ||
|
||
} ).setLayout( { | ||
name: 'LTC_ClippedSphereFormFactor', | ||
type: 'float', | ||
inputs: [ | ||
{ name: 'f', type: 'vec3' } | ||
] | ||
} ); | ||
|
||
const LTC_EdgeVectorFormFactor = tslFn( ( { v1, v2 } ) => { | ||
|
||
const x = v1.dot( v2 ); | ||
const y = x.abs().toVar(); | ||
|
||
// rational polynomial approximation to theta / sin( theta ) / 2PI | ||
const a = y.mul( 0.0145206 ).add( 0.4965155 ).mul( y ).add( 0.8543985 ).toVar(); | ||
const b = y.add( 4.1616724 ).mul( y ).add( 3.4175940 ).toVar(); | ||
const v = a.div( b ); | ||
|
||
const theta_sintheta = x.greaterThan( 0.0 ).cond( v, max( x.mul( x ).oneMinus(), 1e-7 ).inverseSqrt().mul( 0.5 ).sub( v ) ); | ||
|
||
return v1.cross( v2 ).mul( theta_sintheta ); | ||
|
||
} ).setLayout( { | ||
name: 'LTC_EdgeVectorFormFactor', | ||
type: 'vec3', | ||
inputs: [ | ||
{ name: 'v1', type: 'vec3' }, | ||
{ name: 'v2', type: 'vec3' } | ||
] | ||
} ); | ||
|
||
const LTC_Evaluate = tslFn( ( { N, V, P, mInv, p0, p1, p2, p3 } ) => { | ||
|
||
// bail if point is on back side of plane of light | ||
// assumes ccw winding order of light vertices | ||
const v1 = p1.sub( p0 ).toVar(); | ||
const v2 = p3.sub( p0 ).toVar(); | ||
|
||
const lightNormal = v1.cross( v2 ); | ||
const result = vec3().toVar(); | ||
|
||
If( lightNormal.dot( P.sub( p0 ) ).greaterThanEqual( 0.0 ), () => { | ||
|
||
// construct orthonormal basis around N | ||
const T1 = V.sub( N.mul( V.dot( N ) ) ).normalize(); | ||
const T2 = N.cross( T1 ).negate(); // negated from paper; possibly due to a different handedness of world coordinate system | ||
|
||
// compute transform | ||
const mat = mInv.mul( mat3( T1, T2, N ).transpose() ).toVar(); | ||
|
||
// transform rect | ||
// & project rect onto sphere | ||
const coords0 = mat.mul( p0.sub( P ) ).normalize().toVar(); | ||
const coords1 = mat.mul( p1.sub( P ) ).normalize().toVar(); | ||
const coords2 = mat.mul( p2.sub( P ) ).normalize().toVar(); | ||
const coords3 = mat.mul( p3.sub( P ) ).normalize().toVar(); | ||
|
||
// calculate vector form factor | ||
const vectorFormFactor = vec3( 0 ).toVar(); | ||
vectorFormFactor.addAssign( LTC_EdgeVectorFormFactor( { v1: coords0, v2: coords1 } ) ); | ||
vectorFormFactor.addAssign( LTC_EdgeVectorFormFactor( { v1: coords1, v2: coords2 } ) ); | ||
vectorFormFactor.addAssign( LTC_EdgeVectorFormFactor( { v1: coords2, v2: coords3 } ) ); | ||
vectorFormFactor.addAssign( LTC_EdgeVectorFormFactor( { v1: coords3, v2: coords0 } ) ); | ||
|
||
// adjust for horizon clipping | ||
result.assign( vec3( LTC_ClippedSphereFormFactor( { f: vectorFormFactor } ) ) ); | ||
|
||
} ); | ||
|
||
return result; | ||
|
||
} ).setLayout( { | ||
name: 'LTC_Evaluate', | ||
type: 'vec3', | ||
inputs: [ | ||
{ name: 'N', type: 'vec3' }, | ||
{ name: 'V', type: 'vec3' }, | ||
{ name: 'P', type: 'vec3' }, | ||
{ name: 'mInv', type: 'mat3' }, | ||
{ name: 'p0', type: 'vec3' }, | ||
{ name: 'p1', type: 'vec3' }, | ||
{ name: 'p2', type: 'vec3' }, | ||
{ name: 'p3', type: 'vec3' } | ||
] | ||
} ); | ||
|
||
|
||
export { LTC_Evaluate, LTC_Uv }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import AnalyticLightNode from './AnalyticLightNode.js'; | ||
import { addLightNode } from './LightsNode.js'; | ||
import { texture } from '../accessors/TextureNode.js'; | ||
import { uniform } from '../core/UniformNode.js'; | ||
import { objectViewPosition } from '../accessors/Object3DNode.js'; | ||
import { addNodeClass } from '../core/Node.js'; | ||
|
||
import { RectAreaLight, Matrix4, Vector3, UniformsLib } from 'three'; | ||
|
||
const _matrix41 = new Matrix4(); | ||
const _matrix42 = new Matrix4(); | ||
let ltc_1, ltc_2; | ||
|
||
class RectAreaLightNode extends AnalyticLightNode { | ||
|
||
constructor( light = null ) { | ||
|
||
super( light ); | ||
|
||
this.halfHeight = uniform( new Vector3() ); | ||
this.halfWidth = uniform( new Vector3() ); | ||
|
||
} | ||
|
||
update( frame ) { | ||
|
||
super.update( frame ); | ||
|
||
const { light } = this; | ||
|
||
const viewMatrix = frame.camera.matrixWorldInverse; | ||
|
||
_matrix42.identity(); | ||
_matrix41.copy( light.matrixWorld ); | ||
_matrix41.premultiply( viewMatrix ); | ||
_matrix42.extractRotation( _matrix41 ); | ||
|
||
this.halfWidth.value.set( light.width * 0.5, 0.0, 0.0 ); | ||
this.halfHeight.value.set( 0.0, light.height * 0.5, 0.0 ); | ||
|
||
this.halfWidth.value.applyMatrix4( _matrix42 ); | ||
this.halfHeight.value.applyMatrix4( _matrix42 ); | ||
|
||
} | ||
|
||
setup( builder ) { | ||
|
||
super.setup( builder ); | ||
|
||
if ( ltc_1 === undefined ) { | ||
|
||
if ( builder.isAvailable( 'float32Filterable' ) ) { | ||
|
||
ltc_1 = texture( UniformsLib.LTC_FLOAT_1 ); | ||
ltc_2 = texture( UniformsLib.LTC_FLOAT_2 ); | ||
|
||
} else { | ||
|
||
ltc_1 = texture( UniformsLib.LTC_HALF_1 ); | ||
ltc_2 = texture( UniformsLib.LTC_HALF_2 ); | ||
|
||
} | ||
|
||
} | ||
|
||
const { colorNode, light } = this; | ||
const lightingModel = builder.context.lightingModel; | ||
|
||
const lightPosition = objectViewPosition( light ); | ||
const reflectedLight = builder.context.reflectedLight; | ||
|
||
lightingModel.directRectArea( { | ||
lightColor: colorNode, | ||
lightPosition, | ||
halfWidth: this.halfWidth, | ||
halfHeight: this.halfHeight, | ||
reflectedLight, | ||
ltc_1, | ||
ltc_2 | ||
}, builder.stack, builder ); | ||
|
||
} | ||
|
||
} | ||
|
||
export default RectAreaLightNode; | ||
|
||
addNodeClass( 'RectAreaLightNode', RectAreaLightNode ); | ||
|
||
addLightNode( RectAreaLight, RectAreaLightNode ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.