@meimu/mutracer 0.1.0 → 0.1.2

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.
Files changed (88) hide show
  1. package/README.md +61 -16
  2. package/dist/assets/bvh.worker.js +1 -1
  3. package/dist/mutracer.umd.js +668 -658
  4. package/docs/materials.md +27 -7
  5. package/docs/progressive-rendering.md +24 -8
  6. package/docs/threejs.md +15 -3
  7. package/docs/viewer.md +53 -0
  8. package/lib/Display.d.ts +1 -1
  9. package/lib/Display.js +4 -2
  10. package/lib/Display.js.map +1 -1
  11. package/lib/Render.d.ts +9 -0
  12. package/lib/Render.js +84 -13
  13. package/lib/Render.js.map +1 -1
  14. package/lib/RenderLoop.d.ts +5 -2
  15. package/lib/RenderLoop.js +47 -15
  16. package/lib/RenderLoop.js.map +1 -1
  17. package/lib/oidn.d.ts +1 -1
  18. package/lib/oidnBundledWeights.cdn.d.ts +2 -2
  19. package/lib/renderer/MaterialTable.d.ts +1 -0
  20. package/lib/renderer/MaterialTable.js +2 -1
  21. package/lib/renderer/MaterialTable.js.map +1 -1
  22. package/lib/renderer/WGPUBloomPass.js +8 -8
  23. package/lib/renderer/WGPUJNLMDenoiser.js +2 -2
  24. package/lib/renderer/WGPURayTraceOutput.js +5 -5
  25. package/lib/renderer/WGPURayTracePass.js +1 -1
  26. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  27. package/lib/renderer/WGPURenderingPipeline.js +8 -6
  28. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  29. package/lib/renderer/WebGLJNLMDenoiser.js +2 -2
  30. package/lib/renderer/WebGLRayTracePass.js +3 -1
  31. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  32. package/lib/renderer/committedScene.js +11 -6
  33. package/lib/renderer/committedScene.js.map +1 -1
  34. package/lib/renderer/glsl/chunks/envMap.glsl.js +5 -5
  35. package/lib/renderer/glsl/chunks/intersect.glsl.js +32 -32
  36. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +9 -9
  37. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +30 -30
  38. package/lib/renderer/glsl/chunks/rand.glsl.js +2 -2
  39. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +4 -4
  40. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +3 -3
  41. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +4 -4
  42. package/lib/renderer/glsl/chunks/sampling.glsl.js +3 -3
  43. package/lib/renderer/glsl/chunks/sobol.glsl.js +3 -3
  44. package/lib/renderer/glsl/rayTrace.frag.js +7 -3
  45. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  46. package/lib/renderer/glsl/webglPipeline.frag.js +2 -2
  47. package/lib/renderer/materialComparison.generated.js +5 -0
  48. package/lib/renderer/materialComparison.generated.js.map +1 -1
  49. package/lib/renderer/materialLayout.js +2 -1
  50. package/lib/renderer/materialLayout.js.map +1 -1
  51. package/lib/renderer/prepareMeshBVH.d.ts +4 -2
  52. package/lib/renderer/prepareMeshBVH.js +44 -20
  53. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  54. package/lib/renderer/util.d.ts +2 -2
  55. package/lib/renderer/util.js +3 -3
  56. package/lib/renderer/util.js.map +1 -1
  57. package/lib/renderer/wgsl/chunks/common.wgsl.js +2 -2
  58. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +14 -14
  59. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +85 -88
  60. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  61. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +34 -34
  62. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.d.ts +1 -1
  63. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +51 -46
  64. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js.map +1 -1
  65. package/lib/renderer/wgsl/chunks/rand.wgsl.js +2 -2
  66. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +3 -3
  67. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +4 -4
  68. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +4 -4
  69. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +3 -3
  70. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +3 -3
  71. package/lib/renderer/wgsl/compose.frag.wgsl.js +2 -2
  72. package/lib/renderer/wgsl/customDenoise.wgsl.js +4 -4
  73. package/lib/renderer/wgsl/offlineAdaptive.compute.js +10 -10
  74. package/lib/renderer/wgsl/rayTrace.compute.js +13 -9
  75. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  76. package/lib/scene/OpenPBRMaterial.d.ts +4 -2
  77. package/lib/scene/OpenPBRMaterial.js +4 -2
  78. package/lib/scene/OpenPBRMaterial.js.map +1 -1
  79. package/lib/scene/openPbrSchema.d.ts +7 -2
  80. package/lib/scene/openPbrSchema.js +2 -1
  81. package/lib/scene/openPbrSchema.js.map +1 -1
  82. package/lib/util/fromTHREE.d.ts +6 -0
  83. package/lib/util/fromTHREE.js +85 -19
  84. package/lib/util/fromTHREE.js.map +1 -1
  85. package/lib/version.d.ts +1 -1
  86. package/lib/version.js +1 -1
  87. package/lib/version.js.map +1 -1
  88. package/package.json +1 -1
@@ -58,9 +58,9 @@ fu lm;
58
58
  top = 0;
59
59
  stack[0] = ivec2(0, -1);
60
60
 
61
- int wd = 0;
62
- while(top >= 0 && wd < 5000) {
63
- wd++;
61
+ int wf = 0;
62
+ while(top >= 0 && wf < 5000) {
63
+ wf++;
64
64
  ivec2 node = stack[top--];
65
65
 
66
66
  int cf = node.y;
@@ -117,7 +117,7 @@ uintBitsToFloat(uint(rs + 127) << 23u)
117
117
  uint childBase = floatBitsToUint(fC.x);
118
118
  uint oE = floatBitsToUint(fC.y);
119
119
  uint internalMask = header >> 24u;
120
- uint uf = (d.x < 0.0 ? 4u : 0u) |
120
+ uint ug = (d.x < 0.0 ? 4u : 0u) |
121
121
  (d.y < 0.0 ? 2u : 0u) |
122
122
  (d.z < 0.0 ? 1u : 0u);
123
123
  uint gA[12] = uint[12](
@@ -130,7 +130,7 @@ floatBitsToUint(mJ.z), floatBitsToUint(mJ.w)
130
130
  );
131
131
 
132
132
  for (int order = 7; order >= 0; order--) {
133
- uint slot = uint(order) ^ uf;
133
+ uint slot = uint(order) ^ ug;
134
134
  uint sn = slot >= 4u
135
135
  ? floatBitsToUint(fC.w)
136
136
  : floatBitsToUint(fC.z);
@@ -148,9 +148,9 @@ float((gA[6u + kJ] >> lL) & 0xffu),
148
148
  float((gA[8u + kJ] >> lL) & 0xffu),
149
149
  float((gA[10u + kJ] >> lL) & 0xffu)
150
150
  );
151
- vec3 uI = jr.xyz + tq * hG;
152
- vec3 uH = jr.xyz + tp * hG;
153
- if (cM(o, d, uI, uH, tMax) <= 0.0) continue;
151
+ vec3 uK = jr.xyz + tq * hG;
152
+ vec3 uJ = jr.xyz + tp * hG;
153
+ if (cM(o, d, uK, uJ, tMax) <= 0.0) continue;
154
154
  uint ju = meta & 0x1fu;
155
155
  if (ju >= 24u) {
156
156
  uint mY = internalMask & ((1u << slot) - 1u);
@@ -162,29 +162,29 @@ eS + int(sL * 5u), cf
162
162
  else {
163
163
  uint triangleCount = qG(meta >> 5u);
164
164
  uint rF = oE + ju;
165
- uint uS = 0x80000000u |
165
+ uint uU = 0x80000000u |
166
166
  (triangleCount << 23u) |
167
167
  (rF & 0x007fffffu);
168
- stack[++top] = ivec2(int(uS), cf);
168
+ stack[++top] = ivec2(int(uU), cf);
169
169
  }
170
170
  }
171
171
  }
172
172
  }
173
173
  else if (gL == 2) {
174
174
  int i = node.x + bo;
175
- vec4 uK = dp(i);
176
- vec4 uL = dp(i + 1);
177
- vec4 uM = dp(i + 2);
178
- vec4 uN = dp(i + 3);
179
- bh = int(floatBitsToUint(uM.w));
175
+ vec4 uM = dp(i);
176
+ vec4 uN = dp(i + 1);
177
+ vec4 uO = dp(i + 2);
178
+ vec4 uP = dp(i + 3);
179
+ bh = int(floatBitsToUint(uO.w));
180
180
  if (bh > 0) {
181
- primitiveOffset = bo + eS + int(floatBitsToUint(uN.w));
181
+ primitiveOffset = bo + eS + int(floatBitsToUint(uP.w));
182
182
  }
183
183
  else {
184
- int left = eS + int(floatBitsToUint(uK.w) * 4u);
185
- int right = eS + int(floatBitsToUint(uL.w) * 4u);
186
- float d0 = cM(o, d, uK.xyz, uL.xyz, tMax);
187
- float d1 = cM(o, d, uM.xyz, uN.xyz, tMax);
184
+ int left = eS + int(floatBitsToUint(uM.w) * 4u);
185
+ int right = eS + int(floatBitsToUint(uN.w) * 4u);
186
+ float d0 = cM(o, d, uM.xyz, uN.xyz, tMax);
187
+ float d1 = cM(o, d, uO.xyz, uP.xyz, tMax);
188
188
  if (d0 > d1) {
189
189
  if (d0 > 0.0) stack[++top] = ivec2(left, cf);
190
190
  if (d1 > 0.0) stack[++top] = ivec2(right, cf);
@@ -203,12 +203,12 @@ primitiveOffset = bo + eS + int(hT & 0x007fffffu);
203
203
  }
204
204
  else {
205
205
  int i = node.x + bo;
206
- vec4 tV = dp(i);
206
+ vec4 tW = dp(i);
207
207
  vec4 ra = dp(i + 1);
208
208
  vec4 rb = dp(i + 2);
209
209
  vec4 rc = dp(i + 3);
210
- vec3 nodeMin = tV.xyz;
211
- vec3 ec = vec3(tV.w, ra.x, ra.y);
210
+ vec3 nodeMin = tW.xyz;
211
+ vec3 ec = vec3(tW.w, ra.x, ra.y);
212
212
  uint lS[4] = uint[4](
213
213
  floatBitsToUint(ra.z), floatBitsToUint(ra.w),
214
214
  floatBitsToUint(rb.x), floatBitsToUint(rb.y)
@@ -451,8 +451,8 @@ d = ht(light.radius, light.position, r);
451
451
  lightSample.isDelta = false;
452
452
  }
453
453
  if (d != INF) {
454
- vec3 uQ = r.o + d * r.d;
455
- float mo = bL(light, normalize(r.o - uQ));
454
+ vec3 uS = r.o + d * r.d;
455
+ float mo = bL(light, normalize(r.o - uS));
456
456
  if (mo <= 0.0) return INF;
457
457
  float aj = distance(r.o, light.position);
458
458
  float cosThetaMax = aj <= light.radius
@@ -835,19 +835,19 @@ return vec3(0.0);
835
835
 
836
836
 
837
837
 
838
- float cM(vec3 o, vec3 d, vec3 vw, vec3 vu, float tMax) {
838
+ float cM(vec3 o, vec3 d, vec3 vx, vec3 vw, float tMax) {
839
839
  #ifdef DEPTH_QUERY_MODE
840
840
  vec3 gv = eX(d);
841
- vec3 uD = (vw - o) * gv;
842
- vec3 uE = (vu - o) * gv;
841
+ vec3 uF = (vx - o) * gv;
842
+ vec3 uG = (vw - o) * gv;
843
843
  vec3 bJ = eV(gv, o);
844
844
  #else
845
- vec3 uD = (vw - o) / d;
846
- vec3 uE = (vu - o) / d;
845
+ vec3 uF = (vx - o) / d;
846
+ vec3 uG = (vw - o) / d;
847
847
  vec3 bJ = vec3(0.0);
848
848
  #endif
849
- vec3 my = min(uD, uE) - bJ;
850
- vec3 rH = max(uD, uE) + bJ;
849
+ vec3 my = min(uF, uG) - bJ;
850
+ vec3 rH = max(uF, uG) + bJ;
851
851
  float t0 = max(my.x, max(my.y, my.z));
852
852
  float t1 = min(rH.x, min(rH.y, rH.z));
853
853
 
@@ -51,7 +51,7 @@ return alpha >= alphaCutoff ? 1.0 : 0.0;
51
51
  return alpha;
52
52
  }
53
53
 
54
- float vO(int idx) {
54
+ float vP(int idx) {
55
55
  return fetchMatData(idx * MAT_DATA_SIZE + MAT_ROW_COLOR).w;
56
56
  }
57
57
 
@@ -135,8 +135,8 @@ vec4 nh = fetchMatData(i + MAT_ROW_ALPHA_FILM);
135
135
  vec4 lF = fetchMatData(i + MAT_ROW_BASE_CONTROLS);
136
136
  vec4 oM = fetchMatData(i + MAT_ROW_SPECULAR_FUZZ);
137
137
  vec4 subsurface = fetchMatData(i + MAT_ROW_SUBSURFACE);
138
- vec4 wc = fetchMatData(i + MAT_ROW_FUZZ);
139
- vec4 vx = fetchMatData(i + MAT_ROW_COAT);
138
+ vec4 wd = fetchMatData(i + MAT_ROW_FUZZ);
139
+ vec4 vy = fetchMatData(i + MAT_ROW_COAT);
140
140
  vec4 lG = fetchMatData(i + MAT_ROW_COAT_CONTROLS);
141
141
  vec4 mK = fetchMatData(i + MAT_ROW_FILM_DISPERSION);
142
142
  vec4 ng = fetchMatData(i + MAT_ROW_STANDALONE_MAPS);
@@ -173,10 +173,10 @@ mat.specularColor = max(oM.rgb, vec3(0.0));
173
173
  mat.fuzzWeight = clamp(oM.w, 0.0, 1.0);
174
174
  mat.subsurfaceColor = max(subsurface.rgb, vec3(0.0));
175
175
  mat.transmissionColor = max(fetchMatData(i + MAT_ROW_TRANSMISSION_COLOR).rgb, vec3(0.0));
176
- mat.fuzzColor = max(wc.rgb, vec3(0.0));
177
- mat.fuzzRoughness = clamp(wc.w, 0.0, 1.0);
178
- mat.coatColor = max(vx.rgb, vec3(0.0));
179
- mat.coatWeight = clamp(vx.w, 0.0, 1.0);
176
+ mat.fuzzColor = max(wd.rgb, vec3(0.0));
177
+ mat.fuzzRoughness = clamp(wd.w, 0.0, 1.0);
178
+ mat.coatColor = max(vy.rgb, vec3(0.0));
179
+ mat.coatWeight = clamp(vy.w, 0.0, 1.0);
180
180
  mat.coatRoughness = clamp(lG.x, ROUGHNESS_MIN, 1.0);
181
181
  mat.coatAnisotropy = clamp(lG.y, 0.0, 1.0);
182
182
  mat.coatIor = max(lG.z, 1.0);
@@ -253,10 +253,10 @@ mat.emission *= decodeMapColor(emissiveMapIndex, emissive);
253
253
  #ifdef NUM_COAT_WEIGHT_MAPS
254
254
  int hN = int(fetchMatData(i + MAT_ROW_LAYER_MAPS).x);
255
255
  if (hN >= 0 && mat.materialType == 0) {
256
- float qk = clamp(vx.w, 0.0, 1.0) * sampleMap(
256
+ float qk = clamp(vy.w, 0.0, 1.0) * sampleMap(
257
257
  hN, getMapUvSet(hN, uv, uv1)
258
258
  ).r;
259
- mat.emission *= mix(vec3(1.0), sqrt(max(vx.rgb, vec3(0.0))), qk);
259
+ mat.emission *= mix(vec3(1.0), sqrt(max(vy.rgb, vec3(0.0))), qk);
260
260
  }
261
261
  #endif
262
262
 
@@ -122,18 +122,18 @@ float cp = bA - float(dP);
122
122
  float cy = aG - float(aM);
123
123
  float ku = bt - float(cI);
124
124
 
125
- float uk = F(cI, dP * 32 + aM);
126
- float ul = F(eO, dP * 32 + aM);
127
- float um = F(cI, dP * 32 + cn);
128
- float un = F(eO, dP * 32 + cn);
129
- float uo = F(cI, hB * 32 + aM);
130
- float uq = F(eO, hB * 32 + aM);
131
- float ur = F(cI, hB * 32 + cn);
132
- float us = F(eO, hB * 32 + cn);
133
- float v00 = mix(uk, uo, cp);
134
- float v01 = mix(ul, uq, cp);
135
- float v10 = mix(um, ur, cp);
136
- float v11 = mix(un, us, cp);
125
+ float ul = F(cI, dP * 32 + aM);
126
+ float um = F(eO, dP * 32 + aM);
127
+ float un = F(cI, dP * 32 + cn);
128
+ float uo = F(eO, dP * 32 + cn);
129
+ float uq = F(cI, hB * 32 + aM);
130
+ float ur = F(eO, hB * 32 + aM);
131
+ float us = F(cI, hB * 32 + cn);
132
+ float ut = F(eO, hB * 32 + cn);
133
+ float v00 = mix(ul, uq, cp);
134
+ float v01 = mix(um, ur, cp);
135
+ float v10 = mix(un, us, cp);
136
+ float v11 = mix(uo, ut, cp);
137
137
  return mix(
138
138
  mix(v00, v10, cy),
139
139
  mix(v01, v11, cy),
@@ -181,18 +181,18 @@ float cp = bA - float(dP);
181
181
  float cy = aG - float(aM);
182
182
  float ku = bt - float(cI);
183
183
 
184
- float uk = F(cI, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + dP * 32 + aM);
185
- float ul = F(eO, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + dP * 32 + aM);
186
- float um = F(cI, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + dP * 32 + cn);
187
- float un = F(eO, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + dP * 32 + cn);
188
- float uo = F(cI, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + hB * 32 + aM);
189
- float uq = F(eO, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + hB * 32 + aM);
190
- float ur = F(cI, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + hB * 32 + cn);
191
- float us = F(eO, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + hB * 32 + cn);
192
- float v00 = mix(uk, uo, cp);
193
- float v01 = mix(ul, uq, cp);
194
- float v10 = mix(um, ur, cp);
195
- float v11 = mix(un, us, cp);
184
+ float ul = F(cI, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + dP * 32 + aM);
185
+ float um = F(eO, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + dP * 32 + aM);
186
+ float un = F(cI, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + dP * 32 + cn);
187
+ float uo = F(eO, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + dP * 32 + cn);
188
+ float uq = F(cI, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + hB * 32 + aM);
189
+ float ur = F(eO, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + hB * 32 + aM);
190
+ float us = F(cI, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + hB * 32 + cn);
191
+ float ut = F(eO, ${OPENPBR_DIELECTRIC_ENERGY_LUT_ROW} + hB * 32 + cn);
192
+ float v00 = mix(ul, uq, cp);
193
+ float v01 = mix(um, ur, cp);
194
+ float v10 = mix(un, us, cp);
195
+ float v11 = mix(uo, ut, cp);
196
196
  return mix(
197
197
  mix(v00, v10, cy),
198
198
  mix(v01, v11, cy),
@@ -906,17 +906,17 @@ J material = prepared.material;
906
906
  float dielectric = 1.0 - material.baseMetalness;
907
907
  float opaque = dielectric * (1.0 - material.transmissionWeight) * (1.0 - material.subsurfaceWeight);
908
908
  float sss = dielectric * (1.0 - material.transmissionWeight) * material.subsurfaceWeight;
909
- float tT = dielectric * material.transmissionWeight;
909
+ float tU = dielectric * material.transmissionWeight;
910
910
  float fL = bM(prepared.specularEtaTOverEtaI);
911
911
  float kI = bM(1.0 / prepared.transmissionReflectionIor);
912
- float pw = clamp(opaque * fL + (sss + tT) * kI + material.baseMetalness, 0.0, 1.0);
912
+ float pw = clamp(opaque * fL + (sss + tU) * kI + material.baseMetalness, 0.0, 1.0);
913
913
  float ji = mix(1.0, material.specularRoughness, pw);
914
914
  float ks = bM(material.coatIor);
915
915
  float kr = 1.0 - (1.0 - ks) / (material.coatIor * material.coatIor);
916
916
  float k = mix(ks, kr, ji);
917
917
  vec3 eb = material.baseMetalness * material.specularWeight * bP(material.baseWeight * material.baseColor, material.specularColor)
918
918
  + opaque * mix(material.baseWeight * material.baseColor, vec3(1.0), fL)
919
- + sss * material.subsurfaceColor + vec3(tT);
919
+ + sss * material.subsurfaceColor + vec3(tU);
920
920
  vec3 qH = vec3(1.0 - k) / max(vec3(1.0) - eb * k, vec3(1e-6));
921
921
  return mix(vec3(1.0), qH, material.coatWeight * material.coatDarkening);
922
922
  }
@@ -1545,7 +1545,7 @@ normal
1545
1545
  return bI(prepared, lightDirection, pdf);
1546
1546
  }
1547
1547
 
1548
- float vJ(
1548
+ float vK(
1549
1549
  vec3 tangent,
1550
1550
  J material,
1551
1551
  vec3 bF,
@@ -1662,8 +1662,8 @@ else if (bm < cdf[OPENPBR_LOBE_TRANSMISSION]) {
1662
1662
  isTransmissionEvent = true;
1663
1663
 
1664
1664
 
1665
- float ue = (bm - cdf[OPENPBR_LOBE_SPECULAR]) / max(prepared.probabilities[OPENPBR_LOBE_TRANSMISSION], 1e-12);
1666
- if (prepared.dielectricMsProbability > 0.0 && ue < prepared.dielectricMsProbability) {
1665
+ float uf = (bm - cdf[OPENPBR_LOBE_SPECULAR]) / max(prepared.probabilities[OPENPBR_LOBE_TRANSMISSION], 1e-12);
1666
+ if (prepared.dielectricMsProbability > 0.0 && uf < prepared.dielectricMsProbability) {
1667
1667
  float iU = prepared.dielectricMsReflectionRatio;
1668
1668
  bool lM = cx < iU;
1669
1669
  float z;
@@ -11,7 +11,7 @@ s0 = uvec4(p, uint(frame), uint(p.x) + uint(p.y));
11
11
  }
12
12
 
13
13
 
14
- void vg(inout uvec4 v) {
14
+ void vj(inout uvec4 v) {
15
15
  v = v * 1664525u + 1013904223u;
16
16
  v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z;
17
17
  v = v ^ (v>>16u);
@@ -19,7 +19,7 @@ v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z;
19
19
  }
20
20
 
21
21
  float rand() {
22
- vg(s0); return float(s0.x)/float(0xffffffffu);
22
+ vj(s0); return float(s0.x)/float(0xffffffffu);
23
23
  }
24
24
 
25
25
  `;
@@ -23,7 +23,7 @@ export default createShaderMixin({
23
23
  main: glsl `
24
24
 
25
25
  const vec3 luminance = vec3(0.2126, 0.7152, 0.0722);
26
- const int vo = 8;
26
+ const int vp = 8;
27
27
 
28
28
  #define RAY_MAX_DISTANCE 9999.0
29
29
 
@@ -276,10 +276,10 @@ float tD = light.v.y > 1e-6
276
276
  ? smoothstep(light.v.x, light.v.x + light.v.y, cc)
277
277
  : step(light.v.x, cc);
278
278
  if (tD <= 0.0 || light.textureIndex < 0) return tD;
279
- vec3 tW = light.iesReference;
280
- vec3 vn = cross(axis, tW);
279
+ vec3 tX = light.iesReference;
280
+ vec3 vo = cross(axis, tX);
281
281
  float vertical = acos(clamp(cc, -1.0, 1.0));
282
- float horizontal = atan(dot(dJ, vn), dot(dJ, tW));
282
+ float horizontal = atan(dot(dJ, vo), dot(dJ, tX));
283
283
  return tD * kF(light.textureIndex, vec2(vertical / PI, horizontal / TWOPI + 0.5));
284
284
  }
285
285
  `
@@ -60,9 +60,9 @@ result.pdf = 1.0 / max(TWOPI * (1.0 - rJ), 1e-8);
60
60
  float iZ = dot(direction, nE);
61
61
  float ej = iZ * iZ
62
62
  - dot(nE, nE) + radius * radius;
63
- float wt = sqrt(max(0.0, ej));
64
- float cN = iZ - wt;
65
- float cB = iZ + wt;
63
+ float wu = sqrt(max(0.0, ej));
64
+ float cN = iZ - wu;
65
+ float cB = iZ + wu;
66
66
  result.dist = cN > 1e-6 ? cN : cB;
67
67
  result.direction = direction;
68
68
  vec3 surfacePoint = shadingPoint + direction * result.dist;
@@ -93,14 +93,14 @@ return eW > 0.0
93
93
  #ifdef NUM_COAT_NORMAL_MAPS
94
94
  vec3 fB(vec3 pk, vec3 tc, vec3 view) {
95
95
  bool qL = dot(tc, view) >= 0.0;
96
- vec3 ub = qL ? tc : -tc;
96
+ vec3 uc = qL ? tc : -tc;
97
97
  vec3 qK = qL ? pk : -pk;
98
- if (dot(pk, tc) <= 0.0 || dot(qK, view) <= 1e-6) { return ub; }
98
+ if (dot(pk, tc) <= 0.0 || dot(qK, view) <= 1e-6) { return uc; }
99
99
  return qK;
100
100
  }
101
101
  #endif
102
102
  void lO(inout ad si, J mat, inout Path aa) {
103
- vec3 ud = vec3(fk(cv(BSDF_DIRECTION)), dz(cv(BSDF_LOBE)));
103
+ vec3 ue = vec3(fk(cv(BSDF_DIRECTION)), dz(cv(BSDF_LOBE)));
104
104
  #ifdef NUM_COAT_NORMAL_MAPS
105
105
  vec3 bC = si.faceNormal;
106
106
  vec3 mr = si.normal;
@@ -219,7 +219,7 @@ aa.li += mG(si, mat, prepared, aa, ffnormal, true) * aa.beta;
219
219
  bool isNextRefraction;
220
220
  vec3 f = dv(
221
221
  prepared,
222
- ud,
222
+ ue,
223
223
  cF,
224
224
  scatteringPdf,
225
225
  isNextRefraction
@@ -7,11 +7,11 @@ export default glsl `
7
7
  vec3 aQ(vec3 view, vec2 alpha, vec2 samplePoint) {
8
8
  vec3 qY = vec3(alpha, 1.0);
9
9
  vec3 ge = normalize(qY * view);
10
- float tU = min(ge.z, 1.0);
11
- float qD = 1.0 + tU;
10
+ float tV = min(ge.z, 1.0);
11
+ float qD = 1.0 + tV;
12
12
  float qT = (1.0 - samplePoint.y) * qD;
13
13
  float rE = samplePoint.y * qD;
14
- float st = qT + (1.0 - tU);
14
+ float st = qT + (1.0 - tV);
15
15
  float fi = sqrt(max(0.0, st * rE));
16
16
  float br = TWOPI * samplePoint.x;
17
17
  vec3 lt = vec3(
@@ -52,7 +52,7 @@ uint r = reverseBits(ki);
52
52
  ${sobolD1Steps.map(([shift, mask]) => `r ^= (r << ${shift}u) & 0x${mask.toString(16)}u;`).join("\n ")}
53
53
  return r;
54
54
  }
55
- vec2 vC(uint index, uint ie) {
55
+ vec2 vD(uint index, uint ie) {
56
56
  uint i = sv(index, aq(ie, 0u));
57
57
  uint x = reverseBits(iA(i, aq(ie, 1u)));
58
58
  uint y = sv(qX(i), aq(ie, 2u));
@@ -74,9 +74,9 @@ return (word >> ((tm & 3u) * 8u)) & 255u;
74
74
  }
75
75
  uint pK(uint kB, uint dim, uint logSPP) {
76
76
  uint odd = logSPP & 1u;
77
- uint tZ = 8u + ((logSPP + 1u) >> 1u);
77
+ uint ua = 8u + ((logSPP + 1u) >> 1u);
78
78
  uint result = 0u;
79
- for (int digit = int(tZ) - 1; digit >= int(odd); digit--) {
79
+ for (int digit = int(ua) - 1; digit >= int(odd); digit--) {
80
80
  uint shift = 2u*uint(digit) - odd;
81
81
 
82
82
  uint sd = 0u;
@@ -32,7 +32,7 @@ return;
32
32
  // So here the code are injected separately.
33
33
  const bounceCodeOnIntelMac = glsl `
34
34
 
35
- for(int vH = 0; vH < BOUNCES; vH++) {{
35
+ for(int vI = 0; vI < BOUNCES; vI++) {{
36
36
  if (aa.abort) {
37
37
  return vec4(aa.li, aa.alpha);
38
38
  }
@@ -40,10 +40,10 @@ bounce(aa, depth, si, mat, outAux);
40
40
  }}
41
41
  `;
42
42
  const bounceCodeOnOther = glsl `
43
- bool uR = !si.isTransparency;
43
+ bool uT = !si.isTransparency;
44
44
  int qB = min(BOUNCES, bounceLimit);
45
45
  for(int idx = 0; idx < min(BOUNCES * 3, ${MAX_PATH_EVENTS}); idx++) {
46
- if (uR && idx > qB) {
46
+ if (uT && idx > qB) {
47
47
  aa.abort = true;
48
48
  }
49
49
  if (aa.abort) {
@@ -120,6 +120,10 @@ ${intelMac ? doubleTransmissionCheckCode : ''}
120
120
  bool isShadowCatcher = mat.materialType == SHADOW_CATCHER;
121
121
  if (isShadowCatcher) {
122
122
  if (aa.fromEye) {
123
+
124
+
125
+ outAux.albedo = vec3(1.0);
126
+ outAux.normal = dot(si.normal, aa.ray.d) > 0.0 ? -si.normal : si.normal;
123
127
  si.hit = false;
124
128
  outAux.isShadowCatcher = true;
125
129
  }
@@ -1 +1 @@
1
- {"version":3,"file":"rayTrace.frag.js","sourceRoot":"","sources":["../../../src/renderer/glsl/rayTrace.frag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,aAAa,MAAM,4BAA4B,CAAC;AACvD,qDAAqD;AACrD,sDAAsD;AACtD,kFAAkF;AAElF,OAAO,UAAU,MAAM,yBAAyB,CAAC;AACjD,OAAO,YAAY,MAAM,+BAA+B,CAAC;AACzD,OAAO,aAAa,MAAM,gCAAgC,CAAC;AAC3D,OAAO,kBAAkB,MAAM,iCAAiC,CAAC;AACjE,OAAO,SAAS,MAAM,4BAA4B,CAAC;AACnD,OAAO,QAAQ,MAAM,uBAAuB,CAAC;AAC7C,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,OAAO,kBAAkB,MAAM,iCAAiC,CAAC;AACjE,OAAO,eAAe,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,eAAe,MAAM,8BAA8B,CAAC;AAC3D,OAAO,YAAY,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,gBAAgB,MAAM,+BAA+B,CAAC;AAE7D,MAAM,2BAA2B,GAAG,IAAI,CAAA;;;;;;;CAOvC,CAAC;AAEF,0BAA0B;AAC1B,iEAAiE;AACjE,kFAAkF;AAClF,4CAA4C;AAC5C,MAAM,oBAAoB,GAAG,IAAI,CAAA;;;;;;;;CAQhC,CAAC;AACF,MAAM,iBAAiB,GAAG,IAAI,CAAA;;;0CAGY,eAAe;;;;;;;;;CASxD,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,4BAA4B;IAClD,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,OAAO,IAAI,cAAc,CAAC;QAC1B,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC;QACjD,OAAO,EAAE;YACP,OAAO,EAAE,CAAC;SACX;QACD,QAAQ,EAAE;YACR,oBAAoB,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;YAChD,mBAAmB,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAEhD,iBAAiB,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YACzC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,0BAA0B,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;YACnD,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;YAE7C,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAEpC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;YACtC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;YAC1C,qBAAqB,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;YAC/C,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;SACtC;QACD,QAAQ,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC;QACvE,IAAI,EAAE,IAAI,CAAA;EACV,UAAU;;EAEV,QAAQ;EACR,MAAM,CAAC,IAAI;;EAEX,aAAa;;EAEb,YAAY,CAAC,IAAI;;EAEjB,gBAAgB;;EAEhB,kBAAkB,CAAC,IAAI;EACvB,SAAS,CAAC,IAAI;EACd,MAAM,CAAC,IAAI;;EAEX,YAAY;EACZ,eAAe;;;;;EAKf,eAAe;EACf,kBAAkB;;EAElB,aAAa;;;;;;;;;;;;;;;;EAgBb,QAAQ,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoV3C,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8JnD;KACC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { MAX_PATH_EVENTS } from '../pathLimits.js';\nimport cameraRayGlsl from './chunks/cameraRay.glsl.js';\n// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport commonGlsl from './chunks/common.glsl.js';\nimport rayTraceCore from './chunks/rayTraceCore.glsl.js';\nimport textureLinear from './chunks/textureLinear.glsl.js';\nimport materialBufferGlsl from './chunks/materialBuffer.glsl.js';\nimport intersect from './chunks/intersect.glsl.js';\nimport randGlsl from './chunks/rand.glsl.js';\nimport random from './chunks/random.glsl.js';\nimport envMap from './chunks/envMap.glsl.js';\nimport sampleMaterialGlsl from './chunks/sampleMaterial.glsl.js';\nimport sampleLightGlsl from './chunks/sampleLight.glsl.js';\nimport { FragmentShader, glsl, Shader } from '../shader.js';\nimport openPbrBsdfGlsl from './chunks/openPbrBsdf.glsl.js';\nimport samplingGlsl from './chunks/sampling.glsl.js';\nimport { isIntelMac } from '../util.js';\nimport uvProjectionGlsl from './chunks/uvProjection.glsl.js';\n\nconst doubleTransmissionCheckCode = glsl`\n#if defined(HAS_TRANSMISSION) || defined(HAS_ALPHA)\n if (!si.isTransparency && BOUNCES < 16 && depth * 2 > BOUNCES) {\n path.abort = true;\n return;\n }\n#endif\n`;\n\n// bounce fix on intel mac\n// The intel mac only have one bounce if the loop is not unrolled\n// But windows will have extremely slow shader compilation if the loop is unrolled\n// So here the code are injected separately.\nconst bounceCodeOnIntelMac = glsl`\n// Mark loop can be unrolled\nfor(int _idx_ = 0; _idx_ < BOUNCES; _idx_++) {{\n if (path.abort) {\n return vec4(path.li, path.alpha);\n }\n bounce(path, depth, si, mat, outAux);\n}}\n`;\nconst bounceCodeOnOther = glsl`\nbool isOpaque = !si.isTransparency;\nint activeBounceLimit = min(BOUNCES, bounceLimit);\nfor(int idx = 0; idx < min(BOUNCES * 3, ${MAX_PATH_EVENTS}); idx++) {\n if (isOpaque && idx > activeBounceLimit) {\n path.abort = true;\n }\n if (path.abort) {\n return vec4(path.li, path.alpha);\n }\n bounce(path, depth, si, mat, outAux);\n}\n`;\n\nexport default function createRayTraceFragmentShader() {\n const intelMac = isIntelMac();\n return new FragmentShader({\n name: 'rayTraceFrag',\n outputs: ['color', 'albedo', 'normal', 'moments'],\n defines: {\n BOUNCES: 2\n },\n uniforms: {\n cameraApertureRadius: Shader.uniform('float', 0),\n cameraFocusDistance: Shader.uniform('float', 10),\n\n viewProjectionInv: Shader.uniform('mat4'),\n viewInv: Shader.uniform('mat4'),\n transmissiveOutputAlphaMin: Shader.uniform('float'),\n openPbrEnergyLut: Shader.uniform('sampler2D'),\n\n viewportSize: Shader.uniform('vec2'),\n\n wavelength: Shader.uniform('float', 0),\n bevelEnabled: Shader.uniform('bool', true),\n dispersionWavelengths: Shader.uniform('int', 3),\n bounceLimit: Shader.uniform('int', 8)\n },\n includes: [rayTraceCore, materialBufferGlsl, intersect, random, envMap],\n main: glsl`\n${commonGlsl}\n\n${randGlsl}\n${random.main}\n\n${textureLinear}\n\n${rayTraceCore.main}\n\n${uvProjectionGlsl}\n\n${materialBufferGlsl.main}\n${intersect.main}\n${envMap.main}\n\n${samplingGlsl}\n${openPbrBsdfGlsl}\n\nfloat heroWavelength;\nfloat pathWavelength;\n\n${sampleLightGlsl}\n${sampleMaterialGlsl}\n\n${cameraRayGlsl}\nstruct RayTraceAuxiliary {\n // Denoising guides and auxiliary pixel classification.\n vec3 albedo;\n vec3 normal;\n bool isBlank;\n // The primary ray hit a shadow catcher, whose sample the catcher pass owns.\n bool isShadowCatcher;\n};\n\nvoid bounce(inout Path path, inout int depth, inout SurfaceInteraction si, inout OpenPbrResolvedMaterial mat, inout RayTraceAuxiliary outAux) {\n samplingBounce = uint(max(depth, 0));\n // The bounce will be doubled on the transmission material.\n // For other materials. We will quite early\n // TODO It is only used on Mac with AMD card. If I enabled it on windows.\n // It will cause shader compilation too slow.\n${intelMac ? doubleTransmissionCheckCode : ''}\n\n bool isShadowCatcher = mat.materialType == SHADOW_CATCHER;\n if (isShadowCatcher) {\n if (path.fromEye) {\n si.hit = false;\n outAux.isShadowCatcher = true;\n }\n else {\n // The catcher belongs only to the shadow-mask pass. Keep its default\n // white material out of glossy and transmission reflections.\n#ifdef USE_ENVMAP\n if (path.isNextRefraction) {\n // The invisible catcher can sit between a transmitted camera ray and\n // the grounded skybox. Use the displayed background here instead of\n // terminating early with the raw lighting environment.\n vec4 bg = sampleBackgroundFromRay(path.ray.o, path.ray.d);\n bg.rgb = sampling_spectral_rgb_filter(bg.rgb, pathWavelength);\n float backgroundMisWeight = path.scatteringIsDelta ? 1.0\n : sampling_power_heuristic(path.scatteringPdf,\n envMapPdf(cartesianToEquirect(path.ray.d)) * envLightSelectionPmf);\n path.li += backgroundMisWeight * path.beta * bg.rgb * bg.a;\n path.alpha = max(path.alpha, bg.a);\n }\n else {\n vec2 envUv = cartesianToEquirect(path.ray.d);\n vec3 emission = textureLinear(envMap, envUv).rgb;\n emission = sampling_spectral_rgb_filter(emission, pathWavelength);\n path.li += path.beta * emission *\n sampling_power_heuristic(\n path.scatteringPdf,\n envMapPdf(envUv) * envLightSelectionPmf\n );\n }\n#else\n vec4 bg = sampleBackgroundFromRay(path.ray.o, path.ray.d);\n bg.rgb = sampling_spectral_rgb_filter(bg.rgb, pathWavelength);\n path.li += path.beta * bg.rgb * bg.a;\n#endif\n path.abort = true;\n return;\n }\n }\n\n if (!si.hit) {\n // Sample from background if first ray not hit anything.\n vec4 bg = sampleBackgroundFromRay(path.ray.o, path.ray.d);\n // Dispersion renders one additive pass per RGB wavelength. Background\n // radiance has to be split in the same way as lights and emissive\n // surfaces, otherwise the full skybox is accumulated three times.\n bg.rgb = sampling_spectral_rgb_filter(bg.rgb, pathWavelength);\n if (path.fromEye) {\n path.li = bg.rgb * bg.a;\n path.alpha = bg.a;\n // Only treat as blank pixels when the background is transparent\n outAux.isBlank = bg.a < 0.01;\n }\n else {\n Ray cam = initCameraRay(v_Texcoord + pixelJitter());\n bool towardsCam = dot(cam.d, path.ray.d) > 0.0;\n if (transmissiveOutputAlphaMin < 1.0) {\n if (towardsCam) {\n path.alpha = max(path.alpha, bg.a);\n } else {\n path.alpha = 1.0;\n }\n }\n#if !defined(USE_ENVMAP)\n // TODO treat background as a light source\n if (towardsCam) {\n path.li = path.beta * sampling_power_heuristic(path.scatteringPdf, 1.0) * bg.rgb;\n }\n#endif\n }\n path.abort = true;\n return;\n }\n\n bool sampleIgnored = false;\n if ((mat.materialType == STANDARD || mat.materialType == UNLIT)) {\n if (transmissiveOutputAlphaMin < 1.0) {\n // TODO increase the alpha when there are more bounces.\n float alpha = max(1.0 - mat.transmissionWeight, 1.0 - pow(1.0 - transmissiveOutputAlphaMin, float(depth)));\n path.alpha = max(alpha, path.alpha);\n }\n float alpha = mat.alpha;\n if (alpha < 1.0) {\n // Alpha is sampled as stochastic coverage. An accepted surface branch\n // must use full radiance weight; multiplying by alpha again is biased.\n path.alpha = max(path.alpha, alpha);\n if (hashedSample(3u) > alpha) {\n sampleIgnored = true;\n }\n }\n if (!sampleIgnored) {\n SampleMaterial(si, mat, path);\n }\n else {\n path.ray.o = si.position + path.ray.d * EPS;\n }\n }\n\n if (!si.albedoSampled && !sampleIgnored) {\n // Select denoising guides.\n if ((mat.materialType == STANDARD || mat.materialType == UNLIT) &&\n !(mat.transmissionWeight > 0.5 ||\n (mat.specularRoughness <= 0.02 && mat.baseMetalness > 0.98))) {\n // Alpha-blended decals can be skipped stochastically. In that case keep\n // looking for the first useful rough-surface guide behind the decal,\n // even though it is reached after depth zero.\n outAux.albedo = mat.baseColor;\n outAux.normal = dot(si.normal, si.position - vec3(viewInv[3])) > 0.0\n ? -si.normal\n : si.normal;\n si.albedoSampled = true;\n }\n if (depth > 4) {\n si.albedoSampled = true;\n }\n }\n\n\n // Russian Roulette sampling\n#ifdef USE_RR\n if (depth >= RR_MAX && !sampleIgnored) {\n // Max-component survival (pbrt-v4) avoids over-amplifying saturated paths.\n float maxComponent = max(max(path.beta.x, path.beta.y), path.beta.z);\n float q = max(0.0, 1.0 - maxComponent);\n if (sample1D(bounceDim(RUSSIAN_ROULETTE)) < q) {\n // TODO we force use alpha 1.0 here.\n // To avoid the glass pixels is transparent even if it's on an opaque object.\n path.alpha = 1.0;\n path.abort = true;\n }\n else {\n path.beta /= 1.0 - q;\n }\n }\n#endif\n\n if (path.abort) {\n return;\n }\n\n // Next ray hit\n intersectScene(path.ray, si, true);\n getMaterial(mat, si.materialIndex, si.uv, si.uv1, si.instanceIndex);\n si.isTransparency = mat.transmissionWeight > 0.001 || mat.subsurfaceWeight > 0.001 || mat.alpha < 0.999;\n\n // Dynamic preview only becomes spectral when the primary visible surface\n // actually disperses transmitted light. Direct background and ordinary\n // opaque materials stay full RGB and therefore remain noise-free.\n if (dispersionWavelengths == 1 && si.hit && mat.transmissionWeight > 0.001 && mat.transmissionDispersionScale > 0.001) {\n pathWavelength = heroWavelength;\n }\n if (mat.transmissionWeight > 0.001 && pathWavelength > 0.0) {\n mat.specularIor = sampling_cauchy_ior(\n pathWavelength,\n mat.specularIor,\n mat.transmissionDispersionScale,\n mat.dispersionAbbeNumber\n );\n }\n\n if (sampleIgnored) {\n return;\n }\n\n bool isAlphaOccluded = hashedSample(3u) < mat.alpha;\n#ifdef NUM_LIGHTS\n // Use light if hit the light\n LightSampleRec lightSample;\n for (int i = 0; i < NUM_LIGHTS; i++) {\n // Fetch light Data\n Light light = fetchLight(i);\n float d = intersectLight(path.ray, light, lightSample);\n bool occluded = si.hit && d > distance(si.position, path.ray.o);\n if (d != INF && !(occluded && isAlphaOccluded)) {\n lightSample.pdf *= light.selectionPmf;\n float misWeight = sampling_power_heuristic(path.scatteringPdf, lightSample.pdf);\n lightSample.emission = sampling_spectral_rgb_filter(lightSample.emission, pathWavelength);\n path.li += misWeight * path.beta * lightSample.emission;\n // TODO set alpha to 1.0 will cause some sharp edges\n path.alpha = 1.0;\n path.abort = true;\n }\n }\n#endif\n\n#ifdef USE_ENVMAP\n if (!(si.hit && isAlphaOccluded)) {\n if (path.isNextRefraction) {\n // Camera-visible transmission must use the displayed background. The\n // raw environment remains the lighting source for non-transmission rays.\n vec4 bg = sampleBackgroundFromRay(path.ray.o, path.ray.d);\n bg.rgb = sampling_spectral_rgb_filter(bg.rgb, pathWavelength);\n float backgroundMisWeight = path.scatteringIsDelta ? 1.0\n : sampling_power_heuristic(path.scatteringPdf,\n envMapPdf(cartesianToEquirect(path.ray.d)) * envLightSelectionPmf);\n path.li += backgroundMisWeight * path.beta * bg.rgb * bg.a;\n path.alpha = max(path.alpha, bg.a);\n path.abort = true;\n return;\n }\n vec2 uv = cartesianToEquirect(path.ray.d);\n vec3 emission = textureLinear(envMap, uv).rgb;\n float misWeight = sampling_power_heuristic(\n path.scatteringPdf,\n envMapPdf(uv) * envLightSelectionPmf\n );\n emission = sampling_spectral_rgb_filter(emission, pathWavelength);\n path.li += path.beta * emission * misWeight;\n\n // TODO force the min alpha to be 1 when showing envmap as background\n #if !defined(USE_BACKGROUND)\n path.alpha = 1.0;\n #endif\n path.abort = true;\n return;\n }\n#endif\n\n depth++;\n}\n\n\nvec2 bevelCubicSample(float radius, float xi) {\n float x = 0.25;\n for (int iteration = 0; iteration < 10; iteration++) {\n float x2 = x * x;\n float x3 = x2 * x;\n float oneMinusX = 1.0 - x;\n float value = 10.0 * x2 - 20.0 * x3 + 15.0 * x2 * x2 -\n 4.0 * x2 * x3 - xi;\n float derivative = 20.0 * x * oneMinusX * oneMinusX * oneMinusX;\n if (abs(value) < 1e-6 || derivative == 0.0) break;\n x = clamp(x - value / derivative, 0.0, 1.0);\n }\n float diskRadius = x * radius;\n return vec2(\n diskRadius,\n sqrt(max(radius * radius - diskRadius * diskRadius, 0.0))\n );\n}\n\nfloat bevelCubicPdf(float radius, float distanceToSource) {\n if (distanceToSource >= radius) return 0.0;\n float falloff = radius - distanceToSource;\n return falloff * falloff * falloff;\n}\n\n// Shader bevel. Usage: docs/materials.md (Renderer extensions)\nvec3 getBevelNormal(SurfaceInteraction source, float radius) {\n vec3 geometricNormal = source.faceNormal;\n if (dot(geometricNormal, source.vertexNormal) < 0.0) geometricNormal = -geometricNormal;\n vec3 tangent;\n vec3 bitangent;\n sampling_orthonormal_basis(geometricNormal, tangent, bitangent);\n vec3 normalSum = vec3(0.0);\n\n for (int sampleIndex = 0; sampleIndex < BEVEL_SAMPLES; sampleIndex++) {\n vec2 bevelRnd = sample2D(CAMERA_BEVEL_BASE + uint(sampleIndex));\n float axisSample = bevelRnd.x;\n float radialSample = bevelRnd.y;\n vec3 diskNormal = geometricNormal;\n vec3 diskTangent = tangent;\n vec3 diskBitangent = bitangent;\n float pickPdfNormal = 0.5;\n float pickPdfTangent = 0.25;\n float pickPdfBitangent = 0.25;\n if (axisSample < 0.5) {\n axisSample *= 2.0;\n }\n else if (axisSample < 0.75) {\n vec3 swapAxis = diskNormal;\n diskNormal = diskTangent;\n diskTangent = swapAxis;\n pickPdfNormal = 0.25;\n pickPdfTangent = 0.5;\n axisSample = (axisSample - 0.5) * 4.0;\n }\n else {\n vec3 swapAxis = diskNormal;\n diskNormal = diskBitangent;\n diskBitangent = swapAxis;\n pickPdfNormal = 0.25;\n pickPdfBitangent = 0.5;\n axisSample = (axisSample - 0.75) * 4.0;\n }\n\n vec2 diskSample = bevelCubicSample(radius, radialSample);\n float phi = TWOPI * axisSample;\n vec3 diskOffset = diskSample.x *\n (cos(phi) * diskTangent + sin(phi) * diskBitangent);\n Ray ray;\n initRay(\n ray,\n source.position + diskNormal * diskSample.y + diskOffset,\n -diskNormal\n );\n SurfaceInteraction hit;\n intersectSceneBevel(\n ray,\n hit,\n source.instanceIndex,\n max(2.0 * diskSample.y, EPS)\n );\n if (!hit.hit) continue;\n\n float pdfNormal = pickPdfNormal * abs(dot(diskNormal, hit.faceNormal));\n float pdfTangent = pickPdfTangent * abs(dot(diskTangent, hit.faceNormal));\n float pdfBitangent = pickPdfBitangent * abs(dot(diskBitangent, hit.faceNormal));\n float weight = pdfNormal / max(\n pdfNormal * pdfNormal + pdfTangent * pdfTangent +\n pdfBitangent * pdfBitangent,\n 1e-8\n );\n weight *= bevelCubicPdf(radius, distance(hit.position, source.position)) /\n max(bevelCubicPdf(radius, diskSample.x), 1e-8);\n normalSum += weight * hit.vertexNormal;\n }\n\n if (dot(normalSum, normalSum) <= 1e-12) return source.normal;\n vec3 bevelVertexNormal = normalize(normalSum);\n vec3 result = source.normal + bevelVertexNormal - source.vertexNormal;\n return dot(result, result) > 1e-12 ? normalize(result) : source.normal;\n}\n\nvec4 integrateOnePathSample(SurfaceInteraction si, Ray ray, OpenPbrResolvedMaterial mat, inout RayTraceAuxiliary outAux) {\n Path path;\n path.ray = ray;\n path.li = vec3(0);\n path.alpha = transmissiveOutputAlphaMin < 1.0 ? 0.0 : 1.0;\n path.beta = vec3(1.0);\n path.fromEye = true;\n path.scatteringPdf = 0.0;\n path.scatteringIsDelta = false;\n path.isNextRefraction = false;\n path.abort = false;\n int depth = 0;\n${intelMac ? bounceCodeOnIntelMac : bounceCodeOnOther}\n return vec4(path.li, path.alpha);\n}\n\nvec4 integrateOnePath(SurfaceInteraction si, Ray ray, OpenPbrResolvedMaterial mat, inout RayTraceAuxiliary outAux) {\n float coverage = clamp(mat.alpha, 0.0, 1.0);\n if (!si.hit || (mat.materialType != STANDARD && mat.materialType != UNLIT) || coverage >= 1.0) {\n return integrateOnePathSample(si, ray, mat, outAux);\n }\n\n // glTF BLEND is source-over opacity, not a physical glass boundary. Split\n // the primary ray so reflected radiance is attenuated by alpha every sample\n // while the complementary branch continues straight to the scene behind it.\n // Secondary alpha hits remain stochastic to avoid exponential path growth.\n Ray backRay = ray;\n backRay.o = si.position + ray.d * (EPS * 0.02);\n SurfaceInteraction backSi = si;\n backSi.albedoSampled = false;\n intersectScene(backRay, backSi, false);\n OpenPbrResolvedMaterial backMat;\n getMaterial(backMat, backSi.materialIndex, backSi.uv, backSi.uv1, backSi.instanceIndex);\n RayTraceAuxiliary backAux = outAux;\n if (backSi.hit && (backMat.materialType == STANDARD || backMat.materialType == UNLIT)) {\n backAux.albedo = backMat.transmissionWeight > 0.5\n ? vec3(1.0)\n : backMat.baseColor;\n backAux.normal = dot(\n backSi.normal,\n backSi.position - vec3(viewInv[3])\n ) > 0.0\n ? -backSi.normal\n : backSi.normal;\n backSi.albedoSampled = backMat.transmissionWeight > 0.5;\n }\n vec4 back = integrateOnePathSample(backSi, backRay, backMat, backAux);\n if (coverage <= 0.0) {\n outAux = backAux;\n return back;\n }\n\n SurfaceInteraction frontSi = si;\n OpenPbrResolvedMaterial frontMat = mat;\n frontMat.alpha = 1.0;\n RayTraceAuxiliary frontAux = outAux;\n vec4 front = integrateOnePathSample(frontSi, ray, frontMat, frontAux);\n outAux.albedo = mix(backAux.albedo, frontAux.albedo, coverage);\n vec3 blendedNormal = mix(backAux.normal, frontAux.normal, coverage);\n outAux.normal = dot(blendedNormal, blendedNormal) > 1e-12\n ? normalize(blendedNormal)\n : vec3(0.0);\n outAux.isBlank = backAux.isBlank && frontAux.isBlank;\n return mix(back, front, coverage);\n}\n\n// Path tracing integrator as described in\n// http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Path_Tracing.html#\nvec4 integrator(Ray ray, out RayTraceAuxiliary outAux) {\n\n SurfaceInteraction si;\n OpenPbrResolvedMaterial mat;\n\n si.albedoSampled = false;\n#ifdef HAS_MEDIUM\n si.mediumType = 0;\n si.mediumExtinction = vec3(0.0);\n si.mediumScattering = vec3(0.0);\n si.mediumAnisotropy = 0.0;\n#endif\n\n intersectScene(ray, si, false);\n getMaterial(mat, si.materialIndex, si.uv, si.uv1, si.instanceIndex);\n si.isTransparency = mat.transmissionWeight > 0.001 || mat.subsurfaceWeight > 0.001 || mat.alpha < 0.999;\n\n#ifdef HAS_BEVEL\n if (bevelEnabled && mat.bevel > 0.0) {\n si.normal = getBevelNormal(si, mat.bevel);\n }\n#endif\n\n // Keep a clean first-hit fallback for denoising. Glass retains this stable\n // guide; other delta paths may replace it at their first non-delta hit.\n if (si.hit && (mat.materialType == STANDARD || mat.materialType == UNLIT)) {\n outAux.albedo = mat.transmissionWeight > 0.5 ? vec3(1.0) : mat.baseColor;\n outAux.normal = dot(si.normal, si.position - vec3(viewInv[3])) > 0.0\n ? -si.normal\n : si.normal;\n // A refracted/reflected continuation is stochastic and therefore not a\n // clean OIDN feature. Keep the stable primary glass guide instead.\n si.albedoSampled = mat.transmissionWeight > 0.5;\n }\n\n if (mat.transmissionDispersionScale > 0.001) {\n mat.specularIor = sampling_cauchy_ior(\n pathWavelength,\n mat.specularIor,\n mat.transmissionDispersionScale,\n mat.dispersionAbbeNumber\n );\n }\n return integrateOnePath(si, ray, mat, outAux);\n}\n\nvoid main() {\n\n initRandom();\n rng_initialize(v_Texcoord * viewportSize, int(uint(sampleCount) ^ sampleHash(uint(sequenceSeed))));\n\n\n pathWavelength = dispersionWavelengths == 1 ? 0.0 : wavelength;\n heroWavelength = wavelength;\n if (dispersionWavelengths == 1 && wavelength > 0.0) {\n float channel = floor(sample1D(CAMERA_WAVELENGTH) * 3.0);\n heroWavelength = channel < 1.0 ? 650.0 : channel < 2.0 ? 510.0 : 475.0;\n }\n\n // Thin lens model with depth-of-field\n // http://www.pbr-book.org/3ed-2018/Camera_Models/Projective_Camera_Models.html#TheThinLensModelandDepthofField\n Ray cam = primaryCameraRay();\n\n RayTraceAuxiliary outAux;\n outAux.albedo = vec3(0.0);\n outAux.normal = vec3(0.0);\n outAux.isBlank = false;\n outAux.isShadowCatcher = false;\n\n vec4 liAndAlpha = integrator(cam, outAux);\n\n // Clamp at Integration Level to reduce fireflies.\n // TODO Path level configuration?\n#ifdef CLAMPING\n float clamping = float(CLAMPING);\n liAndAlpha.rgb = clamp(liAndAlpha.rgb, -clamping, clamping);\n#endif\n\n // Each sample will be accumulated three times.\n if (pathWavelength > 0.0) {\n // RGB will be normalized using accumated hit in out_albedo.a\n liAndAlpha.rgb *= 3.0;\n if (dispersionWavelengths != 1) {\n // Full spectral samples use three additive passes. Preview only submits\n // one hero wavelength, so its alpha must not be divided.\n liAndAlpha.a /= 3.0;\n }\n }\n if (!(liAndAlpha.x < INF && liAndAlpha.x > -EPS)) {\n liAndAlpha = vec4(0, 0, 0, 1);\n }\n\n out_color = liAndAlpha;\n out_albedo = vec4(outAux.albedo, outAux.isBlank ? 0.0 : 1.0);\n out_normal = vec4(outAux.normal, 1.0);\n // Moments and count of the built-in denoiser's guide value. The catcher\n // pass accumulates its own samples (see shadowCatcher.frag).\n float guideValue = dot(liAndAlpha.rgb, vec3(0.2126, 0.7152, 0.0722));\n out_moments = outAux.isShadowCatcher\n ? vec4(0.0)\n : vec4(guideValue, guideValue * guideValue, 1.0, 0.0);\n\n}`\n });\n}\n"]}
1
+ {"version":3,"file":"rayTrace.frag.js","sourceRoot":"","sources":["../../../src/renderer/glsl/rayTrace.frag.ts"],"names":[],"mappings":"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","sourcesContent":["import { MAX_PATH_EVENTS } from '../pathLimits.js';\nimport cameraRayGlsl from './chunks/cameraRay.glsl.js';\n// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport commonGlsl from './chunks/common.glsl.js';\nimport rayTraceCore from './chunks/rayTraceCore.glsl.js';\nimport textureLinear from './chunks/textureLinear.glsl.js';\nimport materialBufferGlsl from './chunks/materialBuffer.glsl.js';\nimport intersect from './chunks/intersect.glsl.js';\nimport randGlsl from './chunks/rand.glsl.js';\nimport random from './chunks/random.glsl.js';\nimport envMap from './chunks/envMap.glsl.js';\nimport sampleMaterialGlsl from './chunks/sampleMaterial.glsl.js';\nimport sampleLightGlsl from './chunks/sampleLight.glsl.js';\nimport { FragmentShader, glsl, Shader } from '../shader.js';\nimport openPbrBsdfGlsl from './chunks/openPbrBsdf.glsl.js';\nimport samplingGlsl from './chunks/sampling.glsl.js';\nimport { isIntelMac } from '../util.js';\nimport uvProjectionGlsl from './chunks/uvProjection.glsl.js';\n\nconst doubleTransmissionCheckCode = glsl`\n#if defined(HAS_TRANSMISSION) || defined(HAS_ALPHA)\n if (!si.isTransparency && BOUNCES < 16 && depth * 2 > BOUNCES) {\n path.abort = true;\n return;\n }\n#endif\n`;\n\n// bounce fix on intel mac\n// The intel mac only have one bounce if the loop is not unrolled\n// But windows will have extremely slow shader compilation if the loop is unrolled\n// So here the code are injected separately.\nconst bounceCodeOnIntelMac = glsl`\n// Mark loop can be unrolled\nfor(int _idx_ = 0; _idx_ < BOUNCES; _idx_++) {{\n if (path.abort) {\n return vec4(path.li, path.alpha);\n }\n bounce(path, depth, si, mat, outAux);\n}}\n`;\nconst bounceCodeOnOther = glsl`\nbool isOpaque = !si.isTransparency;\nint activeBounceLimit = min(BOUNCES, bounceLimit);\nfor(int idx = 0; idx < min(BOUNCES * 3, ${MAX_PATH_EVENTS}); idx++) {\n if (isOpaque && idx > activeBounceLimit) {\n path.abort = true;\n }\n if (path.abort) {\n return vec4(path.li, path.alpha);\n }\n bounce(path, depth, si, mat, outAux);\n}\n`;\n\nexport default function createRayTraceFragmentShader() {\n const intelMac = isIntelMac();\n return new FragmentShader({\n name: 'rayTraceFrag',\n outputs: ['color', 'albedo', 'normal', 'moments'],\n defines: {\n BOUNCES: 2\n },\n uniforms: {\n cameraApertureRadius: Shader.uniform('float', 0),\n cameraFocusDistance: Shader.uniform('float', 10),\n\n viewProjectionInv: Shader.uniform('mat4'),\n viewInv: Shader.uniform('mat4'),\n transmissiveOutputAlphaMin: Shader.uniform('float'),\n openPbrEnergyLut: Shader.uniform('sampler2D'),\n\n viewportSize: Shader.uniform('vec2'),\n\n wavelength: Shader.uniform('float', 0),\n bevelEnabled: Shader.uniform('bool', true),\n dispersionWavelengths: Shader.uniform('int', 3),\n bounceLimit: Shader.uniform('int', 8)\n },\n includes: [rayTraceCore, materialBufferGlsl, intersect, random, envMap],\n main: glsl`\n${commonGlsl}\n\n${randGlsl}\n${random.main}\n\n${textureLinear}\n\n${rayTraceCore.main}\n\n${uvProjectionGlsl}\n\n${materialBufferGlsl.main}\n${intersect.main}\n${envMap.main}\n\n${samplingGlsl}\n${openPbrBsdfGlsl}\n\nfloat heroWavelength;\nfloat pathWavelength;\n\n${sampleLightGlsl}\n${sampleMaterialGlsl}\n\n${cameraRayGlsl}\nstruct RayTraceAuxiliary {\n // Denoising guides and auxiliary pixel classification.\n vec3 albedo;\n vec3 normal;\n bool isBlank;\n // The primary ray hit a shadow catcher, whose sample the catcher pass owns.\n bool isShadowCatcher;\n};\n\nvoid bounce(inout Path path, inout int depth, inout SurfaceInteraction si, inout OpenPbrResolvedMaterial mat, inout RayTraceAuxiliary outAux) {\n samplingBounce = uint(max(depth, 0));\n // The bounce will be doubled on the transmission material.\n // For other materials. We will quite early\n // TODO It is only used on Mac with AMD card. If I enabled it on windows.\n // It will cause shader compilation too slow.\n${intelMac ? doubleTransmissionCheckCode : ''}\n\n bool isShadowCatcher = mat.materialType == SHADOW_CATCHER;\n if (isShadowCatcher) {\n if (path.fromEye) {\n // Match the composed shadow with neutral receiver guides, while keeping\n // background coverage and radiance independent of the invisible ground.\n outAux.albedo = vec3(1.0);\n outAux.normal = dot(si.normal, path.ray.d) > 0.0 ? -si.normal : si.normal;\n si.hit = false;\n outAux.isShadowCatcher = true;\n }\n else {\n // The catcher belongs only to the shadow-mask pass. Keep its default\n // white material out of glossy and transmission reflections.\n#ifdef USE_ENVMAP\n if (path.isNextRefraction) {\n // The invisible catcher can sit between a transmitted camera ray and\n // the grounded skybox. Use the displayed background here instead of\n // terminating early with the raw lighting environment.\n vec4 bg = sampleBackgroundFromRay(path.ray.o, path.ray.d);\n bg.rgb = sampling_spectral_rgb_filter(bg.rgb, pathWavelength);\n float backgroundMisWeight = path.scatteringIsDelta ? 1.0\n : sampling_power_heuristic(path.scatteringPdf,\n envMapPdf(cartesianToEquirect(path.ray.d)) * envLightSelectionPmf);\n path.li += backgroundMisWeight * path.beta * bg.rgb * bg.a;\n path.alpha = max(path.alpha, bg.a);\n }\n else {\n vec2 envUv = cartesianToEquirect(path.ray.d);\n vec3 emission = textureLinear(envMap, envUv).rgb;\n emission = sampling_spectral_rgb_filter(emission, pathWavelength);\n path.li += path.beta * emission *\n sampling_power_heuristic(\n path.scatteringPdf,\n envMapPdf(envUv) * envLightSelectionPmf\n );\n }\n#else\n vec4 bg = sampleBackgroundFromRay(path.ray.o, path.ray.d);\n bg.rgb = sampling_spectral_rgb_filter(bg.rgb, pathWavelength);\n path.li += path.beta * bg.rgb * bg.a;\n#endif\n path.abort = true;\n return;\n }\n }\n\n if (!si.hit) {\n // Sample from background if first ray not hit anything.\n vec4 bg = sampleBackgroundFromRay(path.ray.o, path.ray.d);\n // Dispersion renders one additive pass per RGB wavelength. Background\n // radiance has to be split in the same way as lights and emissive\n // surfaces, otherwise the full skybox is accumulated three times.\n bg.rgb = sampling_spectral_rgb_filter(bg.rgb, pathWavelength);\n if (path.fromEye) {\n path.li = bg.rgb * bg.a;\n path.alpha = bg.a;\n // Only treat as blank pixels when the background is transparent\n outAux.isBlank = bg.a < 0.01;\n }\n else {\n Ray cam = initCameraRay(v_Texcoord + pixelJitter());\n bool towardsCam = dot(cam.d, path.ray.d) > 0.0;\n if (transmissiveOutputAlphaMin < 1.0) {\n if (towardsCam) {\n path.alpha = max(path.alpha, bg.a);\n } else {\n path.alpha = 1.0;\n }\n }\n#if !defined(USE_ENVMAP)\n // TODO treat background as a light source\n if (towardsCam) {\n path.li = path.beta * sampling_power_heuristic(path.scatteringPdf, 1.0) * bg.rgb;\n }\n#endif\n }\n path.abort = true;\n return;\n }\n\n bool sampleIgnored = false;\n if ((mat.materialType == STANDARD || mat.materialType == UNLIT)) {\n if (transmissiveOutputAlphaMin < 1.0) {\n // TODO increase the alpha when there are more bounces.\n float alpha = max(1.0 - mat.transmissionWeight, 1.0 - pow(1.0 - transmissiveOutputAlphaMin, float(depth)));\n path.alpha = max(alpha, path.alpha);\n }\n float alpha = mat.alpha;\n if (alpha < 1.0) {\n // Alpha is sampled as stochastic coverage. An accepted surface branch\n // must use full radiance weight; multiplying by alpha again is biased.\n path.alpha = max(path.alpha, alpha);\n if (hashedSample(3u) > alpha) {\n sampleIgnored = true;\n }\n }\n if (!sampleIgnored) {\n SampleMaterial(si, mat, path);\n }\n else {\n path.ray.o = si.position + path.ray.d * EPS;\n }\n }\n\n if (!si.albedoSampled && !sampleIgnored) {\n // Select denoising guides.\n if ((mat.materialType == STANDARD || mat.materialType == UNLIT) &&\n !(mat.transmissionWeight > 0.5 ||\n (mat.specularRoughness <= 0.02 && mat.baseMetalness > 0.98))) {\n // Alpha-blended decals can be skipped stochastically. In that case keep\n // looking for the first useful rough-surface guide behind the decal,\n // even though it is reached after depth zero.\n outAux.albedo = mat.baseColor;\n outAux.normal = dot(si.normal, si.position - vec3(viewInv[3])) > 0.0\n ? -si.normal\n : si.normal;\n si.albedoSampled = true;\n }\n if (depth > 4) {\n si.albedoSampled = true;\n }\n }\n\n\n // Russian Roulette sampling\n#ifdef USE_RR\n if (depth >= RR_MAX && !sampleIgnored) {\n // Max-component survival (pbrt-v4) avoids over-amplifying saturated paths.\n float maxComponent = max(max(path.beta.x, path.beta.y), path.beta.z);\n float q = max(0.0, 1.0 - maxComponent);\n if (sample1D(bounceDim(RUSSIAN_ROULETTE)) < q) {\n // TODO we force use alpha 1.0 here.\n // To avoid the glass pixels is transparent even if it's on an opaque object.\n path.alpha = 1.0;\n path.abort = true;\n }\n else {\n path.beta /= 1.0 - q;\n }\n }\n#endif\n\n if (path.abort) {\n return;\n }\n\n // Next ray hit\n intersectScene(path.ray, si, true);\n getMaterial(mat, si.materialIndex, si.uv, si.uv1, si.instanceIndex);\n si.isTransparency = mat.transmissionWeight > 0.001 || mat.subsurfaceWeight > 0.001 || mat.alpha < 0.999;\n\n // Dynamic preview only becomes spectral when the primary visible surface\n // actually disperses transmitted light. Direct background and ordinary\n // opaque materials stay full RGB and therefore remain noise-free.\n if (dispersionWavelengths == 1 && si.hit && mat.transmissionWeight > 0.001 && mat.transmissionDispersionScale > 0.001) {\n pathWavelength = heroWavelength;\n }\n if (mat.transmissionWeight > 0.001 && pathWavelength > 0.0) {\n mat.specularIor = sampling_cauchy_ior(\n pathWavelength,\n mat.specularIor,\n mat.transmissionDispersionScale,\n mat.dispersionAbbeNumber\n );\n }\n\n if (sampleIgnored) {\n return;\n }\n\n bool isAlphaOccluded = hashedSample(3u) < mat.alpha;\n#ifdef NUM_LIGHTS\n // Use light if hit the light\n LightSampleRec lightSample;\n for (int i = 0; i < NUM_LIGHTS; i++) {\n // Fetch light Data\n Light light = fetchLight(i);\n float d = intersectLight(path.ray, light, lightSample);\n bool occluded = si.hit && d > distance(si.position, path.ray.o);\n if (d != INF && !(occluded && isAlphaOccluded)) {\n lightSample.pdf *= light.selectionPmf;\n float misWeight = sampling_power_heuristic(path.scatteringPdf, lightSample.pdf);\n lightSample.emission = sampling_spectral_rgb_filter(lightSample.emission, pathWavelength);\n path.li += misWeight * path.beta * lightSample.emission;\n // TODO set alpha to 1.0 will cause some sharp edges\n path.alpha = 1.0;\n path.abort = true;\n }\n }\n#endif\n\n#ifdef USE_ENVMAP\n if (!(si.hit && isAlphaOccluded)) {\n if (path.isNextRefraction) {\n // Camera-visible transmission must use the displayed background. The\n // raw environment remains the lighting source for non-transmission rays.\n vec4 bg = sampleBackgroundFromRay(path.ray.o, path.ray.d);\n bg.rgb = sampling_spectral_rgb_filter(bg.rgb, pathWavelength);\n float backgroundMisWeight = path.scatteringIsDelta ? 1.0\n : sampling_power_heuristic(path.scatteringPdf,\n envMapPdf(cartesianToEquirect(path.ray.d)) * envLightSelectionPmf);\n path.li += backgroundMisWeight * path.beta * bg.rgb * bg.a;\n path.alpha = max(path.alpha, bg.a);\n path.abort = true;\n return;\n }\n vec2 uv = cartesianToEquirect(path.ray.d);\n vec3 emission = textureLinear(envMap, uv).rgb;\n float misWeight = sampling_power_heuristic(\n path.scatteringPdf,\n envMapPdf(uv) * envLightSelectionPmf\n );\n emission = sampling_spectral_rgb_filter(emission, pathWavelength);\n path.li += path.beta * emission * misWeight;\n\n // TODO force the min alpha to be 1 when showing envmap as background\n #if !defined(USE_BACKGROUND)\n path.alpha = 1.0;\n #endif\n path.abort = true;\n return;\n }\n#endif\n\n depth++;\n}\n\n\nvec2 bevelCubicSample(float radius, float xi) {\n float x = 0.25;\n for (int iteration = 0; iteration < 10; iteration++) {\n float x2 = x * x;\n float x3 = x2 * x;\n float oneMinusX = 1.0 - x;\n float value = 10.0 * x2 - 20.0 * x3 + 15.0 * x2 * x2 -\n 4.0 * x2 * x3 - xi;\n float derivative = 20.0 * x * oneMinusX * oneMinusX * oneMinusX;\n if (abs(value) < 1e-6 || derivative == 0.0) break;\n x = clamp(x - value / derivative, 0.0, 1.0);\n }\n float diskRadius = x * radius;\n return vec2(\n diskRadius,\n sqrt(max(radius * radius - diskRadius * diskRadius, 0.0))\n );\n}\n\nfloat bevelCubicPdf(float radius, float distanceToSource) {\n if (distanceToSource >= radius) return 0.0;\n float falloff = radius - distanceToSource;\n return falloff * falloff * falloff;\n}\n\n// Shader bevel. Usage: docs/materials.md (Renderer extensions)\nvec3 getBevelNormal(SurfaceInteraction source, float radius) {\n vec3 geometricNormal = source.faceNormal;\n if (dot(geometricNormal, source.vertexNormal) < 0.0) geometricNormal = -geometricNormal;\n vec3 tangent;\n vec3 bitangent;\n sampling_orthonormal_basis(geometricNormal, tangent, bitangent);\n vec3 normalSum = vec3(0.0);\n\n for (int sampleIndex = 0; sampleIndex < BEVEL_SAMPLES; sampleIndex++) {\n vec2 bevelRnd = sample2D(CAMERA_BEVEL_BASE + uint(sampleIndex));\n float axisSample = bevelRnd.x;\n float radialSample = bevelRnd.y;\n vec3 diskNormal = geometricNormal;\n vec3 diskTangent = tangent;\n vec3 diskBitangent = bitangent;\n float pickPdfNormal = 0.5;\n float pickPdfTangent = 0.25;\n float pickPdfBitangent = 0.25;\n if (axisSample < 0.5) {\n axisSample *= 2.0;\n }\n else if (axisSample < 0.75) {\n vec3 swapAxis = diskNormal;\n diskNormal = diskTangent;\n diskTangent = swapAxis;\n pickPdfNormal = 0.25;\n pickPdfTangent = 0.5;\n axisSample = (axisSample - 0.5) * 4.0;\n }\n else {\n vec3 swapAxis = diskNormal;\n diskNormal = diskBitangent;\n diskBitangent = swapAxis;\n pickPdfNormal = 0.25;\n pickPdfBitangent = 0.5;\n axisSample = (axisSample - 0.75) * 4.0;\n }\n\n vec2 diskSample = bevelCubicSample(radius, radialSample);\n float phi = TWOPI * axisSample;\n vec3 diskOffset = diskSample.x *\n (cos(phi) * diskTangent + sin(phi) * diskBitangent);\n Ray ray;\n initRay(\n ray,\n source.position + diskNormal * diskSample.y + diskOffset,\n -diskNormal\n );\n SurfaceInteraction hit;\n intersectSceneBevel(\n ray,\n hit,\n source.instanceIndex,\n max(2.0 * diskSample.y, EPS)\n );\n if (!hit.hit) continue;\n\n float pdfNormal = pickPdfNormal * abs(dot(diskNormal, hit.faceNormal));\n float pdfTangent = pickPdfTangent * abs(dot(diskTangent, hit.faceNormal));\n float pdfBitangent = pickPdfBitangent * abs(dot(diskBitangent, hit.faceNormal));\n float weight = pdfNormal / max(\n pdfNormal * pdfNormal + pdfTangent * pdfTangent +\n pdfBitangent * pdfBitangent,\n 1e-8\n );\n weight *= bevelCubicPdf(radius, distance(hit.position, source.position)) /\n max(bevelCubicPdf(radius, diskSample.x), 1e-8);\n normalSum += weight * hit.vertexNormal;\n }\n\n if (dot(normalSum, normalSum) <= 1e-12) return source.normal;\n vec3 bevelVertexNormal = normalize(normalSum);\n vec3 result = source.normal + bevelVertexNormal - source.vertexNormal;\n return dot(result, result) > 1e-12 ? normalize(result) : source.normal;\n}\n\nvec4 integrateOnePathSample(SurfaceInteraction si, Ray ray, OpenPbrResolvedMaterial mat, inout RayTraceAuxiliary outAux) {\n Path path;\n path.ray = ray;\n path.li = vec3(0);\n path.alpha = transmissiveOutputAlphaMin < 1.0 ? 0.0 : 1.0;\n path.beta = vec3(1.0);\n path.fromEye = true;\n path.scatteringPdf = 0.0;\n path.scatteringIsDelta = false;\n path.isNextRefraction = false;\n path.abort = false;\n int depth = 0;\n${intelMac ? bounceCodeOnIntelMac : bounceCodeOnOther}\n return vec4(path.li, path.alpha);\n}\n\nvec4 integrateOnePath(SurfaceInteraction si, Ray ray, OpenPbrResolvedMaterial mat, inout RayTraceAuxiliary outAux) {\n float coverage = clamp(mat.alpha, 0.0, 1.0);\n if (!si.hit || (mat.materialType != STANDARD && mat.materialType != UNLIT) || coverage >= 1.0) {\n return integrateOnePathSample(si, ray, mat, outAux);\n }\n\n // glTF BLEND is source-over opacity, not a physical glass boundary. Split\n // the primary ray so reflected radiance is attenuated by alpha every sample\n // while the complementary branch continues straight to the scene behind it.\n // Secondary alpha hits remain stochastic to avoid exponential path growth.\n Ray backRay = ray;\n backRay.o = si.position + ray.d * (EPS * 0.02);\n SurfaceInteraction backSi = si;\n backSi.albedoSampled = false;\n intersectScene(backRay, backSi, false);\n OpenPbrResolvedMaterial backMat;\n getMaterial(backMat, backSi.materialIndex, backSi.uv, backSi.uv1, backSi.instanceIndex);\n RayTraceAuxiliary backAux = outAux;\n if (backSi.hit && (backMat.materialType == STANDARD || backMat.materialType == UNLIT)) {\n backAux.albedo = backMat.transmissionWeight > 0.5\n ? vec3(1.0)\n : backMat.baseColor;\n backAux.normal = dot(\n backSi.normal,\n backSi.position - vec3(viewInv[3])\n ) > 0.0\n ? -backSi.normal\n : backSi.normal;\n backSi.albedoSampled = backMat.transmissionWeight > 0.5;\n }\n vec4 back = integrateOnePathSample(backSi, backRay, backMat, backAux);\n if (coverage <= 0.0) {\n outAux = backAux;\n return back;\n }\n\n SurfaceInteraction frontSi = si;\n OpenPbrResolvedMaterial frontMat = mat;\n frontMat.alpha = 1.0;\n RayTraceAuxiliary frontAux = outAux;\n vec4 front = integrateOnePathSample(frontSi, ray, frontMat, frontAux);\n outAux.albedo = mix(backAux.albedo, frontAux.albedo, coverage);\n vec3 blendedNormal = mix(backAux.normal, frontAux.normal, coverage);\n outAux.normal = dot(blendedNormal, blendedNormal) > 1e-12\n ? normalize(blendedNormal)\n : vec3(0.0);\n outAux.isBlank = backAux.isBlank && frontAux.isBlank;\n return mix(back, front, coverage);\n}\n\n// Path tracing integrator as described in\n// http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Path_Tracing.html#\nvec4 integrator(Ray ray, out RayTraceAuxiliary outAux) {\n\n SurfaceInteraction si;\n OpenPbrResolvedMaterial mat;\n\n si.albedoSampled = false;\n#ifdef HAS_MEDIUM\n si.mediumType = 0;\n si.mediumExtinction = vec3(0.0);\n si.mediumScattering = vec3(0.0);\n si.mediumAnisotropy = 0.0;\n#endif\n\n intersectScene(ray, si, false);\n getMaterial(mat, si.materialIndex, si.uv, si.uv1, si.instanceIndex);\n si.isTransparency = mat.transmissionWeight > 0.001 || mat.subsurfaceWeight > 0.001 || mat.alpha < 0.999;\n\n#ifdef HAS_BEVEL\n if (bevelEnabled && mat.bevel > 0.0) {\n si.normal = getBevelNormal(si, mat.bevel);\n }\n#endif\n\n // Keep a clean first-hit fallback for denoising. Glass retains this stable\n // guide; other delta paths may replace it at their first non-delta hit.\n if (si.hit && (mat.materialType == STANDARD || mat.materialType == UNLIT)) {\n outAux.albedo = mat.transmissionWeight > 0.5 ? vec3(1.0) : mat.baseColor;\n outAux.normal = dot(si.normal, si.position - vec3(viewInv[3])) > 0.0\n ? -si.normal\n : si.normal;\n // A refracted/reflected continuation is stochastic and therefore not a\n // clean OIDN feature. Keep the stable primary glass guide instead.\n si.albedoSampled = mat.transmissionWeight > 0.5;\n }\n\n if (mat.transmissionDispersionScale > 0.001) {\n mat.specularIor = sampling_cauchy_ior(\n pathWavelength,\n mat.specularIor,\n mat.transmissionDispersionScale,\n mat.dispersionAbbeNumber\n );\n }\n return integrateOnePath(si, ray, mat, outAux);\n}\n\nvoid main() {\n\n initRandom();\n rng_initialize(v_Texcoord * viewportSize, int(uint(sampleCount) ^ sampleHash(uint(sequenceSeed))));\n\n\n pathWavelength = dispersionWavelengths == 1 ? 0.0 : wavelength;\n heroWavelength = wavelength;\n if (dispersionWavelengths == 1 && wavelength > 0.0) {\n float channel = floor(sample1D(CAMERA_WAVELENGTH) * 3.0);\n heroWavelength = channel < 1.0 ? 650.0 : channel < 2.0 ? 510.0 : 475.0;\n }\n\n // Thin lens model with depth-of-field\n // http://www.pbr-book.org/3ed-2018/Camera_Models/Projective_Camera_Models.html#TheThinLensModelandDepthofField\n Ray cam = primaryCameraRay();\n\n RayTraceAuxiliary outAux;\n outAux.albedo = vec3(0.0);\n outAux.normal = vec3(0.0);\n outAux.isBlank = false;\n outAux.isShadowCatcher = false;\n\n vec4 liAndAlpha = integrator(cam, outAux);\n\n // Clamp at Integration Level to reduce fireflies.\n // TODO Path level configuration?\n#ifdef CLAMPING\n float clamping = float(CLAMPING);\n liAndAlpha.rgb = clamp(liAndAlpha.rgb, -clamping, clamping);\n#endif\n\n // Each sample will be accumulated three times.\n if (pathWavelength > 0.0) {\n // RGB will be normalized using accumated hit in out_albedo.a\n liAndAlpha.rgb *= 3.0;\n if (dispersionWavelengths != 1) {\n // Full spectral samples use three additive passes. Preview only submits\n // one hero wavelength, so its alpha must not be divided.\n liAndAlpha.a /= 3.0;\n }\n }\n if (!(liAndAlpha.x < INF && liAndAlpha.x > -EPS)) {\n liAndAlpha = vec4(0, 0, 0, 1);\n }\n\n out_color = liAndAlpha;\n out_albedo = vec4(outAux.albedo, outAux.isBlank ? 0.0 : 1.0);\n out_normal = vec4(outAux.normal, 1.0);\n // Moments and count of the built-in denoiser's guide value. The catcher\n // pass accumulates its own samples (see shadowCatcher.frag).\n float guideValue = dot(liAndAlpha.rgb, vec3(0.2126, 0.7152, 0.0722));\n out_moments = outAux.isShadowCatcher\n ? vec4(0.0)\n : vec4(guideValue, guideValue * guideValue, 1.0, 0.0);\n\n}`\n });\n}\n"]}
@@ -7,7 +7,7 @@ export const brightFragment = new FragmentShader({
7
7
  scale: Shader.uniform('float', 1)
8
8
  },
9
9
  main: glsl `
10
- const vec3 uC = vec3(0.2125, 0.7154, 0.0721);
10
+ const vec3 uE = vec3(0.2125, 0.7154, 0.0721);
11
11
  vec4 sm(vec4 a, vec4 b, vec4 c) {
12
12
  return a + b + c - min(min(a, b), c) - max(max(a, b), c);
13
13
  }
@@ -19,7 +19,7 @@ vec4 s2 = sample_colorTex(v_Texcoord + vec2(d.x, 0.0));
19
19
  vec4 s3 = sample_colorTex(v_Texcoord - vec2(0.0, d.y));
20
20
  vec4 s4 = sample_colorTex(v_Texcoord + vec2(0.0, d.y));
21
21
  iW = sm(sm(iW, s1, s2), s3, s4);
22
- float lum = dot(iW.rgb, uC);
22
+ float lum = dot(iW.rgb, uE);
23
23
  out_color = lum > threshold && iW.a > 0.0
24
24
  ? vec4(iW.rgb * scale, iW.a)
25
25
  : vec4(0.0, 0.0, 0.0, iW.a);
@@ -393,6 +393,11 @@ export function materialParametersEqual(material, values) {
393
393
  else if (!Object.is(value, previous))
394
394
  return false;
395
395
  }
396
+ {
397
+ const value = material.displacementBias, previous = values[57];
398
+ if (!Object.is(value, previous) && !arrayParametersEqual(value, previous))
399
+ return false;
400
+ }
396
401
  return true;
397
402
  }
398
403
  /** Mirrors captureMaterialTextures, including numeric texture-category scalars. */