three-realtime-rt 0.3.0 → 0.4.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/src/RestirPass.js CHANGED
@@ -21,6 +21,7 @@ uniform sampler2D uGNormalMetal;
21
21
  uniform sampler2D uMaterialsTex; // row 1: emissive tris, rows 2..65: blue noise
22
22
  uniform vec4 uLightPosType[MAX_LIGHTS];
23
23
  uniform vec4 uLightColorRadius[MAX_LIGHTS];
24
+ uniform vec4 uLightDirCone[MAX_LIGHTS]; // spot: direction.xyz + cos(outer)
24
25
  uniform int uLightCount;
25
26
  uniform int uEmissiveCount;
26
27
  uniform float uFrame;
@@ -53,23 +54,45 @@ vec4 fetchBlueNoise() {
53
54
 
54
55
  float luminance(vec3 c) { return dot(c, vec3(0.299, 0.587, 0.114)); }
55
56
 
57
+ // Primary-surface roughness, set per pixel in main(). Drives the cheap specular
58
+ // lobe below so reservoirs favour lights that land on a highlight.
59
+ float gRestirRough;
60
+
61
+ // Cheap Blinn-Phong specular lobe for the target pdf ONLY (never shaded with
62
+ // this). Deliberately approximate — no Fresnel or geometry term, one pow() per
63
+ // candidate — since it just biases which light each reservoir keeps. wi points
64
+ // from the surface toward the light. Returns a multiplier boost in [0, ~0.8].
65
+ float restirSpecBoost(vec3 N, vec3 wi, vec3 P) {
66
+ vec3 V = normalize(uCameraPos - P);
67
+ vec3 H = normalize(wi + V);
68
+ float shin = mix(4.0, 256.0, 1.0 - gRestirRough);
69
+ return 0.8 * pow(max(dot(N, H), 0.0), shin);
70
+ }
71
+
56
72
  // Unshadowed contribution of candidate (id, uv) at surface (P, N).
57
73
  vec3 candidateContribution(float id, vec2 uv, vec3 P, vec3 N) {
58
74
  if (id < float(MAX_LIGHTS)) {
59
75
  int i = int(id);
60
76
  vec4 posType = uLightPosType[i];
61
77
  vec4 colRad = uLightColorRadius[i];
62
- if (posType.w < 0.5) {
78
+ if (posType.w < 0.5 || posType.w >= 1.5) {
63
79
  vec3 d = posType.xyz - P; // light CENTER: soft-radius jitter re-drawn at shading
64
80
  float dl = length(d);
65
81
  if (dl < 1e-5) return vec3(0.0);
66
82
  float NdotL = dot(N, d / dl);
67
83
  if (NdotL <= 0.0) return vec3(0.0);
68
- return colRad.rgb * (NdotL / (dl * dl));
84
+ float cone = 1.0;
85
+ if (posType.w >= 1.5) {
86
+ // spot cone — MUST match RTLightingPass.spotFalloff for a consistent estimator
87
+ vec4 dc = uLightDirCone[i];
88
+ cone = smoothstep(dc.w, posType.w - 2.0, dot(dc.xyz, -d / dl));
89
+ if (cone <= 0.0) return vec3(0.0);
90
+ }
91
+ return colRad.rgb * (cone * NdotL / (dl * dl)) * (1.0 + restirSpecBoost(N, d / dl, P));
69
92
  }
70
93
  float NdotL = dot(N, -posType.xyz);
71
94
  if (NdotL <= 0.0) return vec3(0.0);
72
- return colRad.rgb * NdotL;
95
+ return colRad.rgb * NdotL * (1.0 + restirSpecBoost(N, -posType.xyz, P));
73
96
  }
74
97
  int t = (int(id) - MAX_LIGHTS) * 4;
75
98
  vec4 t0 = texelFetch(uMaterialsTex, ivec2(t, 1), 0);
@@ -87,7 +110,7 @@ vec3 candidateContribution(float id, vec2 uv, vec3 P, vec3 N) {
87
110
  if (cosS <= 0.0 || cosL < 1e-4) return vec3(0.0);
88
111
  // Uniform pick within the emissive set happens at candidate level, so the
89
112
  // per-triangle contribution uses area only (count folds into pick pdf).
90
- return vec3(t1.w, t2.w, t3.w) * (cosS * cosL * t0.w / max(d2, 1e-6));
113
+ return vec3(t1.w, t2.w, t3.w) * (cosS * cosL * t0.w / max(d2, 1e-6)) * (1.0 + restirSpecBoost(N, wi, P));
91
114
  }
92
115
 
93
116
  // v3: reservoirs select TRIANGLES, not points. The selection target is the
@@ -129,6 +152,7 @@ void main() {
129
152
  }
130
153
  vec3 P = wp.xyz;
131
154
  vec3 N = normalize(texture(uGNormalMetal, vUv).xyz);
155
+ gRestirRough = clamp(wp.w - 1.0, 0.0, 1.0);
132
156
 
133
157
  ivec2 px = ivec2(gl_FragCoord.xy);
134
158
  gSeed = uint(px.x) * 3079u + uint(px.y) * 9277u + uint(uFrame) * 26699u;
@@ -215,6 +239,7 @@ void main() {
215
239
  }
216
240
  vec3 P = wp.xyz;
217
241
  vec3 N = normalize(texture(uGNormalMetal, vUv).xyz);
242
+ gRestirRough = clamp(wp.w - 1.0, 0.0, 1.0);
218
243
 
219
244
  ivec2 px = ivec2(gl_FragCoord.xy);
220
245
  gSeed = uint(px.x) * 5417u + uint(px.y) * 7907u + uint(uFrame) * 15731u;
@@ -283,6 +308,7 @@ export class RestirPass {
283
308
  uMaterialsTex: { value: null },
284
309
  uLightPosType: { value: [] },
285
310
  uLightColorRadius: { value: [] },
311
+ uLightDirCone: { value: [] },
286
312
  uLightCount: { value: 0 },
287
313
  uEmissiveCount: { value: 0 },
288
314
  uFrame: { value: 0 },
@@ -334,6 +360,7 @@ export class RestirPass {
334
360
  u.uMaterialsTex.value = compiled.materialsTex;
335
361
  u.uLightPosType.value = compiled.lightPosType;
336
362
  u.uLightColorRadius.value = compiled.lightColorRadius;
363
+ u.uLightDirCone.value = compiled.lightDirCone;
337
364
  u.uLightCount.value = compiled.lightCount;
338
365
  u.uEmissiveCount.value = compiled.emissiveTriCount;
339
366
  }