rayzee 11.6.0 → 11.8.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 +26 -14
- package/dist/{ArchiveImporter-iofnC8zB.js → ArchiveImporter-_sJm5uaK.js} +1923 -1243
- package/dist/{BidirectionalIntegrator-CqAilt2I.js → BidirectionalIntegrator-Dzt4Z97x.js} +290 -289
- package/dist/{ColorManagement-oeEk7oSu.js → ColorManagement-30MfpHwQ.js} +1 -1
- package/dist/{EngineIssues-CIZnXHsF.js → EngineIssues-xljoXM0E.js} +18 -8
- package/dist/{NeuralRetouch-DFI4pOjO.js → NeuralRetouch-BKQCsmEs.js} +2 -2
- package/dist/{NeuralSuperRes-ChCiiilS.js → NeuralSuperRes-C4ymq68e.js} +2 -2
- package/dist/{PathTracer-BX4sJLx7.js → PathTracer-C_to_i40.js} +3158 -3102
- package/dist/{PhysicalSky-Biv39eTA.js → PhysicalSky-bMHbm_7H.js} +1 -1
- package/dist/{RayzeeRenderer-D8XRKU3A.js → RayzeeRenderer-B_aB6VTJ.js} +370 -372
- package/dist/SceneMetadata-CSmlJW4c.js +183 -0
- 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-B085D9FR.js} +1 -1
- package/dist/rayzee-core.es.js +5 -5
- package/dist/rayzee.es.js +917 -1624
- package/dist/rayzee.umd.js +113 -156
- package/dist/{sceneCachePolicy-W5PLbzuR.js → sceneCachePolicy-D_uBSbQF.js} +2 -2
- package/package.json +1 -1
- package/src/EngineIssues.js +20 -6
- package/src/Passes/AIUpscaler.js +4 -0
- package/src/Passes/OIDNDenoiser.js +17 -17
- package/src/PathTracerApp.js +16 -18
- package/src/Pipeline/BindingAudit.js +2 -3
- package/src/Pipeline/PipelineContext.js +0 -1
- package/src/Pipeline/RenderPipeline.js +1 -2
- package/src/Pipeline/RenderStage.js +1 -1
- package/src/Processor/ArchiveImporter.js +23 -6
- package/src/Processor/AssetLoader.js +15 -0
- package/src/Processor/LightSerializer.js +6 -2
- package/src/Processor/PBRT/PBRTMaterials.js +2 -1
- package/src/Processor/PBRT/PBRTSceneBuilder.js +1 -1
- package/src/Processor/PackedRayBuffer.js +2 -2
- package/src/Processor/PlatformImageLoader.js +9 -0
- package/src/Processor/StorageTexturePool.js +1 -1
- 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 +82 -27
- package/src/Stages/AutoExposure.js +21 -8
- package/src/Stages/Compositor.js +39 -1
- package/src/Stages/DenoiserSettings.js +5 -81
- package/src/Stages/LocalExposure.js +44 -18
- package/src/Stages/MotionVector.js +21 -215
- package/src/Stages/NormalDepth.js +12 -8
- package/src/Stages/PathTracer.js +26 -2
- package/src/Stages/PathTracerStage.js +13 -18
- package/src/TSL/CameraRay.js +12 -1
- package/src/TSL/DebugKernel.js +3 -2
- package/src/TSL/FinalWriteKernel.js +6 -9
- package/src/TSL/GenerateKernel.js +6 -2
- package/src/TSL/LightSplatKernel.js +7 -4
- package/src/TSL/LightsCore.js +6 -2
- package/src/TSL/LightsSampling.js +45 -12
- package/src/TSL/SceneResources.js +11 -0
- package/src/TSL/ShadeKernel.js +4 -5
- package/src/index.js +1 -1
- package/src/integrators/BidirectionalIntegrator.js +1 -0
- package/src/managers/CameraManager.js +9 -0
- package/src/managers/DenoisingManager.js +79 -187
- package/src/managers/InteractionManager.js +6 -0
- package/src/managers/UniformManager.js +3 -1
- package/dist/SceneMetadata-D2Sh-0e-.js +0 -110
- package/src/Stages/ASVGF.js +0 -932
- package/src/Stages/BilateralFilter.js +0 -486
- package/src/Stages/EdgeFilter.js +0 -545
- package/src/Stages/Variance.js +0 -443
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,
|
|
@@ -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
|
|
@@ -48,7 +47,7 @@ export function buildFinalWriteKernel( params ) {
|
|
|
48
47
|
// on): when off, skip the G-buffer decode, the prev-frame aux mix, and the two aux stores.
|
|
49
48
|
auxGBufferEnabled, gBuffer = true, // compiled in only when requested (PathTracer.requestOutput( 'gBuffer' ))
|
|
50
49
|
// Clean-aux normal (1 = temporally accumulate + renormalize the aux normal). On only for clean-aux
|
|
51
|
-
// OIDN models (calb_cnrm/high, alb_nrm/balanced); off
|
|
50
|
+
// OIDN models (calb_cnrm/high, alb_nrm/balanced); off otherwise, as the fast model wants the bump normal.
|
|
52
51
|
cleanAuxNormalEnabled,
|
|
53
52
|
// Tier-1 convergence early-stop: per-pixel Welford luminance second moment + converged bit (stamped
|
|
54
53
|
// into streak bit 31; counted with 3×3 erosion by countConvergedDilated at the next frame start).
|
|
@@ -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.
|
|
@@ -126,7 +127,7 @@ export function buildFinalWriteKernel( params ) {
|
|
|
126
127
|
finalHitDist.assign( mix( prevAlbedoSample.w, finalHitDist, auxAccumulationAlpha ) );
|
|
127
128
|
|
|
128
129
|
// NORMAL: by default keep this frame's POINT-SAMPLED normal — it varies with the bump,
|
|
129
|
-
// which fast
|
|
130
|
+
// which the fast model wants to preserve edge detail. But a CLEAN-AUX OIDN model (calb_cnrm/high,
|
|
130
131
|
// alb_nrm/balanced) trusts the aux and is fed per-frame point-sampled NOISE → leaked noise
|
|
131
132
|
// (high) / over-smoothing (balanced). When cleanAuxNormalOn, temporally accumulate it like
|
|
132
133
|
// the colour. Average the RAW unit normals (decode 0.5+0.5 → [-1,1]) and RENORMALIZE — a
|
|
@@ -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
|
} );
|
|
@@ -86,7 +86,9 @@ import {
|
|
|
86
86
|
offsetRayOrigin,
|
|
87
87
|
SHADOW_END,
|
|
88
88
|
REC709_LUMINANCE_COEFFICIENTS,
|
|
89
|
+
constructTBN,
|
|
89
90
|
} from './Common.js';
|
|
91
|
+
import { usesLightRadius } from './SceneResources.js';
|
|
90
92
|
import { sampleEnvironment, sampleEnvironmentExact } from './Environment.js';
|
|
91
93
|
import { shadowTerminatorOrigin } from './ShadowTerminator.js';
|
|
92
94
|
import { sunRadianceToward, sampleSunDisc } from './Sun.js';
|
|
@@ -233,8 +235,30 @@ export const sampleRectAreaLight = Fn( ( [ light, rayOrigin, ruv, lightSelection
|
|
|
233
235
|
|
|
234
236
|
} );
|
|
235
237
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
+
/**
|
|
239
|
+
* A point or spot light with a radius is a disc of it facing the lit point, as Cycles' with Soft Falloff: uniform on the
|
|
240
|
+
* disc, so on its axis the light gives intensity / ( d² + r² ) where a point gives intensity / d². Moves `toLight` and
|
|
241
|
+
* `distSq` to the sampled point and returns the disc's tilt toward it. No BSDF ray hits these lights, so no MIS.
|
|
242
|
+
*/
|
|
243
|
+
const aimAtLightDisc = ( light, toLight, distSq, ruv ) => {
|
|
244
|
+
|
|
245
|
+
const facing = float( 1.0 ).toVar();
|
|
246
|
+
If( light.radius.greaterThan( 0.0 ), () => {
|
|
247
|
+
|
|
248
|
+
const axis = toLight.div( sqrt( distSq ) ).toVar();
|
|
249
|
+
const r = light.radius.mul( sqrt( ruv.x ) );
|
|
250
|
+
const phi = ruv.y.mul( 2 * Math.PI );
|
|
251
|
+
toLight.addAssign( constructTBN( { N: axis } ).mul( vec3( r.mul( cos( phi ) ), r.mul( sin( phi ) ), 0.0 ) ) );
|
|
252
|
+
distSq.assign( dot( toLight, toLight ) );
|
|
253
|
+
facing.assign( max( dot( toLight, axis ).div( sqrt( distSq ) ), 0.0 ) );
|
|
254
|
+
|
|
255
|
+
} );
|
|
256
|
+
return facing;
|
|
257
|
+
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// Spot light sampling; a radius makes it a disc (aimAtLightDisc), its cone still read from the centre.
|
|
261
|
+
export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectionPdf, ruv ], builder ) => {
|
|
238
262
|
|
|
239
263
|
const ls_valid = tslBool( false ).toVar();
|
|
240
264
|
const ls_direction = vec3( 0.0, 1.0, 0.0 ).toVar();
|
|
@@ -250,11 +274,16 @@ export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectio
|
|
|
250
274
|
// Guard against zero distance
|
|
251
275
|
If( lightDistSq.greaterThanEqual( 1e-20 ), () => {
|
|
252
276
|
|
|
277
|
+
// Compiled in only while some light has a radius (SceneResources lightRadius); otherwise the exact point code.
|
|
278
|
+
const sized = usesLightRadius( builder );
|
|
279
|
+
const centerDir = sized ? toLight.div( sqrt( lightDistSq ) ).toVar() : null;
|
|
280
|
+
const facing = sized ? aimAtLightDisc( light, toLight, lightDistSq, ruv ) : null;
|
|
253
281
|
const lightDist = sqrt( lightDistSq ).toVar();
|
|
254
282
|
const lightDir = toLight.div( lightDist ).toVar();
|
|
283
|
+
const coneDir = sized ? centerDir : lightDir;
|
|
255
284
|
|
|
256
285
|
// Check cone attenuation
|
|
257
|
-
const spotCosAngle = dot(
|
|
286
|
+
const spotCosAngle = dot( coneDir.negate(), light.direction ).toVar();
|
|
258
287
|
const coneCosAngle = cos( light.angle ).toVar();
|
|
259
288
|
|
|
260
289
|
ls_direction.assign( lightDir );
|
|
@@ -268,10 +297,11 @@ export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectio
|
|
|
268
297
|
const distanceAttenuation = getDistanceAttenuation( { lightDistance: lightDist, cutoffDistance: light.distance, decayExponent: light.decay } );
|
|
269
298
|
|
|
270
299
|
// Gobo projection mask + IES photometric profile — both 1.0 when not assigned.
|
|
271
|
-
const goboMask = sampleSpotGoboMask( light,
|
|
272
|
-
const iesProfile = sampleIESProfile( light,
|
|
300
|
+
const goboMask = sampleSpotGoboMask( light, coneDir );
|
|
301
|
+
const iesProfile = sampleIESProfile( light, coneDir );
|
|
273
302
|
|
|
274
|
-
|
|
303
|
+
const emission = light.color.mul( light.intensity ).mul( distanceAttenuation ).mul( coneAttenuation ).mul( goboMask ).mul( iesProfile );
|
|
304
|
+
ls_emission.assign( sized ? emission.mul( facing ) : emission );
|
|
275
305
|
|
|
276
306
|
} );
|
|
277
307
|
|
|
@@ -290,7 +320,7 @@ export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectio
|
|
|
290
320
|
} );
|
|
291
321
|
|
|
292
322
|
// Enhanced point light sampling with distance attenuation
|
|
293
|
-
export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSelectionPdf ] ) => {
|
|
323
|
+
export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSelectionPdf, ruv ], builder ) => {
|
|
294
324
|
|
|
295
325
|
const ls_valid = tslBool( false ).toVar();
|
|
296
326
|
const ls_direction = vec3( 0.0, 1.0, 0.0 ).toVar();
|
|
@@ -306,6 +336,8 @@ export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSe
|
|
|
306
336
|
// Guard against zero distance
|
|
307
337
|
If( lightDistSq.greaterThanEqual( 1e-20 ), () => {
|
|
308
338
|
|
|
339
|
+
const sized = usesLightRadius( builder );
|
|
340
|
+
const facing = sized ? aimAtLightDisc( light, toLight, lightDistSq, ruv ) : null;
|
|
309
341
|
const lightDist = sqrt( lightDistSq ).toVar();
|
|
310
342
|
const lightDir = toLight.div( lightDist );
|
|
311
343
|
|
|
@@ -315,7 +347,8 @@ export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSe
|
|
|
315
347
|
ls_lightType.assign( int( LIGHT_TYPE_POINT ) );
|
|
316
348
|
ls_direction.assign( lightDir );
|
|
317
349
|
ls_distance.assign( lightDist );
|
|
318
|
-
|
|
350
|
+
const emission = light.color.mul( light.intensity ).mul( distanceAttenuation );
|
|
351
|
+
ls_emission.assign( sized ? emission.mul( facing ) : emission );
|
|
319
352
|
ls_pdf.assign( lightSelectionPdf );
|
|
320
353
|
ls_valid.assign( tslBool( true ) );
|
|
321
354
|
|
|
@@ -614,7 +647,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
614
647
|
|
|
615
648
|
If( light.intensity.greaterThan( 0.0 ), () => {
|
|
616
649
|
|
|
617
|
-
const ptSample = LightSample.wrap( samplePointLightWithAttenuation( light, rayOrigin, lightSelectionPdf ) );
|
|
650
|
+
const ptSample = LightSample.wrap( samplePointLightWithAttenuation( light, rayOrigin, lightSelectionPdf, vec2( randomSeed.y, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 15 ) ), rngState, resolution, frame ) ) ) );
|
|
618
651
|
r_valid.assign( ptSample.valid );
|
|
619
652
|
r_direction.assign( ptSample.direction );
|
|
620
653
|
r_emission.assign( ptSample.emission );
|
|
@@ -639,7 +672,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
639
672
|
|
|
640
673
|
If( light.intensity.greaterThan( 0.0 ), () => {
|
|
641
674
|
|
|
642
|
-
const spotSample = LightSample.wrap( sampleSpotLightWithRadius( light, rayOrigin, lightSelectionPdf ) );
|
|
675
|
+
const spotSample = LightSample.wrap( sampleSpotLightWithRadius( light, rayOrigin, lightSelectionPdf, vec2( randomSeed.y, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 15 ) ), rngState, resolution, frame ) ) ) );
|
|
643
676
|
r_valid.assign( spotSample.valid );
|
|
644
677
|
r_direction.assign( spotSample.direction );
|
|
645
678
|
r_emission.assign( spotSample.emission );
|
|
@@ -702,7 +735,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
702
735
|
If( selectedType.equal( int( 2 ) ).and( selectedIdx.greaterThanEqual( int( 0 ) ) ), () => {
|
|
703
736
|
|
|
704
737
|
const light = PointLight.wrap( getPointLight( pointLightsBuffer, selectedIdx ) );
|
|
705
|
-
const ptSample = LightSample.wrap( samplePointLightWithAttenuation( light, rayOrigin, pdf ) );
|
|
738
|
+
const ptSample = LightSample.wrap( samplePointLightWithAttenuation( light, rayOrigin, pdf, vec2( randomSeed.y, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 15 ) ), rngState, resolution, frame ) ) ) );
|
|
706
739
|
r_valid.assign( ptSample.valid );
|
|
707
740
|
r_direction.assign( ptSample.direction );
|
|
708
741
|
r_emission.assign( ptSample.emission );
|
|
@@ -716,7 +749,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
716
749
|
If( selectedType.equal( int( 3 ) ).and( selectedIdx.greaterThanEqual( int( 0 ) ) ), () => {
|
|
717
750
|
|
|
718
751
|
const light = SpotLight.wrap( getSpotLight( spotLightsBuffer, selectedIdx ) );
|
|
719
|
-
const spotSample = LightSample.wrap( sampleSpotLightWithRadius( light, rayOrigin, pdf ) );
|
|
752
|
+
const spotSample = LightSample.wrap( sampleSpotLightWithRadius( light, rayOrigin, pdf, vec2( randomSeed.y, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 15 ) ), rngState, resolution, frame ) ) ) );
|
|
720
753
|
r_valid.assign( spotSample.valid );
|
|
721
754
|
r_direction.assign( spotSample.direction );
|
|
722
755
|
r_emission.assign( spotSample.emission );
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* @property {?Node} iesProfiles - IES profile texture array, or null
|
|
13
13
|
* @property {?Node} alphaShadows - int uniform, 1 = shadow rays test texture alpha; null = never
|
|
14
14
|
* @property {?Object<string, boolean>} materialLayers - the MATERIAL_LAYERS some material uses; null = all of them
|
|
15
|
+
* @property {boolean} [lightRadius] - some point or spot light has a radius (path tracing only); false = all are points
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import { context } from 'three/tsl';
|
|
@@ -49,3 +50,13 @@ export function materialLayers( builder ) {
|
|
|
49
50
|
return builder.context[ KEY ]?.materialLayers ?? ALL_MATERIAL_LAYERS;
|
|
50
51
|
|
|
51
52
|
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Whether the kernel being built samples light radii. Off unless a point or spot light has a radius, and off for the
|
|
56
|
+
* bidirectional integrator, whose light paths leave a light's centre. Outside a scene kernel (a test) it is on.
|
|
57
|
+
*/
|
|
58
|
+
export function usesLightRadius( builder ) {
|
|
59
|
+
|
|
60
|
+
return builder.context[ KEY ]?.lightRadius ?? true;
|
|
61
|
+
|
|
62
|
+
}
|
package/src/TSL/ShadeKernel.js
CHANGED
|
@@ -125,7 +125,7 @@ export function buildShadeKernel( params ) {
|
|
|
125
125
|
emissiveBoost, enableEmissiveTriangleSampling,
|
|
126
126
|
lightBVHNodeCount, reverseMapVec4Offset,
|
|
127
127
|
maxRayCount,
|
|
128
|
-
// Aux G-buffer (normal/depth/albedo
|
|
128
|
+
// Aux G-buffer (normal/depth/albedo) feeds only the denoiser/OIDN MRT. Compiled in only when
|
|
129
129
|
// requested (PathTracer.requestOutput( 'gBuffer' )); then a live uniform (1 = denoiser on) skips the writes
|
|
130
130
|
// while nothing consumes them.
|
|
131
131
|
auxGBufferEnabled, gBuffer = true,
|
|
@@ -289,7 +289,7 @@ export function buildShadeKernel( params ) {
|
|
|
289
289
|
// This bounce's block of sampler dimensions — see the budget table in Random.js.
|
|
290
290
|
const dimBase = int( currentBounce ).mul( int( SAMPLER_DIMS_PER_BOUNCE ) );
|
|
291
291
|
|
|
292
|
-
// DDFA see-through aux tint carried across smooth glass/mirror; committed into the OIDN/
|
|
292
|
+
// DDFA see-through aux tint carried across smooth glass/mirror; committed into the OIDN/NRD
|
|
293
293
|
// albedo guide at the first diffuse-enough surface (or env). Mutated locally, persisted on every
|
|
294
294
|
// deferring continue (a missed persist = stale tint). 1 = untinted (direct hit → today's albedo).
|
|
295
295
|
const featCarry = aux ? readFeatureThroughput( rayBufferRW, rayID ).toVar() : null;
|
|
@@ -424,7 +424,7 @@ export function buildShadeKernel( params ) {
|
|
|
424
424
|
|
|
425
425
|
// The catcher is a real ground surface for the denoiser — write the plane's normal/depth
|
|
426
426
|
// + a neutral albedo (black albedo would break OIDN demodulation) and mark the pixel a
|
|
427
|
-
// valid surface, so OIDN/
|
|
427
|
+
// valid surface, so OIDN/NRD don't smear the caught shadow as a background miss.
|
|
428
428
|
if ( aux ) If( aux, () => {
|
|
429
429
|
|
|
430
430
|
const planeDepth = computeNDCDepth( { worldPos: planePoint, cameraProjectionMatrix, cameraViewMatrix } );
|
|
@@ -653,8 +653,7 @@ export function buildShadeKernel( params ) {
|
|
|
653
653
|
// surface that escapes to env keeps alpha 1 (HAS_HIT_OPAQUE set), so glass-in-front-of-an-
|
|
654
654
|
// object stays opaque while glass-in-front-of-sky exports see-through.
|
|
655
655
|
// Ungated (was transparentBackground-only) so the alpha lane always carries coverage: it is the
|
|
656
|
-
// only hit/miss signal valid at FinalWrite
|
|
657
|
-
// when transparent-bg is off.
|
|
656
|
+
// only hit/miss signal valid at FinalWrite, which accumulates it as is.
|
|
658
657
|
If( flags.bitAnd( uint( RAY_FLAG.HAS_HIT_OPAQUE ) ).equal( uint( 0 ) ), () => {
|
|
659
658
|
|
|
660
659
|
currentRadiance.w.assign( 0.0 );
|
package/src/index.js
CHANGED
|
@@ -63,7 +63,7 @@ export {
|
|
|
63
63
|
} from './EngineDefaults.js';
|
|
64
64
|
export { MAX_RESERVABLE_RENDER_SIZE } from './Processor/StorageTexturePool.js';
|
|
65
65
|
export {
|
|
66
|
-
DENOISER_DEFAULTS,
|
|
66
|
+
DENOISER_DEFAULTS, DENOISER_STRATEGIES, NRD_DEFAULTS, NRD_QUALITY_PRESETS, NRD_PRESET_KEYS, NRD_HIT_DIST_A,
|
|
67
67
|
NRD_HIT_DIST_B,
|
|
68
68
|
} from './Stages/DenoiserSettings.js';
|
|
69
69
|
export { SKY_DEFAULTS, DEFAULT_SUN_PATH } from './managers/EnvironmentManager.js';
|
|
@@ -336,6 +336,7 @@ export class BidirectionalIntegrator {
|
|
|
336
336
|
strategyView: bd.strategyView,
|
|
337
337
|
cameraPosition: bd.cameraPosition,
|
|
338
338
|
cameraForward: bd.cameraForward,
|
|
339
|
+
cameraClipStart: this.pt.cameraClipStart,
|
|
339
340
|
cameraViewMatrix: this.pt.cameraViewMatrix,
|
|
340
341
|
cameraProjectionMatrix: this.pt.cameraProjectionMatrix,
|
|
341
342
|
pixelArea: bd.pixelArea,
|
|
@@ -93,6 +93,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
93
93
|
|
|
94
94
|
this.cameras = cameras;
|
|
95
95
|
this._userCameraCounter = 0;
|
|
96
|
+
this.camera.userData.clipStart = 0;
|
|
96
97
|
this.resetAutoFocus();
|
|
97
98
|
|
|
98
99
|
}
|
|
@@ -120,6 +121,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
120
121
|
cam.userData.__rayzeeUserCamera = true;
|
|
121
122
|
cam.userData.__rayzeeEffects = this._captureEffects();
|
|
122
123
|
if ( this.controls ) cam.userData.__rayzeeOrbitTarget = this.controls.target.clone();
|
|
124
|
+
if ( src.userData.clipStart > 0 ) cam.userData.clipStart = src.userData.clipStart;
|
|
123
125
|
|
|
124
126
|
this._userCameraCounter += 1;
|
|
125
127
|
cam.name = name || `View Camera ${this._userCameraCounter}`;
|
|
@@ -310,6 +312,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
310
312
|
this.camera.fov = s.fov;
|
|
311
313
|
this.camera.near = s.near;
|
|
312
314
|
this.camera.far = s.far;
|
|
315
|
+
this.camera.userData.clipStart = 0;
|
|
313
316
|
this.camera.updateProjectionMatrix();
|
|
314
317
|
this.camera.updateMatrixWorld( true );
|
|
315
318
|
|
|
@@ -380,6 +383,8 @@ export class CameraManager extends EventDispatcher {
|
|
|
380
383
|
if ( source.isPerspectiveCamera ) this.camera.fov = source.fov;
|
|
381
384
|
this.camera.near = source.near;
|
|
382
385
|
this.camera.far = source.far;
|
|
386
|
+
// Only an imported camera's clip start clips the render: see PathTracerStage._updateCameraUniforms.
|
|
387
|
+
this.camera.userData.clipStart = source.userData?.clipStart ?? 0;
|
|
383
388
|
this.camera.updateProjectionMatrix();
|
|
384
389
|
this.camera.updateMatrixWorld( true );
|
|
385
390
|
|
|
@@ -611,6 +616,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
611
616
|
fov: camera.fov,
|
|
612
617
|
near: camera.near,
|
|
613
618
|
far: camera.far,
|
|
619
|
+
clipStart: camera.userData.clipStart ?? 0,
|
|
614
620
|
zoom: camera.zoom,
|
|
615
621
|
orthographic: !! camera.orthographic,
|
|
616
622
|
orthoHalfHeight: camera.orthoHalfHeight,
|
|
@@ -641,6 +647,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
641
647
|
aspect: cam.aspect ?? null,
|
|
642
648
|
near: cam.near,
|
|
643
649
|
far: cam.far,
|
|
650
|
+
clipStart: cam.userData.clipStart ?? 0,
|
|
644
651
|
zoom: cam.zoom,
|
|
645
652
|
position: cam.position.toArray(),
|
|
646
653
|
quaternion: cam.quaternion.toArray(),
|
|
@@ -684,6 +691,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
684
691
|
cam.updateMatrixWorld( true );
|
|
685
692
|
cam.userData.__rayzeeUserCamera = true;
|
|
686
693
|
cam.userData.__rayzeeEffects = fromPortable( entry.effects );
|
|
694
|
+
if ( entry.clipStart > 0 ) cam.userData.clipStart = entry.clipStart;
|
|
687
695
|
if ( entry.orbitTarget ) cam.userData.__rayzeeOrbitTarget = new Vector3().fromArray( entry.orbitTarget );
|
|
688
696
|
indexMap.set( entry.index, this.cameras.length );
|
|
689
697
|
this.cameras.push( cam );
|
|
@@ -730,6 +738,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
730
738
|
camera.fov = v.fov;
|
|
731
739
|
camera.near = v.near;
|
|
732
740
|
camera.far = v.far;
|
|
741
|
+
camera.userData.clipStart = v.clipStart ?? 0;
|
|
733
742
|
this._setView( v.orthographic, v.orthoHalfHeight );
|
|
734
743
|
camera.zoom = v.zoom ?? 1;
|
|
735
744
|
camera.updateProjectionMatrix();
|