three-realtime-rt 0.4.2 → 0.6.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 +157 -14
- package/package.json +2 -1
- package/src/CompositePass.js +6 -2
- package/src/DenoisePass.js +58 -6
- package/src/GBufferPass.js +132 -46
- package/src/GIReservoirPass.js +946 -0
- package/src/RTLightingPass.js +123 -10
- package/src/RealtimeRaytracer.js +143 -6
- package/src/SceneCompiler.js +386 -51
- package/src/bvhAnyHit.glsl.js +15 -0
- package/src/index.d.ts +105 -5
package/src/GBufferPass.js
CHANGED
|
@@ -1,17 +1,50 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
2
|
|
|
3
|
+
// three defines USE_SKINNING and supplies the bindMatrix / bindMatrixInverse /
|
|
4
|
+
// boneTexture uniforms + skinIndex / skinWeight attributes automatically when the
|
|
5
|
+
// rendered object is a SkinnedMesh (this is a ShaderMaterial, not a
|
|
6
|
+
// RawShaderMaterial, so it inherits three's default vertex prefix and #include
|
|
7
|
+
// resolution). For a non-skinned mesh USE_SKINNING is undefined and every chunk
|
|
8
|
+
// below collapses to nothing — the shader source is identical in both cases.
|
|
9
|
+
// The chunks operate on `transformed` (position) and `objectNormal` (normal) in
|
|
10
|
+
// the mesh's LOCAL/bind space, so we map our own variables in and out. The
|
|
11
|
+
// skinned local position/normal then go through modelMatrix / uNormalMatrixWorld
|
|
12
|
+
// exactly like the un-skinned path — matching the CPU BVH skinning in
|
|
13
|
+
// SceneCompiler (both skin to local, then transform to world).
|
|
3
14
|
const gbufferVert = /* glsl */ `
|
|
15
|
+
#include <skinning_pars_vertex>
|
|
16
|
+
|
|
4
17
|
out vec3 vWorldPos;
|
|
5
18
|
out vec3 vWorldNormal;
|
|
6
19
|
out vec2 vUvCoord;
|
|
20
|
+
out vec3 vColor;
|
|
7
21
|
|
|
8
22
|
uniform mat3 uNormalMatrixWorld;
|
|
9
23
|
|
|
10
24
|
void main() {
|
|
11
|
-
|
|
25
|
+
vec3 transformed = position;
|
|
26
|
+
vec3 objectNormal = normal;
|
|
27
|
+
#include <skinbase_vertex>
|
|
28
|
+
#include <skinnormal_vertex>
|
|
29
|
+
#include <skinning_vertex>
|
|
30
|
+
|
|
31
|
+
vec4 wp = modelMatrix * vec4(transformed, 1.0);
|
|
12
32
|
vWorldPos = wp.xyz;
|
|
13
|
-
vWorldNormal = normalize(uNormalMatrixWorld *
|
|
33
|
+
vWorldNormal = normalize(uNormalMatrixWorld * objectNormal);
|
|
14
34
|
vUvCoord = uv;
|
|
35
|
+
// Geometry vertex colours. three's shader prefix declares the built-in
|
|
36
|
+
// \`color\` attribute (vec3 or vec4) and sets USE_COLOR / USE_COLOR_ALPHA only
|
|
37
|
+
// when material.vertexColors is on — which we enable ONLY for meshes whose
|
|
38
|
+
// geometry actually carries a color attribute (see GBufferPass swap). A mesh
|
|
39
|
+
// without one compiles the else branch (white), so its albedo is byte-identical
|
|
40
|
+
// to before this varying existed. 4-component colours use .rgb.
|
|
41
|
+
#if defined( USE_COLOR_ALPHA )
|
|
42
|
+
vColor = color.rgb;
|
|
43
|
+
#elif defined( USE_COLOR )
|
|
44
|
+
vColor = color;
|
|
45
|
+
#else
|
|
46
|
+
vColor = vec3(1.0);
|
|
47
|
+
#endif
|
|
15
48
|
gl_Position = projectionMatrix * viewMatrix * wp;
|
|
16
49
|
}
|
|
17
50
|
`;
|
|
@@ -27,11 +60,13 @@ layout(location = 3) out vec4 gEmissive;
|
|
|
27
60
|
in vec3 vWorldPos;
|
|
28
61
|
in vec3 vWorldNormal;
|
|
29
62
|
in vec2 vUvCoord;
|
|
63
|
+
in vec3 vColor;
|
|
30
64
|
|
|
31
65
|
uniform vec3 uColor;
|
|
32
66
|
uniform float uRoughness;
|
|
33
67
|
uniform float uMetalness;
|
|
34
68
|
uniform float uTransmission;
|
|
69
|
+
uniform float uIor;
|
|
35
70
|
uniform vec3 uEmissive;
|
|
36
71
|
uniform sampler2D uMap;
|
|
37
72
|
uniform bool uHasMap;
|
|
@@ -69,6 +104,7 @@ void main() {
|
|
|
69
104
|
if (uHasMap) {
|
|
70
105
|
albedo *= texture(uMap, vUvCoord).rgb;
|
|
71
106
|
}
|
|
107
|
+
albedo *= vColor; // vertex colours (white when the mesh has no color attribute)
|
|
72
108
|
vec3 emissive = uEmissive;
|
|
73
109
|
if (uHasEmissiveMap) {
|
|
74
110
|
emissive *= texture(uEmissiveMap, vUvCoord).rgb;
|
|
@@ -87,16 +123,34 @@ void main() {
|
|
|
87
123
|
float metalness = uMetalness;
|
|
88
124
|
if (uHasMetalnessMap) metalness *= texture(uMetalnessMap, vUvCoord).b;
|
|
89
125
|
gAlbedoRough = vec4(albedo, roughness);
|
|
90
|
-
// .w is a packed material word in
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
126
|
+
// .w is a packed material word in disjoint ranges, so the lighting pass reads
|
|
127
|
+
// specular/glass/blend properties without an extra G-buffer sampler (it already
|
|
128
|
+
// sits at the WebGL2 16-sampler minimum — the reason per-material IOR rides
|
|
129
|
+
// here rather than in a third G-buffer texture the lighting pass would have to
|
|
130
|
+
// sample):
|
|
131
|
+
// [0,1] plain metalness
|
|
132
|
+
// (2,3] transmissive glass, PARTIAL: w - 2 = transmission (global rt.ior)
|
|
133
|
+
// [3,4) transmissive glass, FULL (transmission >= ~1): w - 3 = ior - 1
|
|
134
|
+
// [4,5] alpha blend: w - 4 = opacity
|
|
135
|
+
// Blend wins: a transparent surface is kept out of the BVH and composited by
|
|
136
|
+
// the lighting pass, so it must never be read as glass. Every EXISTING consumer
|
|
137
|
+
// decodes clamp(w - 2, 0, 1) as transmission, which saturates to 1.0 across the
|
|
138
|
+
// whole [3,4) band — so full glass keeps reading as fully transmissive there and
|
|
139
|
+
// only the lighting pass additionally recovers the per-material IOR (Task 2).
|
|
140
|
+
float matWord;
|
|
141
|
+
if (uBlend) {
|
|
142
|
+
matWord = 4.0 + uOpacity;
|
|
143
|
+
} else if (uTransmission > 0.0) {
|
|
144
|
+
if (uTransmission >= 0.99) {
|
|
145
|
+
// clamp (ior - 1) to [0, 0.98] so the word stays clear of the 4.0 blend
|
|
146
|
+
// boundary even after fp16 rounding of this channel; covers ior 1.0-1.98.
|
|
147
|
+
matWord = 3.0 + clamp(uIor - 1.0, 0.0, 0.98);
|
|
148
|
+
} else {
|
|
149
|
+
matWord = 2.0 + uTransmission; // partial glass: keep transmission, global ior
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
matWord = metalness;
|
|
153
|
+
}
|
|
100
154
|
gNormalMetal = vec4(n, matWord);
|
|
101
155
|
// .w packs the valid flag AND roughness: 0 = background, 1 + roughness
|
|
102
156
|
// otherwise. Every consumer only tests w < 0.5, so this stays compatible.
|
|
@@ -184,46 +238,54 @@ export class GBufferPass {
|
|
|
184
238
|
for (const t of this._targets) t.setSize(width, height);
|
|
185
239
|
}
|
|
186
240
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
241
|
+
_makeGbufferMaterial(mesh) {
|
|
242
|
+
const material = new THREE.ShaderMaterial({
|
|
243
|
+
glslVersion: THREE.GLSL3,
|
|
244
|
+
vertexShader: gbufferVert,
|
|
245
|
+
fragmentShader: gbufferFrag,
|
|
246
|
+
uniforms: {
|
|
247
|
+
uNormalMatrixWorld: { value: new THREE.Matrix3() },
|
|
248
|
+
uColor: { value: new THREE.Color(1, 1, 1) },
|
|
249
|
+
uRoughness: { value: 1.0 },
|
|
250
|
+
uMetalness: { value: 0.0 },
|
|
251
|
+
uTransmission: { value: 0.0 },
|
|
252
|
+
uIor: { value: 1.5 },
|
|
253
|
+
uEmissive: { value: new THREE.Color(0, 0, 0) },
|
|
254
|
+
uMap: { value: null },
|
|
255
|
+
uHasMap: { value: false },
|
|
256
|
+
uEmissiveMap: { value: null },
|
|
257
|
+
uHasEmissiveMap: { value: false },
|
|
258
|
+
uNormalMap: { value: null },
|
|
259
|
+
uHasNormalMap: { value: false },
|
|
260
|
+
uNormalScale: { value: new THREE.Vector2(1, 1) },
|
|
261
|
+
uRoughnessMap: { value: null },
|
|
262
|
+
uHasRoughnessMap: { value: false },
|
|
263
|
+
uMetalnessMap: { value: null },
|
|
264
|
+
uHasMetalnessMap: { value: false },
|
|
265
|
+
uBlend: { value: false },
|
|
266
|
+
uOpacity: { value: 1.0 },
|
|
267
|
+
},
|
|
268
|
+
side: THREE.FrontSide,
|
|
269
|
+
});
|
|
270
|
+
// Enable the vertex-colour path ONLY when this mesh's geometry carries a
|
|
271
|
+
// color attribute. This drives three's USE_COLOR define (see gbufferVert), so
|
|
272
|
+
// a mesh without one writes byte-identical albedo. The material cache is
|
|
273
|
+
// per-mesh, so this per-mesh define variant is safe.
|
|
274
|
+
material.vertexColors = !!(mesh.geometry && mesh.geometry.getAttribute("color"));
|
|
275
|
+
return material;
|
|
276
|
+
}
|
|
219
277
|
|
|
220
|
-
|
|
221
|
-
|
|
278
|
+
// Sync properties from one user material into one gbuffer material (cheap; run
|
|
279
|
+
// every frame). `mesh` supplies the shared world normal matrix + side.
|
|
280
|
+
_syncGbufferMaterial(material, src, mesh) {
|
|
222
281
|
const u = material.uniforms;
|
|
223
282
|
if (src.color) u.uColor.value.copy(src.color);
|
|
224
283
|
u.uRoughness.value = src.roughness ?? 1.0;
|
|
225
284
|
u.uMetalness.value = src.metalness ?? 0.0;
|
|
226
285
|
u.uTransmission.value = src.transmission ?? 0.0; // MeshPhysicalMaterial
|
|
286
|
+
// Per-material IOR (MeshPhysicalMaterial.ior; default 1.5). Encoded into the
|
|
287
|
+
// packed material word for fully-transmissive glass (see gbufferFrag).
|
|
288
|
+
u.uIor.value = src.ior ?? 1.5;
|
|
227
289
|
|
|
228
290
|
if (src.emissive) {
|
|
229
291
|
u.uEmissive.value
|
|
@@ -249,6 +311,30 @@ export class GBufferPass {
|
|
|
249
311
|
u.uOpacity.value = src.opacity ?? 1.0;
|
|
250
312
|
u.uNormalMatrixWorld.value.getNormalMatrix(mesh.matrixWorld);
|
|
251
313
|
material.side = src.side ?? THREE.FrontSide;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Returns the gbuffer material(s) for a mesh: a single ShaderMaterial, or — for
|
|
317
|
+
// a multi-material mesh (mesh.material is an array + geometry.groups) — an ARRAY
|
|
318
|
+
// of them, one per source material, index-aligned so three renders each group
|
|
319
|
+
// with its own gbuffer material natively.
|
|
320
|
+
_gbufferMaterialFor(mesh) {
|
|
321
|
+
if (Array.isArray(mesh.material)) {
|
|
322
|
+
let cached = this._materialCache.get(mesh);
|
|
323
|
+
if (!Array.isArray(cached) || cached.length !== mesh.material.length) {
|
|
324
|
+
cached = mesh.material.map(() => this._makeGbufferMaterial(mesh));
|
|
325
|
+
this._materialCache.set(mesh, cached);
|
|
326
|
+
}
|
|
327
|
+
for (let i = 0; i < mesh.material.length; i++) {
|
|
328
|
+
this._syncGbufferMaterial(cached[i], mesh.material[i], mesh);
|
|
329
|
+
}
|
|
330
|
+
return cached;
|
|
331
|
+
}
|
|
332
|
+
let material = this._materialCache.get(mesh);
|
|
333
|
+
if (!material || Array.isArray(material)) {
|
|
334
|
+
material = this._makeGbufferMaterial(mesh);
|
|
335
|
+
this._materialCache.set(mesh, material);
|
|
336
|
+
}
|
|
337
|
+
this._syncGbufferMaterial(material, mesh.material, mesh);
|
|
252
338
|
return material;
|
|
253
339
|
}
|
|
254
340
|
|