rayzee 11.8.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 +18 -6
- package/dist/{ArchiveImporter-_sJm5uaK.js → ArchiveImporter-BJxszU_k.js} +1549 -1232
- package/dist/{BidirectionalIntegrator-Dzt4Z97x.js → BidirectionalIntegrator-DWg66vAd.js} +240 -240
- package/dist/{BufferLayout-xPRH9jYD.js → BufferLayout-Bzgccg1o.js} +1 -0
- package/dist/{ColorManagement-30MfpHwQ.js → ColorManagement-B_HjAiy2.js} +1 -1
- package/dist/{EngineIssues-xljoXM0E.js → EngineIssues-CfJiBaNj.js} +1 -0
- package/dist/{NeuralRetouch-BKQCsmEs.js → NeuralRetouch-BD9E-Yht.js} +1 -1
- package/dist/{NeuralSuperRes-C4ymq68e.js → NeuralSuperRes-C8Mf3tJX.js} +1 -1
- package/dist/{PathTracer-C_to_i40.js → PathTracer-DeE9UJ1n.js} +3433 -3363
- package/dist/{PhysicalSky-bMHbm_7H.js → PhysicalSky-cKy75-oR.js} +45 -45
- package/dist/{RayzeeRenderer-B_aB6VTJ.js → RayzeeRenderer-CzLyBA3G.js} +13 -8
- package/dist/{SceneMetadata-CSmlJW4c.js → SceneMetadata-C17UfOEp.js} +1 -1
- package/dist/{SolarLimb-bZE6y63S.js → SolarLimb-DRYy8tln.js} +184 -172
- 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-B085D9FR.js → openStorage-CdyVjmps.js} +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +567 -559
- package/dist/rayzee.umd.js +106 -101
- package/dist/{sceneCachePolicy-D_uBSbQF.js → sceneCachePolicy-ywbGKHD3.js} +9 -6
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -0
- package/src/EngineIssues.js +1 -0
- package/src/Passes/OIDNTemporalHistory.js +1 -1
- package/src/Processor/ArchiveImporter.js +83 -21
- package/src/Processor/AssetLoader.js +7 -0
- package/src/Processor/BufferLayout.js +3 -3
- package/src/Processor/GeometryExtractor.js +12 -4
- package/src/Processor/MaterialPacking.js +2 -2
- package/src/Processor/PBRT/PBRTMaterials.js +47 -12
- 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/SceneProcessor.js +42 -26
- package/src/RayzeeRenderer.js +4 -2
- package/src/Stages/DenoiserSettings.js +0 -4
- package/src/Stages/NRD.js +53 -24
- package/src/Stages/NormalDepth.js +5 -3
- package/src/Stages/PathTracer.js +2 -0
- package/src/Stages/PathTracerStage.js +5 -1
- package/src/Storage/SceneGraphCodec.js +6 -3
- package/src/TSL/BVHTraversal.js +33 -20
- package/src/TSL/Common.js +25 -8
- package/src/TSL/Displacement.js +66 -113
- package/src/TSL/LightsDirect.js +33 -11
- package/src/TSL/SceneResources.js +8 -0
- package/src/TSL/ShadeKernel.js +2 -2
- package/src/TSL/Struct.js +1 -0
- package/src/managers/MaterialDataManager.js +11 -0
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
|
} );
|
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
|
|
@@ -13,6 +13,7 @@
|
|
|
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
15
|
* @property {boolean} [lightRadius] - some point or spot light has a radius (path tracing only); false = all are points
|
|
16
|
+
* @property {boolean} [transmissionMaps] - some material has a transmission map; false = shadow rays never sample one
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
import { context } from 'three/tsl';
|
|
@@ -60,3 +61,10 @@ export function usesLightRadius( builder ) {
|
|
|
60
61
|
return builder.context[ KEY ]?.lightRadius ?? true;
|
|
61
62
|
|
|
62
63
|
}
|
|
64
|
+
|
|
65
|
+
/** Whether the kernel being built has shadow rays sample transmission maps. Outside a scene kernel (a test) it does. */
|
|
66
|
+
export function usesTransmissionMaps( builder ) {
|
|
67
|
+
|
|
68
|
+
return builder.context[ KEY ]?.transmissionMaps ?? true;
|
|
69
|
+
|
|
70
|
+
}
|
package/src/TSL/ShadeKernel.js
CHANGED
|
@@ -917,7 +917,7 @@ export function buildShadeKernel( params ) {
|
|
|
917
917
|
const displacedNormal = N.toVar();
|
|
918
918
|
If(
|
|
919
919
|
material.displacementMapIndex.greaterThanEqual( int( 0 ) )
|
|
920
|
-
.and( material.displacementScale.
|
|
920
|
+
.and( material.displacementScale.notEqual( 0.0 ) ),
|
|
921
921
|
() => {
|
|
922
922
|
|
|
923
923
|
const dispRay = Ray( { origin, direction } );
|
|
@@ -1003,7 +1003,7 @@ export function buildShadeKernel( params ) {
|
|
|
1003
1003
|
const albedo = matSamples.albedo.toVar();
|
|
1004
1004
|
If(
|
|
1005
1005
|
material.displacementMapIndex.greaterThanEqual( int( 0 ) )
|
|
1006
|
-
.and( material.displacementScale.
|
|
1006
|
+
.and( material.displacementScale.notEqual( 0.0 ) ),
|
|
1007
1007
|
() => {
|
|
1008
1008
|
|
|
1009
1009
|
N.assign( normalize( displacedNormal.add( matSamples.normal.sub( normalize( hitNormal ) ) ) ) );
|
package/src/TSL/Struct.js
CHANGED
|
@@ -342,6 +342,16 @@ export class MaterialDataManager {
|
|
|
342
342
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
+
/** Whether some material has a transmission map: only then do shadow rays sample one. */
|
|
346
|
+
hasTransmissionMaps() {
|
|
347
|
+
|
|
348
|
+
const data = this.materialStorageAttr?.array;
|
|
349
|
+
if ( ! data ) return false;
|
|
350
|
+
for ( let i = 0; i < this.materialCount; i ++ ) if ( data[ i * M.FLOATS_PER_MATERIAL + M.TRANSMISSION_MAP_INDEX ] >= 0 ) return true;
|
|
351
|
+
return false;
|
|
352
|
+
|
|
353
|
+
}
|
|
354
|
+
|
|
345
355
|
/**
|
|
346
356
|
* Where a material property's value came from, as a MATERIAL_VALUE_SOURCE. `default` means the
|
|
347
357
|
* model never said and the engine filled it in.
|
|
@@ -503,6 +513,7 @@ export class MaterialDataManager {
|
|
|
503
513
|
break;
|
|
504
514
|
case 'bumpScale': data[ stride + M.BUMP_SCALE ] = value; break;
|
|
505
515
|
case 'displacementScale': data[ stride + M.DISPLACEMENT_SCALE ] = value; break;
|
|
516
|
+
case 'displacementBias': data[ stride + M.DISPLACEMENT_BIAS ] = value; break;
|
|
506
517
|
case 'subsurface': data[ stride + M.SUBSURFACE ] = value; break;
|
|
507
518
|
case 'subsurfaceRadiusScale': data[ stride + M.SUBSURFACE_RADIUS_SCALE ] = value; break;
|
|
508
519
|
case 'subsurfaceAnisotropy': data[ stride + M.SUBSURFACE_ANISOTROPY ] = value; break;
|