rayzee 11.7.0 → 11.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -6
- package/dist/{ArchiveImporter-DYcRXeKE.js → ArchiveImporter-BJxszU_k.js} +2353 -1356
- package/dist/{BidirectionalIntegrator-BvjRpbTP.js → BidirectionalIntegrator-DWg66vAd.js} +302 -301
- package/dist/{BufferLayout-xPRH9jYD.js → BufferLayout-Bzgccg1o.js} +1 -0
- package/dist/{ColorManagement-oeEk7oSu.js → ColorManagement-B_HjAiy2.js} +1 -1
- package/dist/{EngineIssues-CIZnXHsF.js → EngineIssues-CfJiBaNj.js} +19 -8
- package/dist/{NeuralRetouch-ClaMiKYg.js → NeuralRetouch-BD9E-Yht.js} +2 -2
- package/dist/{NeuralSuperRes-WNEXun9s.js → NeuralSuperRes-C8Mf3tJX.js} +2 -2
- package/dist/{PathTracer-B9wZPPVL.js → PathTracer-DeE9UJ1n.js} +3530 -3402
- package/dist/{PhysicalSky-DcjItqKi.js → PhysicalSky-cKy75-oR.js} +45 -45
- package/dist/{RayzeeRenderer-p-NeE6E2.js → RayzeeRenderer-CzLyBA3G.js} +378 -374
- package/dist/SceneMetadata-C17UfOEp.js +183 -0
- package/dist/{SolarLimb-bZE6y63S.js → SolarLimb-DRYy8tln.js} +184 -172
- package/dist/{ToneMapGPU-sAs8wdgI.js → ToneMapGPU-4GPwoG_c.js} +13 -6
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/color.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/addons/storage.es.js +1 -1
- package/dist/{openStorage-Fe9BoS1_.js → openStorage-CdyVjmps.js} +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +821 -794
- package/dist/rayzee.umd.js +112 -103
- package/dist/{sceneCachePolicy-B9w3BnaP.js → sceneCachePolicy-ywbGKHD3.js} +9 -6
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -0
- package/src/EngineIssues.js +21 -6
- package/src/Passes/AIUpscaler.js +4 -0
- package/src/Passes/OIDNDenoiser.js +8 -6
- package/src/Passes/OIDNTemporalHistory.js +1 -1
- package/src/PathTracerApp.js +10 -1
- package/src/Processor/ArchiveImporter.js +106 -27
- package/src/Processor/AssetLoader.js +22 -0
- package/src/Processor/BufferLayout.js +3 -3
- package/src/Processor/GeometryExtractor.js +12 -4
- package/src/Processor/LightSerializer.js +6 -2
- package/src/Processor/MaterialPacking.js +2 -2
- package/src/Processor/PBRT/PBRTMaterials.js +49 -13
- package/src/Processor/PBRT/PBRTParser.js +144 -22
- package/src/Processor/PBRT/PBRTSceneBuilder.js +287 -67
- package/src/Processor/PBRT/PBRTTextureBake.js +191 -9
- package/src/Processor/PBRT/index.js +19 -4
- package/src/Processor/PlatformImageLoader.js +9 -0
- package/src/Processor/SceneProcessor.js +42 -26
- package/src/Processor/TextureCreator.js +44 -4
- package/src/Processor/ToneMapCPU.js +34 -5
- package/src/Processor/ToneMapGPU.js +15 -9
- package/src/Processor/USD/USDScene.js +981 -17
- package/src/RayzeeRenderer.js +85 -28
- package/src/Stages/AutoExposure.js +21 -8
- package/src/Stages/Compositor.js +39 -1
- package/src/Stages/DenoiserSettings.js +0 -4
- package/src/Stages/LocalExposure.js +44 -18
- package/src/Stages/NRD.js +53 -24
- package/src/Stages/NormalDepth.js +12 -6
- package/src/Stages/PathTracer.js +27 -1
- package/src/Stages/PathTracerStage.js +14 -2
- package/src/Storage/SceneGraphCodec.js +6 -3
- package/src/TSL/BVHTraversal.js +33 -20
- package/src/TSL/CameraRay.js +12 -1
- package/src/TSL/Common.js +25 -8
- package/src/TSL/DebugKernel.js +3 -2
- package/src/TSL/Displacement.js +66 -113
- package/src/TSL/FinalWriteKernel.js +4 -7
- package/src/TSL/GenerateKernel.js +6 -2
- package/src/TSL/LightSplatKernel.js +7 -4
- package/src/TSL/LightsCore.js +6 -2
- package/src/TSL/LightsDirect.js +33 -11
- package/src/TSL/LightsSampling.js +45 -12
- package/src/TSL/SceneResources.js +19 -0
- package/src/TSL/ShadeKernel.js +3 -4
- package/src/TSL/Struct.js +1 -0
- package/src/integrators/BidirectionalIntegrator.js +1 -0
- package/src/managers/CameraManager.js +9 -0
- package/src/managers/DenoisingManager.js +1 -0
- package/src/managers/InteractionManager.js +6 -0
- package/src/managers/MaterialDataManager.js +11 -0
- package/src/managers/UniformManager.js +3 -1
- package/dist/SceneMetadata-D2Sh-0e-.js +0 -110
package/src/TSL/Common.js
CHANGED
|
@@ -336,15 +336,22 @@ export function splitStorage( parts, perPart, type ) {
|
|
|
336
336
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
/**
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
339
|
+
/**
|
|
340
|
+
* Elements `base + r` of a {@link splitStorage}, for each offset r in `rows`, all of one record: a part holds whole
|
|
341
|
+
* records, so the part is picked once and every row is read in its branch.
|
|
342
|
+
*/
|
|
343
|
+
export function storageRows( buffer, base, rows ) {
|
|
343
344
|
|
|
344
345
|
const { parts, perPart } = buffer;
|
|
345
|
-
const e = int(
|
|
346
|
-
const out = ( { uvec4, uvec2 }[ buffer.type ] ?? vec4 )( 0 ).toVar();
|
|
347
|
-
const read = ( k ) => () =>
|
|
346
|
+
const e = int( base ).toVar();
|
|
347
|
+
const out = rows.map( () => ( { uvec4, uvec2 }[ buffer.type ] ?? vec4 )( 0 ).toVar() );
|
|
348
|
+
const read = ( k ) => () => {
|
|
349
|
+
|
|
350
|
+
const at = k === 0 ? e : e.sub( int( perPart * k ) );
|
|
351
|
+
rows.forEach( ( r, j ) => out[ j ].assign( parts[ k ].element( r === 0 ? at : at.add( int( r ) ) ) ) );
|
|
352
|
+
|
|
353
|
+
};
|
|
354
|
+
|
|
348
355
|
let branch = If( e.lessThan( int( perPart ) ), read( 0 ) );
|
|
349
356
|
for ( let k = 1; k < parts.length - 1; k ++ ) branch = branch.ElseIf( e.lessThan( int( perPart * ( k + 1 ) ) ), read( k ) );
|
|
350
357
|
branch.Else( read( parts.length - 1 ) );
|
|
@@ -352,6 +359,13 @@ export function storageElement( buffer, index ) {
|
|
|
352
359
|
|
|
353
360
|
}
|
|
354
361
|
|
|
362
|
+
/** Element `index` of a storage node or of a {@link splitStorage}. */
|
|
363
|
+
export function storageElement( buffer, index ) {
|
|
364
|
+
|
|
365
|
+
return buffer.isSplitStorage ? storageRows( buffer, index, [ 0 ] )[ 0 ] : buffer.element( index );
|
|
366
|
+
|
|
367
|
+
}
|
|
368
|
+
|
|
355
369
|
const asNode = ( v ) => ( v?.isNode ? v : int( v ) );
|
|
356
370
|
|
|
357
371
|
// Element `index · stride + offset`.
|
|
@@ -536,6 +550,7 @@ export const getMaterial = Fn( ( [ materialIndex, materialBuffer ] ) => {
|
|
|
536
550
|
bumpScale: data12.r,
|
|
537
551
|
displacementScale: data12.g,
|
|
538
552
|
displacementMapIndex: int( data12.b ),
|
|
553
|
+
displacementBias: data12.a,
|
|
539
554
|
albedoTransform: arrayToMat3( { data1: data13, data2: data14 } ),
|
|
540
555
|
normalTransform: arrayToMat3( { data1: data15, data2: data16 } ),
|
|
541
556
|
roughnessTransform: arrayToMat3( { data1: data17, data2: data18 } ),
|
|
@@ -586,6 +601,7 @@ export const diffuseGroundMaterial = Fn( () => {
|
|
|
586
601
|
bumpMapIndex: int( - 1 ),
|
|
587
602
|
bumpScale: float( 1.0 ),
|
|
588
603
|
displacementScale: float( 0.0 ),
|
|
604
|
+
displacementBias: float( 0.0 ),
|
|
589
605
|
metalnessMapIndex: int( - 1 ),
|
|
590
606
|
roughnessMapIndex: int( - 1 ),
|
|
591
607
|
displacementMapIndex: int( - 1 ),
|
|
@@ -676,9 +692,10 @@ export const shadowFlagsSettle = ( flags, alphaShadows ) => {
|
|
|
676
692
|
|
|
677
693
|
export const instanceRows = ( bvhBuffer, leafIndex ) => {
|
|
678
694
|
|
|
679
|
-
//
|
|
695
|
+
// Over copy clusters an instance is a 3-vec4 copy record, which can straddle two parts: read per element.
|
|
680
696
|
const records = bvhBuffer.value?.copyRecordBase;
|
|
681
697
|
if ( records !== undefined ) return [ 0, 1, 2 ].map( ( row ) => getDatafromStorageBuffer( bvhBuffer, leafIndex, int( records + row ), int( 3 ) ) );
|
|
698
|
+
if ( bvhBuffer.isSplitStorage ) return storageRows( bvhBuffer, int( leafIndex ).mul( 4 ), [ 1, 2, 3 ] );
|
|
682
699
|
return [
|
|
683
700
|
getDatafromStorageBuffer( bvhBuffer, leafIndex, int( 1 ), int( 4 ) ),
|
|
684
701
|
getDatafromStorageBuffer( bvhBuffer, leafIndex, int( 2 ), int( 4 ) ),
|
package/src/TSL/DebugKernel.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
localId, workgroupId,
|
|
14
14
|
} from 'three/tsl';
|
|
15
15
|
|
|
16
|
-
import { generateRayFromCamera } from './CameraRay.js';
|
|
16
|
+
import { generateRayFromCamera, cameraClipDistance } from './CameraRay.js';
|
|
17
17
|
import { Ray } from './Struct.js';
|
|
18
18
|
import { TraceDebugMode } from './Debugger.js';
|
|
19
19
|
import { pcgHash, getStratifiedSample } from './Random.js';
|
|
@@ -28,6 +28,7 @@ export function buildDebugKernel( params ) {
|
|
|
28
28
|
cameraWorldMatrix, cameraProjectionMatrixInverse, cameraProjectionMatrix, cameraViewMatrix,
|
|
29
29
|
cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
|
|
30
30
|
enableDOF, focalLength, aperture, focusDistance, unitsPerMetre, apertureScale, anamorphicRatio, dofMode, dofBlur,
|
|
31
|
+
cameraClipStart,
|
|
31
32
|
bvhBuffer, triangleBuffer, materialBuffer,
|
|
32
33
|
envTexture, environmentMatrix, environmentIntensity, enableEnvironmentLight,
|
|
33
34
|
visMode, debugVisScale,
|
|
@@ -66,7 +67,7 @@ export function buildDebugKernel( params ) {
|
|
|
66
67
|
|
|
67
68
|
// Modes 1-8, 10 — shared per-mode debug color (primary-ray trace + counters).
|
|
68
69
|
color.assign( TraceDebugMode(
|
|
69
|
-
ray.origin, ray.direction,
|
|
70
|
+
ray.origin.add( ray.direction.mul( cameraClipDistance( ray.direction, cameraClipStart, cameraWorldMatrix, cameraProjection ) ) ), ray.direction,
|
|
70
71
|
bvhBuffer, triangleBuffer, materialBuffer,
|
|
71
72
|
envTexture, environmentMatrix, environmentIntensity, enableEnvironmentLight,
|
|
72
73
|
visMode, debugVisScale,
|
package/src/TSL/Displacement.js
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
import { sceneResources } from './SceneResources.js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Fn, wgslFn, float, vec2, int, bool as tslBool, If, Loop, Break, abs, normalize, dot, cross, min, max, clamp, ceil, select, uintBitsToFloat,
|
|
4
|
+
} from 'three/tsl';
|
|
3
5
|
|
|
4
6
|
import { struct } from './patches.js';
|
|
5
7
|
import { triangleRow, instanceRows, instanceDirToWorld } from './Common.js';
|
|
6
8
|
import { sampleDisplacementMap, bucketTexelSize } from './TextureSampling.js';
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
const MAX_MARCH_STEPS =
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const MIN_MARCH_STEPS = 8;
|
|
11
|
+
const MAX_MARCH_STEPS = 64;
|
|
12
|
+
const MAX_BOUNCE_MARCH_STEPS = 16;
|
|
13
|
+
const STEPS_PER_TEXEL = 0.25;
|
|
14
|
+
const BINARY_STEPS = 6;
|
|
15
|
+
const SLOPE_TEXELS = 1.5;
|
|
12
16
|
|
|
13
17
|
export const DisplacementResult = struct( {
|
|
14
18
|
hitPoint: 'vec3',
|
|
15
19
|
uv: 'vec2',
|
|
16
20
|
normal: 'vec3',
|
|
17
|
-
height: 'float',
|
|
18
21
|
} );
|
|
19
22
|
|
|
23
|
+
// Texture-space UV per raw UV, per axis.
|
|
24
|
+
const uvTransformScale = /*@__PURE__*/ wgslFn( `
|
|
25
|
+
fn uvTransformScale( transform: mat3x3f ) -> vec2f {
|
|
26
|
+
return max( vec2f( length( transform[0].xy ), length( transform[1].xy ) ), vec2f( 1e-6 ) );
|
|
27
|
+
}
|
|
28
|
+
` );
|
|
29
|
+
|
|
20
30
|
/**
|
|
21
31
|
* Tessellation-free displacement mapping via analytical UV ray marching.
|
|
22
32
|
*
|
|
23
|
-
* Instead of projecting march points onto the triangle to get barycentrics (which
|
|
24
|
-
* causes UV distortion from clamping), this computes the ray's UV trajectory
|
|
25
|
-
* analytically using the triangle's actual tangent vectors (dP/du, dP/dv).
|
|
26
|
-
*
|
|
27
33
|
* The ray is parameterized as: P(dt) = hitPoint + rayDir * dt
|
|
28
34
|
* UV along the ray: UV(dt) = hitUV + dUV_dt * dt
|
|
29
35
|
* Ray height above surface: h_ray(dt) = dt * dot(rayDir, N)
|
|
30
|
-
* Displaced surface height: h_surf(dt) =
|
|
36
|
+
* Displaced surface height: h_surf(dt) = sample(UV(dt)) * displacementScale + displacementBias, as three.js
|
|
31
37
|
*
|
|
32
38
|
* We find dt where h_ray(dt) = h_surf(dt).
|
|
33
39
|
*/
|
|
@@ -38,7 +44,6 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
38
44
|
const resultHitPoint = hitInfo.hitPoint.toVar();
|
|
39
45
|
const resultUV = hitInfo.uv.toVar();
|
|
40
46
|
const resultNormal = hitInfo.normal.toVar();
|
|
41
|
-
const resultHeight = float( 0.0 ).toVar();
|
|
42
47
|
|
|
43
48
|
// Fetch triangle vertex data
|
|
44
49
|
const triIdx = hitInfo.triangleIndex;
|
|
@@ -83,16 +88,13 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
83
88
|
const B = edge2.mul( dUV1.x ).sub( edge1.mul( dUV2.x ) ).mul( invDet ).toVar();
|
|
84
89
|
const N = hitInfo.normal.toVar();
|
|
85
90
|
|
|
86
|
-
const scale = material.displacementScale
|
|
91
|
+
const scale = material.displacementScale;
|
|
92
|
+
const bias = material.displacementBias;
|
|
87
93
|
const rayDir = ray.direction;
|
|
88
94
|
|
|
89
|
-
// Compute UV velocity along the ray: dUV/dt
|
|
90
|
-
// Project ray direction onto surface tangent plane
|
|
91
95
|
const NdotD = dot( rayDir, N ).toVar();
|
|
92
96
|
const rayProj = rayDir.sub( N.mul( NdotD ) ).toVar();
|
|
93
97
|
|
|
94
|
-
// Solve: rayProj = du_dt * T + dv_dt * B
|
|
95
|
-
// Using the Gram matrix inverse of [T, B]
|
|
96
98
|
const TdotT = dot( T, T );
|
|
97
99
|
const TdotB = dot( T, B );
|
|
98
100
|
const BdotB = dot( B, B );
|
|
@@ -100,89 +102,60 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
100
102
|
|
|
101
103
|
If( abs( detJ ).greaterThan( 1e-10 ), () => {
|
|
102
104
|
|
|
105
|
+
// Dual basis of (T, B): dot( Td, T ) = 1, dot( Td, B ) = 0, and likewise Bd.
|
|
103
106
|
const invDetJ = float( 1.0 ).div( detJ );
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
const du_dt = BdotB.mul( rayProjDotT ).sub( TdotB.mul( rayProjDotB ) ).mul( invDetJ );
|
|
108
|
-
const dv_dt = TdotT.mul( rayProjDotB ).sub( TdotB.mul( rayProjDotT ) ).mul( invDetJ );
|
|
109
|
-
const dUV_dt = vec2( du_dt, dv_dt ).toVar();
|
|
110
|
-
|
|
111
|
-
// Ray height change per unit dt: how fast the ray moves along the surface normal
|
|
112
|
-
const dh_ray_dt = NdotD.toVar();
|
|
113
|
-
|
|
114
|
-
// March range: displacement shell extends ±0.5*scale from base surface
|
|
115
|
-
// Compute dt range to traverse the full shell
|
|
116
|
-
const absNdotD = max( abs( dh_ray_dt ), 0.001 );
|
|
117
|
-
const dtShell = scale.div( absNdotD ).toVar();
|
|
107
|
+
const Td = T.mul( BdotB ).sub( B.mul( TdotB ) ).mul( invDetJ ).toVar();
|
|
108
|
+
const Bd = B.mul( TdotT ).sub( T.mul( TdotB ) ).mul( invDetJ ).toVar();
|
|
109
|
+
const dUV_dt = vec2( dot( rayProj, Td ), dot( rayProj, Bd ) ).toVar();
|
|
118
110
|
|
|
119
|
-
//
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
111
|
+
// +1 when the ray arrives from the side N points to.
|
|
112
|
+
const side = select( NdotD.lessThan( 0.0 ), float( 1.0 ), float( - 1.0 ) ).toVar();
|
|
113
|
+
const under = ( dt, height ) => dt.mul( NdotD ).sub( height.mul( scale ).add( bias ) ).mul( side ).lessThanEqual( 0.0 );
|
|
114
|
+
const heightAt = uv => sampleDisplacementMap( material.displacementMapIndex, uv, material.displacementTransform );
|
|
123
115
|
|
|
124
|
-
//
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
const
|
|
116
|
+
// Enter the shell [bias, bias + scale] on the ray's side, never behind the ray's origin.
|
|
117
|
+
const top = max( bias, bias.add( scale ) );
|
|
118
|
+
const bottom = min( bias, bias.add( scale ) );
|
|
119
|
+
const slope = side.negate().mul( max( abs( NdotD ), 0.001 ) );
|
|
120
|
+
const dtStart = max( select( side.greaterThan( 0.0 ), top, bottom ).div( slope ), hitInfo.dst.negate() ).toVar();
|
|
121
|
+
const dtEnd = select( side.greaterThan( 0.0 ), bottom, top ).div( slope ).toVar();
|
|
128
122
|
|
|
129
|
-
//
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
const
|
|
123
|
+
// One step per few texels the ray crosses, so a grazing ray does not step over the gaps.
|
|
124
|
+
const texelsPerUV = uvTransformScale( { transform: material.displacementTransform } )
|
|
125
|
+
.div( bucketTexelSize( sceneResources( builder ).linearBuckets, material.displacementMapIndex ) ).toVar();
|
|
126
|
+
const texelsCrossed = dUV_dt.mul( texelsPerUV ).length().mul( dtEnd.sub( dtStart ) );
|
|
127
|
+
const maxSteps = select( bounceIndex.equal( int( 0 ) ), float( MAX_MARCH_STEPS ), float( MAX_BOUNCE_MARCH_STEPS ) );
|
|
128
|
+
const marchSteps = select( dtEnd.greaterThan( dtStart ),
|
|
129
|
+
int( clamp( ceil( texelsCrossed.mul( STEPS_PER_TEXEL ) ), float( MIN_MARCH_STEPS ), maxSteps ) ), int( 0 ) ).toVar();
|
|
130
|
+
const dtStep = dtEnd.sub( dtStart ).div( max( float( marchSteps ), 1.0 ) ).toVar();
|
|
133
131
|
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
const loDt = dtStart.toVar();
|
|
133
|
+
const hiDt = dtStart.toVar();
|
|
134
|
+
const found = tslBool( false ).toVar();
|
|
136
135
|
|
|
137
|
-
|
|
136
|
+
Loop( { start: int( 1 ), end: marchSteps.add( 1 ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
138
137
|
|
|
139
|
-
|
|
138
|
+
const dt = dtStart.add( dtStep.mul( float( i ) ) ).toVar();
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
const marchUV = hitInfo.uv.add( dUV_dt.mul( dt ) );
|
|
140
|
+
If( under( dt, heightAt( hitInfo.uv.add( dUV_dt.mul( dt ) ) ) ), () => {
|
|
143
141
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// Displaced surface height
|
|
148
|
-
const heightSample = sampleDisplacementMap(
|
|
149
|
-
material.displacementMapIndex, marchUV, material.displacementTransform,
|
|
150
|
-
);
|
|
151
|
-
const surfaceHeight = heightSample.sub( 0.5 ).mul( scale );
|
|
152
|
-
|
|
153
|
-
// Ray crosses below displaced surface → intersection
|
|
154
|
-
If( rayHeight.lessThanEqual( surfaceHeight ).and( i.greaterThan( int( 0 ) ) ), () => {
|
|
155
|
-
|
|
156
|
-
found.assign( 1 );
|
|
157
|
-
currDt.assign( dt );
|
|
158
|
-
|
|
159
|
-
} ).Else( () => {
|
|
160
|
-
|
|
161
|
-
prevDt.assign( dt );
|
|
162
|
-
|
|
163
|
-
} );
|
|
142
|
+
found.assign( true );
|
|
143
|
+
hiDt.assign( dt );
|
|
144
|
+
Break();
|
|
164
145
|
|
|
165
146
|
} );
|
|
166
147
|
|
|
167
|
-
|
|
148
|
+
loDt.assign( dt );
|
|
168
149
|
|
|
169
|
-
|
|
170
|
-
If( found.equal( int( 1 ) ), () => {
|
|
150
|
+
} );
|
|
171
151
|
|
|
172
|
-
|
|
173
|
-
const hiDt = currDt.toVar();
|
|
152
|
+
If( found, () => {
|
|
174
153
|
|
|
175
154
|
Loop( { start: int( 0 ), end: int( BINARY_STEPS ), type: 'int', condition: '<' }, () => {
|
|
176
155
|
|
|
177
156
|
const midDt = loDt.add( hiDt ).mul( 0.5 ).toVar();
|
|
178
|
-
const midUV = hitInfo.uv.add( dUV_dt.mul( midDt ) );
|
|
179
|
-
const midRayHeight = midDt.mul( dh_ray_dt );
|
|
180
|
-
const midSample = sampleDisplacementMap(
|
|
181
|
-
material.displacementMapIndex, midUV, material.displacementTransform,
|
|
182
|
-
);
|
|
183
|
-
const midSurfHeight = midSample.sub( 0.5 ).mul( scale );
|
|
184
157
|
|
|
185
|
-
If(
|
|
158
|
+
If( under( midDt, heightAt( hitInfo.uv.add( dUV_dt.mul( midDt ) ) ) ), () => {
|
|
186
159
|
|
|
187
160
|
hiDt.assign( midDt );
|
|
188
161
|
|
|
@@ -194,40 +167,20 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
194
167
|
|
|
195
168
|
} );
|
|
196
169
|
|
|
197
|
-
// Final intersection
|
|
198
170
|
const finalDt = loDt.add( hiDt ).mul( 0.5 ).toVar();
|
|
199
171
|
const finalUV = hitInfo.uv.add( dUV_dt.mul( finalDt ) ).toVar();
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
const hU = sampleDisplacementMap(
|
|
212
|
-
material.displacementMapIndex, finalUV.add( vec2( texel.x, 0.0 ) ), material.displacementTransform,
|
|
213
|
-
);
|
|
214
|
-
const hV = sampleDisplacementMap(
|
|
215
|
-
material.displacementMapIndex, finalUV.add( vec2( 0.0, texel.y ) ), material.displacementTransform,
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
// Perturb normal using actual tangent/bitangent from UV parameterization
|
|
219
|
-
const Tn = normalize( T ).toVar();
|
|
220
|
-
const Bn = normalize( B ).toVar();
|
|
221
|
-
const gradU = hU.sub( hC ).mul( scale );
|
|
222
|
-
const gradV = hV.sub( hC ).mul( scale );
|
|
223
|
-
const displacedNormal = normalize(
|
|
224
|
-
N.sub( Tn.mul( gradU ) ).sub( Bn.mul( gradV ) )
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
resultHitPoint.assign( finalPoint );
|
|
172
|
+
|
|
173
|
+
// Central differences over a few texels: an 8-bit map's steps make one-texel slopes flicker.
|
|
174
|
+
const step = vec2( SLOPE_TEXELS ).div( texelsPerUV ).toVar();
|
|
175
|
+
const dhdu = heightAt( finalUV.add( vec2( step.x, 0.0 ) ) ).sub( heightAt( finalUV.sub( vec2( step.x, 0.0 ) ) ) ).mul( scale ).div( step.x.mul( 2.0 ) );
|
|
176
|
+
const dhdv = heightAt( finalUV.add( vec2( 0.0, step.y ) ) ).sub( heightAt( finalUV.sub( vec2( 0.0, step.y ) ) ) ).mul( scale ).div( step.y.mul( 2.0 ) );
|
|
177
|
+
resultNormal.assign( normalize( N.sub( Td.mul( dhdu ) ).sub( Bd.mul( dhdv ) ) ) );
|
|
178
|
+
|
|
179
|
+
// A point behind the base triangle goes up onto its plane: rays spawned below it would hit it.
|
|
180
|
+
const faceN = normalize( cross( Td, Bd ) );
|
|
181
|
+
const moveDir = select( finalDt.greaterThan( 0.0 ), rayDir.sub( faceN.mul( dot( rayDir, faceN ) ) ), rayDir );
|
|
182
|
+
resultHitPoint.assign( hitInfo.hitPoint.add( moveDir.mul( finalDt ) ) );
|
|
228
183
|
resultUV.assign( finalUV );
|
|
229
|
-
resultNormal.assign( displacedNormal );
|
|
230
|
-
resultHeight.assign( displacedHeight );
|
|
231
184
|
|
|
232
185
|
} );
|
|
233
186
|
|
|
@@ -235,6 +188,6 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
235
188
|
|
|
236
189
|
} );
|
|
237
190
|
|
|
238
|
-
return DisplacementResult( { hitPoint: resultHitPoint, uv: resultUV, normal: resultNormal
|
|
191
|
+
return DisplacementResult( { hitPoint: resultHitPoint, uv: resultUV, normal: resultNormal } );
|
|
239
192
|
|
|
240
193
|
} );
|
|
@@ -39,7 +39,6 @@ export function buildFinalWriteKernel( params ) {
|
|
|
39
39
|
enableAccumulation, hasPreviousAccumulated, accumulationAlpha, cameraIsMoving,
|
|
40
40
|
// Aux MRT accumulates on its own epoch; hasPreviousAux=0 → write it verbatim instead of mixing.
|
|
41
41
|
hasPreviousAux, auxAccumulationAlpha,
|
|
42
|
-
transparentBackground,
|
|
43
42
|
prevAccumTexture, prevAlbedoTexture, prevNormalDepthTexture,
|
|
44
43
|
renderWidth,
|
|
45
44
|
chunkRowBase, chunkRows, // row band offset (global first row) + row count for this chunk
|
|
@@ -85,7 +84,9 @@ export function buildFinalWriteKernel( params ) {
|
|
|
85
84
|
|
|
86
85
|
const sampleColor = readRayRadiance( rayBufferRO, rayID );
|
|
87
86
|
const finalColor = sampleColor.xyz.toVar();
|
|
88
|
-
|
|
87
|
+
// Coverage, whatever the backdrop: 0 where the camera saw past everything opaque. What shows or reads the
|
|
88
|
+
// image makes it opaque unless the background is transparent; auto exposure leaves a solid colour out by it.
|
|
89
|
+
const outputAlpha = sampleColor.w.toVar();
|
|
89
90
|
|
|
90
91
|
// A pixel frozen in a prior frame was skipped by Generate, so its rayBuffer sample is stale — pass
|
|
91
92
|
// the accumulated colour through unchanged (below) instead of mixing it. Inert unless adaptiveOn.
|
|
@@ -145,11 +146,7 @@ export function buildFinalWriteKernel( params ) {
|
|
|
145
146
|
|
|
146
147
|
} );
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
outputAlpha.assign( select( wasFrozen, prevAccumSample.w, mix( prevAccumSample.w, sampleColor.w, accumulationAlpha ) ) );
|
|
151
|
-
|
|
152
|
-
} );
|
|
149
|
+
outputAlpha.assign( select( wasFrozen, prevAccumSample.w, mix( prevAccumSample.w, sampleColor.w, accumulationAlpha ) ) );
|
|
153
150
|
|
|
154
151
|
} );
|
|
155
152
|
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
getStratifiedSample,
|
|
23
23
|
} from './Random.js';
|
|
24
24
|
|
|
25
|
-
import { generateRayFromCamera } from './CameraRay.js';
|
|
25
|
+
import { generateRayFromCamera, cameraClipDistance } from './CameraRay.js';
|
|
26
26
|
import { Ray } from './Struct.js';
|
|
27
27
|
import { RAY_FLAG, COUNTER } from '../Processor/QueueManager.js';
|
|
28
28
|
import {
|
|
@@ -41,6 +41,7 @@ export function buildGenerateKernel( params ) {
|
|
|
41
41
|
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
42
42
|
cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
|
|
43
43
|
enableDOF, focalLength, aperture, focusDistance, unitsPerMetre, apertureScale, anamorphicRatio, dofMode, dofBlur,
|
|
44
|
+
cameraClipStart,
|
|
44
45
|
renderWidth,
|
|
45
46
|
chunkRowBase, chunkRows, // row band offset (global first row) + row count for this chunk
|
|
46
47
|
transmissiveBounces, // per-ray refraction budget (megakernel parity: PathTracerCore.js:606)
|
|
@@ -79,7 +80,10 @@ export function buildGenerateKernel( params ) {
|
|
|
79
80
|
enableDOF, focalLength, aperture, focusDistance, unitsPerMetre, apertureScale, anamorphicRatio, dofMode, dofBlur,
|
|
80
81
|
) );
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
// The ray starts at the clip plane. The bidirectional weights still measure the first segment from the camera:
|
|
84
|
+
// the skipped length rides in the alpha-skip distance (mis_ray_t), as a cutout's would.
|
|
85
|
+
const clipAlong = cameraClipDistance( ray.direction, cameraClipStart, cameraWorldMatrix, cameraProjection ).toVar();
|
|
86
|
+
writeRayOriginMeta( rayBufferRW, rayID, ray.origin.add( ray.direction.mul( clipAlong ) ), int( 0 ), int( 0 ), uint( 0 ), bdpt ? clipAlong : null );
|
|
83
87
|
// A fresh camera ray is NOT redirected — REDIRECTED stays clear so it sees the direct backdrop;
|
|
84
88
|
// ShadeKernel sets REDIRECTED on the first direction-changing interaction (see RAY_FLAG.REDIRECTED).
|
|
85
89
|
writeRayDirFlags( rayBufferRW, rayID, ray.direction, uint( RAY_FLAG.ACTIVE ) );
|
|
@@ -29,7 +29,7 @@ export function buildLightSplatKernel( params ) {
|
|
|
29
29
|
hitBufferRO, splatBuffer,
|
|
30
30
|
bvhBuffer, triangleBuffer, materialBuffer,
|
|
31
31
|
lightPaths, slotsPerPath, lightTag, strategyView,
|
|
32
|
-
cameraPosition, cameraForward, cameraViewMatrix, cameraProjectionMatrix, pixelArea,
|
|
32
|
+
cameraPosition, cameraForward, cameraClipStart, cameraViewMatrix, cameraProjectionMatrix, pixelArea,
|
|
33
33
|
renderWidth, renderHeight,
|
|
34
34
|
globalIlluminationIntensity, fireflyThreshold, accumFrame, frame,
|
|
35
35
|
mergeVm = null,
|
|
@@ -68,7 +68,10 @@ export function buildLightSplatKernel( params ) {
|
|
|
68
68
|
const culled = diffuseTransmission
|
|
69
69
|
? select( behind, v.extra.bitAnd( uint( 2 ) ), v.extra.bitAnd( uint( 1 ) ) ).notEqual( uint( 0 ) )
|
|
70
70
|
: v.extra.notEqual( uint( 0 ) );
|
|
71
|
-
|
|
71
|
+
// The camera sees nothing in front of its clip plane, this far back along the line to it.
|
|
72
|
+
const clipAlong = cameraClipStart.div( max( cosCamera, 1e-6 ) ).toVar();
|
|
73
|
+
If( ( diffuseTransmission ? cosFacet.equal( 0.0 ) : cosFacet.lessThanEqual( 0.0 ) ).or( cosCamera.lessThanEqual( 1e-6 ) ).or( culled )
|
|
74
|
+
.or( dist2.lessThanEqual( clipAlong.mul( clipAlong ) ) ), () => {
|
|
72
75
|
|
|
73
76
|
Return();
|
|
74
77
|
|
|
@@ -123,9 +126,9 @@ export function buildLightSplatKernel( params ) {
|
|
|
123
126
|
const origin = offsetRayOrigin( v.position, diffuseTransmission ? select( behind, v.facetN.negate(), v.facetN ) : v.facetN ).toVar();
|
|
124
127
|
const segment = cameraPosition.sub( origin ).toVar();
|
|
125
128
|
const segmentLength = length( segment ).toVar();
|
|
126
|
-
// This segment stands in for the primary ray, which sees through the faces it culls.
|
|
129
|
+
// This segment stands in for the primary ray, which sees through the faces it culls and starts at the clip plane.
|
|
127
130
|
const visibility = traceShadowRayRefractiveOpaque(
|
|
128
|
-
origin, segment.div( segmentLength ), segmentLength.mul( SHADOW_END ),
|
|
131
|
+
origin, segment.div( segmentLength ), segmentLength.sub( clipAlong ).mul( SHADOW_END ),
|
|
129
132
|
traverseBVHShadowCameraCulled, bvhBuffer, triangleBuffer, materialBuffer,
|
|
130
133
|
);
|
|
131
134
|
|
package/src/TSL/LightsCore.js
CHANGED
|
@@ -60,6 +60,7 @@ export const PointLight = struct( {
|
|
|
60
60
|
intensity: 'float',
|
|
61
61
|
distance: 'float', // cutoff distance (0 = infinite)
|
|
62
62
|
decay: 'float', // decay exponent (2 = physically correct)
|
|
63
|
+
radius: 'float', // 0 = a point; else a disc of this radius facing the lit point (Cycles' soft falloff)
|
|
63
64
|
} );
|
|
64
65
|
|
|
65
66
|
export const SpotLight = struct( {
|
|
@@ -75,6 +76,7 @@ export const SpotLight = struct( {
|
|
|
75
76
|
goboIntensity: 'float', // mask strength multiplier (0 = no mask, 1 = full mask)
|
|
76
77
|
iesIndex: 'int', // -1 = no IES, otherwise index into iesProfiles array
|
|
77
78
|
iesIntensity: 'float', // blend [0,1] between flat (0) and full IES distribution (1)
|
|
79
|
+
radius: 'float', // as PointLight's
|
|
78
80
|
} );
|
|
79
81
|
|
|
80
82
|
export const LightSample = struct( {
|
|
@@ -173,7 +175,7 @@ export const getAreaLight = Fn( ( [ areaLightsBuffer, index ] ) => {
|
|
|
173
175
|
|
|
174
176
|
export const getPointLight = Fn( ( [ pointLightsBuffer, index ] ) => {
|
|
175
177
|
|
|
176
|
-
const baseIndex = index.mul(
|
|
178
|
+
const baseIndex = index.mul( 10 );
|
|
177
179
|
return PointLight( {
|
|
178
180
|
position: vec3(
|
|
179
181
|
pointLightsBuffer.element( baseIndex ),
|
|
@@ -188,6 +190,7 @@ export const getPointLight = Fn( ( [ pointLightsBuffer, index ] ) => {
|
|
|
188
190
|
intensity: pointLightsBuffer.element( baseIndex.add( 6 ) ),
|
|
189
191
|
distance: pointLightsBuffer.element( baseIndex.add( 7 ) ),
|
|
190
192
|
decay: pointLightsBuffer.element( baseIndex.add( 8 ) ),
|
|
193
|
+
radius: pointLightsBuffer.element( baseIndex.add( 9 ) ),
|
|
191
194
|
} );
|
|
192
195
|
|
|
193
196
|
} );
|
|
@@ -220,7 +223,8 @@ export const getSpotLight = Fn( ( [ spotLightsBuffer, index ] ) => {
|
|
|
220
223
|
goboIntensity: spotLightsBuffer.element( baseIndex.add( 15 ) ),
|
|
221
224
|
iesIndex: int( spotLightsBuffer.element( baseIndex.add( 16 ) ) ),
|
|
222
225
|
iesIntensity: spotLightsBuffer.element( baseIndex.add( 17 ) ),
|
|
223
|
-
|
|
226
|
+
radius: spotLightsBuffer.element( baseIndex.add( 18 ) ),
|
|
227
|
+
// slot 19 reserved (vec4 padding)
|
|
224
228
|
} );
|
|
225
229
|
|
|
226
230
|
} );
|
package/src/TSL/LightsDirect.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Direct lighting calculations including shadow ray tracing
|
|
3
3
|
// and contribution calculations for all light types.
|
|
4
4
|
|
|
5
|
-
import { sceneResources } from './SceneResources.js';
|
|
5
|
+
import { sceneResources, usesTransmissionMaps } from './SceneResources.js';
|
|
6
6
|
import {
|
|
7
7
|
Fn,
|
|
8
8
|
float,
|
|
@@ -30,6 +30,7 @@ import { Ray, ShadowMaterial, HitInfo } from './Struct.js';
|
|
|
30
30
|
import {
|
|
31
31
|
REC709_LUMINANCE_COEFFICIENTS, getShadowMaterial, instanceRows,
|
|
32
32
|
instanceFaceNormalToWorld, triangleRow, shadowFlagsSettle, offsetRayOrigin,
|
|
33
|
+
getDatafromStorageBuffer, MATERIAL_SLOT, MATERIAL_SLOTS,
|
|
33
34
|
} from './Common.js';
|
|
34
35
|
import { fresnelDielectric } from './Fresnel.js';
|
|
35
36
|
import { calculateBeerLawAbsorption } from './MaterialTransmission.js';
|
|
@@ -49,7 +50,8 @@ const makeTraceShadowRay = ( refractiveBlocks ) => Fn( ( [
|
|
|
49
50
|
materialBuffer,
|
|
50
51
|
], builder ) => {
|
|
51
52
|
|
|
52
|
-
const { alphaShadows, shadowAlbedoMaps } = sceneResources( builder );
|
|
53
|
+
const { alphaShadows, shadowAlbedoMaps, linearBuckets } = sceneResources( builder );
|
|
54
|
+
const transmissionMaps = ! refractiveBlocks && usesTransmissionMaps( builder );
|
|
53
55
|
const transmittance = float( 1.0 ).toVar();
|
|
54
56
|
const rayOrigin = origin.toVar();
|
|
55
57
|
const remainingDist = float( maxDist ).toVar();
|
|
@@ -88,6 +90,19 @@ const makeTraceShadowRay = ( refractiveBlocks ) => Fn( ( [
|
|
|
88
90
|
// Fetch material for the hit surface (thin reader: 7 slots instead of 27)
|
|
89
91
|
const shadowMaterial = ShadowMaterial.wrap( getShadowMaterial( shadowHit.materialIndex, materialBuffer ) );
|
|
90
92
|
|
|
93
|
+
// The hit's texture coordinates through the albedo transform, which Shade reads the extension maps with too.
|
|
94
|
+
const hitTextureUV = () => {
|
|
95
|
+
|
|
96
|
+
const baryU = shadowHit.uv.x;
|
|
97
|
+
const baryV = shadowHit.uv.y;
|
|
98
|
+
const baryW = float( 1.0 ).sub( baryU ).sub( baryV );
|
|
99
|
+
const uvData1 = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 3 ) );
|
|
100
|
+
const uvData2 = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 4 ).xy );
|
|
101
|
+
const hitUV = uvData1.xy.mul( baryW ).add( uvData1.zw.mul( baryU ) ).add( uvData2.mul( baryV ) );
|
|
102
|
+
return getTransformedUV( { uv: hitUV, transform: shadowMaterial.albedoTransform } );
|
|
103
|
+
|
|
104
|
+
};
|
|
105
|
+
|
|
91
106
|
// ---------------------------------------------------------------
|
|
92
107
|
// Alpha-cutout handling (MASK / BLEND with albedo texture alpha)
|
|
93
108
|
// Gated by runtime uniform + alphaMode check — zero overhead for opaque materials.
|
|
@@ -106,14 +121,7 @@ const makeTraceShadowRay = ( refractiveBlocks ) => Fn( ( [
|
|
|
106
121
|
|
|
107
122
|
If( shadowMaterial.albedoMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
108
123
|
|
|
109
|
-
|
|
110
|
-
const baryV = shadowHit.uv.y;
|
|
111
|
-
const baryW = float( 1.0 ).sub( baryU ).sub( baryV );
|
|
112
|
-
const uvData1 = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 3 ) );
|
|
113
|
-
const uvData2 = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 4 ).xy );
|
|
114
|
-
const hitUV = uvData1.xy.mul( baryW ).add( uvData1.zw.mul( baryU ) ).add( uvData2.mul( baryV ) );
|
|
115
|
-
const albedoUV = getTransformedUV( { uv: hitUV, transform: shadowMaterial.albedoTransform } );
|
|
116
|
-
texAlpha.assign( sampleBucket( shadowAlbedoMaps, shadowMaterial.albedoMapIndex, albedoUV ).a );
|
|
124
|
+
texAlpha.assign( sampleBucket( shadowAlbedoMaps, shadowMaterial.albedoMapIndex, hitTextureUV() ).a );
|
|
117
125
|
|
|
118
126
|
} );
|
|
119
127
|
|
|
@@ -167,6 +175,20 @@ const makeTraceShadowRay = ( refractiveBlocks ) => Fn( ( [
|
|
|
167
175
|
|
|
168
176
|
} : () => {
|
|
169
177
|
|
|
178
|
+
let transmission = shadowMaterial.transmission;
|
|
179
|
+
if ( transmissionMaps ) {
|
|
180
|
+
|
|
181
|
+
// glTF's transmission is the factor × the map's R: where the map is black the surface is opaque.
|
|
182
|
+
transmission = transmission.toVar();
|
|
183
|
+
const mapIndex = int( getDatafromStorageBuffer( materialBuffer, shadowHit.materialIndex, int( MATERIAL_SLOT.EXT_MAP_INDICES_A ), int( MATERIAL_SLOTS ) ).r ).toVar();
|
|
184
|
+
If( mapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
185
|
+
|
|
186
|
+
transmission.mulAssign( sampleBucket( linearBuckets, mapIndex, hitTextureUV() ).r );
|
|
187
|
+
|
|
188
|
+
} );
|
|
189
|
+
|
|
190
|
+
}
|
|
191
|
+
|
|
170
192
|
// Deferred geometric-normal compute — refetch triangle positions and
|
|
171
193
|
// derive the normal here so opaque/alpha-cutout shadow hits don't pay
|
|
172
194
|
// the cross+normalize cost in BVH traversal.
|
|
@@ -204,7 +226,7 @@ const makeTraceShadowRay = ( refractiveBlocks ) => Fn( ( [
|
|
|
204
226
|
// Shadow rays go straight through, so both faces see the outside-in angle and eta.
|
|
205
227
|
const fresnel = fresnelDielectric( abs( dot( dir, N ) ), max( shadowMaterial.ior, 1.0 ) );
|
|
206
228
|
|
|
207
|
-
const matTransmittance = float( 1.0 ).sub( fresnel ).mul(
|
|
229
|
+
const matTransmittance = float( 1.0 ).sub( fresnel ).mul( transmission );
|
|
208
230
|
transmittance.mulAssign( matTransmittance );
|
|
209
231
|
|
|
210
232
|
// Early exit if almost no light passes through
|