@meimu/mutracer 0.2.0 → 0.3.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.
Files changed (211) hide show
  1. package/README.md +3 -0
  2. package/dist/assets/bvh.worker.js +1 -1
  3. package/dist/mutracer.umd.js +8949 -7915
  4. package/docs/api.md +42 -21
  5. package/docs/caustics.md +80 -0
  6. package/docs/materials.md +5 -4
  7. package/docs/progressive-rendering.md +50 -9
  8. package/docs/technical/continuous-spectrum.md +93 -0
  9. package/docs/updates.md +3 -1
  10. package/docs/viewer-checklist.md +65 -0
  11. package/docs/viewer.md +7 -5
  12. package/lib/Display.js +1 -1
  13. package/lib/Display.js.map +1 -1
  14. package/lib/Engine.d.ts +2 -0
  15. package/lib/Engine.js +47 -12
  16. package/lib/Engine.js.map +1 -1
  17. package/lib/Render.d.ts +61 -13
  18. package/lib/Render.js +169 -19
  19. package/lib/Render.js.map +1 -1
  20. package/lib/RenderLoop.d.ts +7 -3
  21. package/lib/RenderLoop.js +68 -44
  22. package/lib/RenderLoop.js.map +1 -1
  23. package/lib/main.d.ts +1 -1
  24. package/lib/main.js.map +1 -1
  25. package/lib/oidn.d.ts +1 -1
  26. package/lib/oidnBundledWeights.cdn.d.ts +2 -2
  27. package/lib/renderer/GPUTraceQueue.d.ts +2 -1
  28. package/lib/renderer/GPUTraceQueue.js +3 -1
  29. package/lib/renderer/GPUTraceQueue.js.map +1 -1
  30. package/lib/renderer/RenderingPipeline.d.ts +4 -0
  31. package/lib/renderer/RenderingPipeline.js.map +1 -1
  32. package/lib/renderer/WGPUCausticLightPass.d.ts +48 -0
  33. package/lib/renderer/WGPUCausticLightPass.js +217 -0
  34. package/lib/renderer/WGPUCausticLightPass.js.map +1 -0
  35. package/lib/renderer/WGPUEnvMapDistribution.js +18 -18
  36. package/lib/renderer/WGPUFullQuadPass.js +3 -3
  37. package/lib/renderer/WGPUPhotonMap.d.ts +37 -0
  38. package/lib/renderer/WGPUPhotonMap.js +191 -0
  39. package/lib/renderer/WGPUPhotonMap.js.map +1 -0
  40. package/lib/renderer/WGPURayTraceOutput.d.ts +13 -1
  41. package/lib/renderer/WGPURayTraceOutput.js +122 -28
  42. package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
  43. package/lib/renderer/WGPURayTracePass.d.ts +48 -17
  44. package/lib/renderer/WGPURayTracePass.js +538 -340
  45. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  46. package/lib/renderer/WGPURenderingPipeline.js +83 -47
  47. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  48. package/lib/renderer/WebGLFullQuadPass.d.ts +2 -1
  49. package/lib/renderer/WebGLFullQuadPass.js +4 -0
  50. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  51. package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
  52. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  53. package/lib/renderer/WebGLRayTracePass.d.ts +6 -1
  54. package/lib/renderer/WebGLRayTracePass.js +43 -6
  55. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  56. package/lib/renderer/WebGLRenderingPipeline.js +10 -14
  57. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  58. package/lib/renderer/WebGLSkyBaker.js +60 -60
  59. package/lib/renderer/accumulationTarget.d.ts +2 -1
  60. package/lib/renderer/accumulationTarget.js.map +1 -1
  61. package/lib/renderer/causticDiagnostics.d.ts +31 -0
  62. package/lib/renderer/causticDiagnostics.js +5 -0
  63. package/lib/renderer/causticDiagnostics.js.map +1 -0
  64. package/lib/renderer/causticEmission.d.ts +10 -0
  65. package/lib/renderer/causticEmission.js +40 -0
  66. package/lib/renderer/causticEmission.js.map +1 -0
  67. package/lib/renderer/causticUniform.d.ts +12 -0
  68. package/lib/renderer/causticUniform.js +22 -0
  69. package/lib/renderer/causticUniform.js.map +1 -0
  70. package/lib/renderer/caustics.d.ts +16 -0
  71. package/lib/renderer/caustics.js +93 -0
  72. package/lib/renderer/caustics.js.map +1 -0
  73. package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
  74. package/lib/renderer/glsl/aovReflectance.glsl.js +53 -53
  75. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
  76. package/lib/renderer/glsl/aovTrace.frag.js +33 -33
  77. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -9
  78. package/lib/renderer/glsl/chunks/emissiveLight.glsl.d.ts +2 -0
  79. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +35 -0
  80. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js.map +1 -0
  81. package/lib/renderer/glsl/chunks/envMap.glsl.js +73 -73
  82. package/lib/renderer/glsl/chunks/intersect.glsl.js +426 -428
  83. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  84. package/lib/renderer/glsl/chunks/materialBuffer.glsl.d.ts +1 -0
  85. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +156 -153
  86. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
  87. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +771 -771
  88. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  89. package/lib/renderer/glsl/chunks/random.glsl.js +6 -6
  90. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.d.ts +1 -0
  91. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +68 -63
  92. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  93. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +94 -76
  94. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
  95. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -0
  96. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +140 -117
  97. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  98. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +14 -14
  99. package/lib/renderer/glsl/chunks/sampling.glsl.js +89 -92
  100. package/lib/renderer/glsl/chunks/sampling.glsl.js.map +1 -1
  101. package/lib/renderer/glsl/chunks/sobol.glsl.js +79 -79
  102. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  103. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  104. package/lib/renderer/glsl/depthQuery.frag.js +2 -2
  105. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
  106. package/lib/renderer/glsl/rayTrace.frag.d.ts +1 -1
  107. package/lib/renderer/glsl/rayTrace.frag.js +260 -237
  108. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  109. package/lib/renderer/glsl/shadowCatcher.frag.js +26 -26
  110. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  111. package/lib/renderer/glsl/webglPipeline.frag.js +59 -59
  112. package/lib/renderer/lightSampling.d.ts +3 -2
  113. package/lib/renderer/lightSampling.js +14 -8
  114. package/lib/renderer/lightSampling.js.map +1 -1
  115. package/lib/renderer/progressiveScheduler.js +4 -3
  116. package/lib/renderer/progressiveScheduler.js.map +1 -1
  117. package/lib/renderer/renderPipeline.d.ts +1 -0
  118. package/lib/renderer/renderPipeline.js.map +1 -1
  119. package/lib/renderer/researchTransport.d.ts +31 -0
  120. package/lib/renderer/researchTransport.js +5 -0
  121. package/lib/renderer/researchTransport.js.map +1 -0
  122. package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
  123. package/lib/renderer/sceneLayoutSchema.js +2 -2
  124. package/lib/renderer/sceneLayoutSchema.js.map +1 -1
  125. package/lib/renderer/scratchSampling.js +6 -6
  126. package/lib/renderer/spectrum.d.ts +21 -0
  127. package/lib/renderer/spectrum.js +154 -0
  128. package/lib/renderer/spectrum.js.map +1 -0
  129. package/lib/renderer/webglLightSampling.d.ts +39 -0
  130. package/lib/renderer/webglLightSampling.js +15 -0
  131. package/lib/renderer/webglLightSampling.js.map +1 -0
  132. package/lib/renderer/wgpuBindlessSource.js +3 -3
  133. package/lib/renderer/wgpuShaderSource.d.ts +7 -0
  134. package/lib/renderer/wgpuShaderSource.js +2 -0
  135. package/lib/renderer/wgpuShaderSource.js.map +1 -1
  136. package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
  137. package/lib/renderer/wgsl/aovReflectance.wgsl.js +55 -55
  138. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
  139. package/lib/renderer/wgsl/aovResolve.wgsl.js +2 -2
  140. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +1 -1
  141. package/lib/renderer/wgsl/aovTrace.wgsl.js +33 -33
  142. package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
  143. package/lib/renderer/wgsl/causticLight.compute.d.ts +6 -0
  144. package/lib/renderer/wgsl/causticLight.compute.js +537 -0
  145. package/lib/renderer/wgsl/causticLight.compute.js.map +1 -0
  146. package/lib/renderer/wgsl/chunks/background.wgsl.d.ts +2 -0
  147. package/lib/renderer/wgsl/chunks/background.wgsl.js +102 -0
  148. package/lib/renderer/wgsl/chunks/background.wgsl.js.map +1 -0
  149. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.d.ts +2 -0
  150. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +12 -0
  151. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js.map +1 -0
  152. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.d.ts +2 -0
  153. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +16 -0
  154. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js.map +1 -0
  155. package/lib/renderer/wgsl/chunks/caustics.wgsl.d.ts +1 -0
  156. package/lib/renderer/wgsl/chunks/caustics.wgsl.js +62 -0
  157. package/lib/renderer/wgsl/chunks/caustics.wgsl.js.map +1 -0
  158. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  159. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +37 -37
  160. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +598 -592
  161. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  162. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +14 -14
  163. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
  164. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +75 -73
  165. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  166. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +794 -794
  167. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +139 -139
  168. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  169. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +2 -1
  170. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +35 -25
  171. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  172. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +1 -1
  173. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +142 -138
  174. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -1
  175. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
  176. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +204 -187
  177. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  178. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +1 -1
  179. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +11 -11
  180. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -1
  181. package/lib/renderer/wgsl/chunks/sampling.wgsl.d.ts +1 -1
  182. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +89 -93
  183. package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -1
  184. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
  185. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  186. package/lib/renderer/wgsl/compose.frag.wgsl.js +98 -98
  187. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  188. package/lib/renderer/wgsl/depthQuery.wgsl.js +2 -2
  189. package/lib/renderer/wgsl/jnlm.compute.js +74 -74
  190. package/lib/renderer/wgsl/offlineAdaptive.compute.js +25 -25
  191. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
  192. package/lib/renderer/wgsl/photonMap.compute.d.ts +29 -0
  193. package/lib/renderer/wgsl/photonMap.compute.js +352 -0
  194. package/lib/renderer/wgsl/photonMap.compute.js.map +1 -0
  195. package/lib/renderer/wgsl/proceduralSky.compute.js +64 -64
  196. package/lib/renderer/wgsl/rayTrace.compute.d.ts +9 -1
  197. package/lib/renderer/wgsl/rayTrace.compute.js +448 -522
  198. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  199. package/lib/scene/MaterialState.d.ts +2 -2
  200. package/lib/scene/MaterialState.js +2 -2
  201. package/lib/scene/MaterialState.js.map +1 -1
  202. package/lib/settings/render.d.ts +11 -0
  203. package/lib/settings/render.js +1 -0
  204. package/lib/settings/render.js.map +1 -1
  205. package/lib/util/fromTHREE.js +2 -2
  206. package/lib/util/fromTHREE.js.map +1 -1
  207. package/lib/version.d.ts +1 -1
  208. package/lib/version.js +1 -1
  209. package/lib/version.js.map +1 -1
  210. package/llms.txt +2 -1
  211. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"sampleLight.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/sampleLight.glsl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,eAAe,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2PlB,CAAC","sourcesContent":["import { glsl } from '../../shader.js';\n\nexport default glsl`\n#ifdef NUM_LIGHTS\nvoid light_sample_sphere(\n in Light light,\n in vec3 shadingPoint,\n in vec2 samplePoint,\n inout LightSampleRec result\n) {\n vec3 toCenter = light.position - shadingPoint;\n float centerDistance = length(toCenter);\n if (light.radius <= 0.0) {\n if (centerDistance <= 1e-6) {\n result.direction = vec3(0.0, 0.0, 1.0);\n result.normal = vec3(0.0, 0.0, -1.0);\n result.dist = 0.0;\n result.pdf = 1.0;\n result.emission = vec3(0.0);\n result.isDelta = true;\n return;\n }\n result.direction = toCenter / centerDistance;\n result.normal = -result.direction;\n result.dist = centerDistance;\n result.pdf = 1.0;\n result.emission = light.emission / max(centerDistance * centerDistance, 1e-8);\n result.isDelta = true;\n return;\n }\n float radius = max(light.radius, 1e-6);\n result.isDelta = false;\n vec3 direction;\n\n if (centerDistance <= radius) {\n float z = 1.0 - 2.0 * samplePoint.x;\n float radial = sqrt(max(0.0, 1.0 - z * z));\n float azimuth = TWOPI * samplePoint.y;\n direction = vec3(radial * cos(azimuth), radial * sin(azimuth), z);\n result.pdf = INV4PI;\n }\n else {\n vec3 axis = toCenter / centerDistance;\n float sineLimitSquared = min(\n 1.0,\n radius * radius / (centerDistance * centerDistance)\n );\n float cosineLimit = sqrt(max(0.0, 1.0 - sineLimitSquared));\n float cosine = mix(1.0, cosineLimit, samplePoint.x);\n float sine = sqrt(max(0.0, 1.0 - cosine * cosine));\n float azimuth = TWOPI * samplePoint.y;\n vec3 tangent;\n vec3 bitangent;\n sampling_orthonormal_basis(axis, tangent, bitangent);\n direction = axis * cosine\n + tangent * (sine * cos(azimuth))\n + bitangent * (sine * sin(azimuth));\n result.pdf = 1.0 / max(TWOPI * (1.0 - cosineLimit), 1e-8);\n }\n\n float projectedCenter = dot(direction, toCenter);\n float discriminant = projectedCenter * projectedCenter\n - dot(toCenter, toCenter) + radius * radius;\n float root = sqrt(max(0.0, discriminant));\n float nearDistance = projectedCenter - root;\n float farDistance = projectedCenter + root;\n result.dist = nearDistance > 1e-6 ? nearDistance : farDistance;\n result.direction = direction;\n vec3 surfacePoint = shadingPoint + direction * result.dist;\n result.normal = normalize(surfacePoint - light.position);\n result.emission = light.emission;\n}\n\nvoid light_apply_rect_texture(\n in Light light,\n in vec3 surfacePoint,\n in vec3 lowerLeft,\n inout LightSampleRec result\n) {\n#ifdef NUM_MAPS\n if (light.textureIndex >= 0) {\n float width = length(light.u);\n float height = length(light.v);\n vec3 relative = surfacePoint - lowerLeft;\n vec2 uv = vec2(\n dot(relative, light.u / width) / width,\n dot(relative, light.v / height) / height\n );\n result.emission *= sampleMap(light.textureIndex, uv).rgb;\n }\n#endif\n}\n\nvoid light_sample_rect(\n in Light light,\n in vec3 shadingPoint,\n in vec2 samplePoint,\n inout LightSampleRec result\n) {\n vec3 normal = normalize(cross(light.u, light.v));\n vec3 lowerLeft = light.position - 0.5 * (light.u + light.v);\n float spread = clamp(light.spread, 0.0, 1.0);\n\n if (spread <= 0.001) {\n float planeDistance = dot(normal, shadingPoint - light.position);\n vec3 surfacePoint = shadingPoint - normal * planeDistance;\n vec3 relative = surfacePoint - lowerLeft;\n float width = length(light.u);\n float height = length(light.v);\n vec2 uv = vec2(\n dot(relative, light.u / width) / width,\n dot(relative, light.v / height) / height\n );\n if (planeDistance <= 0.0 || any(lessThan(uv, vec2(0.0)))\n || any(greaterThan(uv, vec2(1.0)))) {\n result.emission = vec3(0.0);\n result.pdf = 0.0;\n return;\n }\n result.direction = -normal;\n result.dist = planeDistance;\n result.normal = normal;\n result.emission = light.emission;\n result.pdf = 1.0;\n result.isDelta = true;\n light_apply_rect_texture(light, surfacePoint, lowerLeft, result);\n return;\n }\n\n vec3 surfacePoint = lowerLeft\n + light.u * samplePoint.x\n + light.v * samplePoint.y;\n vec3 offset = surfacePoint - shadingPoint;\n float distanceSquared = dot(offset, offset);\n result.dist = sqrt(distanceSquared);\n result.direction = offset / result.dist;\n result.normal = normal;\n float emitterCosine = dot(-result.direction, normal);\n float spreadCosine = cos(0.5 * PI * spread);\n if (emitterCosine <= 0.0 || emitterCosine < spreadCosine) {\n result.emission = vec3(0.0);\n result.pdf = 0.0;\n return;\n }\n result.emission = light.emission;\n result.pdf = distanceSquared / max(light.area * emitterCosine, 1e-8);\n light_apply_rect_texture(light, surfacePoint, lowerLeft, result);\n}\n\nvoid light_sample_directional(\n in Light light,\n in vec3 shadingPoint,\n inout LightSampleRec result\n) {\n result.direction = normalize(-light.u);\n result.normal = -result.direction;\n result.emission = light.emission;\n result.dist = INF;\n result.pdf = 1.0;\n result.isDelta = true;\n}\n\nvoid light_sample_spot(\n in Light light,\n in vec3 shadingPoint,\n in vec2 samplePoint,\n inout LightSampleRec result\n) {\n light_sample_sphere(light, shadingPoint, samplePoint, result);\n vec3 surfacePoint = shadingPoint + result.direction * result.dist;\n result.emission *= evaluateSpotEmissionMultiplier(\n light,\n normalize(shadingPoint - surfacePoint)\n );\n}\n\nvoid sampleAnalyticLight(\n in Light light,\n in vec3 shadingPoint,\n in vec2 samplePoint,\n inout LightSampleRec result\n) {\n int lightType = int(light.type);\n#ifdef HAS_RECT_LIGHTS\n if (lightType == RECT_LIGHT) {\n light_sample_rect(light, shadingPoint, samplePoint, result);\n return;\n }\n#endif\n#ifdef HAS_SPHERE_LIGHTS\n if (lightType == SPHERE_LIGHT) {\n light_sample_sphere(light, shadingPoint, samplePoint, result);\n return;\n }\n#endif\n#ifdef HAS_SPOT_LIGHTS\n if (lightType == SPOT_LIGHT) {\n light_sample_spot(light, shadingPoint, samplePoint, result);\n return;\n }\n#endif\n#ifdef HAS_DIRECTIONAL_LIGHTS\n if (lightType == DIRECTIONAL_LIGHT) {\n light_sample_directional(light, shadingPoint, result);\n }\n#endif\n}\n#endif\n\nvoid sampleEnvLight(in vec2 samplePoint, inout LightSampleRec result) {\n vec2 uv;\n result.direction = sampleEnvmap(samplePoint, uv, result.pdf);\n result.emission = textureLinear(envMap, uv).rgb;\n result.dist = INF;\n}\n\nvoid sampleLight(\n in vec3 shadingPoint,\n in vec3 randomSampleValue,\n inout LightSampleRec result,\n bool distByIntensity\n) {\n result.isDelta = false;\n float selector = randomSampleValue.z;\n bool choseEnvironment = false;\n#ifdef USE_ENVMAP\n choseEnvironment = selector < envLightSelectionPmf;\n if (choseEnvironment) {\n sampleEnvLight(randomSampleValue.xy, result);\n result.pdf *= envLightSelectionPmf;\n }\n#endif\n\n#ifdef NUM_LIGHTS\n if (!choseEnvironment) {\n int selectedIndex = NUM_LIGHTS - 1;\n for (int index = 0; index < NUM_LIGHTS; index++) {\n if (selector < fetchLight(index).selectionCdf) {\n selectedIndex = index;\n break;\n }\n }\n Light selectedLight = fetchLight(selectedIndex);\n sampleAnalyticLight(\n selectedLight,\n shadingPoint,\n randomSampleValue.xy,\n result\n );\n result.pdf *= selectedLight.selectionPmf;\n }\n#endif\n}\n`;\n"]}
1
+ {"version":3,"file":"sampleLight.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/sampleLight.glsl.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,eAAe,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+NjB,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CT,CAAC","sourcesContent":["import emissive from './emissiveLight.glsl.js';\nimport { glsl } from '../../shader.js';\n\nexport default glsl`\n#ifdef NUM_LIGHTS\nvoid light_sample_sphere(\n in Light light,\n in vec3 shadingPoint,\n in vec2 samplePoint,\n inout LightSampleRec result\n) {\n vec3 toCenter = light.position - shadingPoint;\n float centerDistance = length(toCenter);\n if (light.radius <= 0.0) {\n if (centerDistance <= 1e-6) {\n result.direction = vec3(0.0, 0.0, 1.0);\n result.normal = vec3(0.0, 0.0, -1.0);\n result.dist = 0.0;\n result.pdf = 1.0;\n result.emission = vec3(0.0);\n result.isDelta = true;\n return;\n }\n result.direction = toCenter / centerDistance;\n result.normal = -result.direction;\n result.dist = centerDistance;\n result.pdf = 1.0;\n result.emission = light.emission / max(centerDistance * centerDistance, 1e-8);\n result.isDelta = true;\n return;\n }\n float radius = max(light.radius, 1e-6);\n#ifdef HAS_EMISSIVE_LIGHTS\n sampleEmissiveSceneLight(shadingPoint,randomSampleValue,result);return;\n#endif\n result.sourceType=0;result.sourceIndex=-1;\n result.isDelta = false;\n vec3 direction;\n\n if (centerDistance <= radius) {\n float z = 1.0 - 2.0 * samplePoint.x;\n float radial = sqrt(max(0.0, 1.0 - z * z));\n float azimuth = TWOPI * samplePoint.y;\n direction = vec3(radial * cos(azimuth), radial * sin(azimuth), z);\n result.pdf = INV4PI;\n }\n else {\n vec3 axis = toCenter / centerDistance;\n float sineLimitSquared = min(\n 1.0,\n radius * radius / (centerDistance * centerDistance)\n );\n float cosineLimit = sqrt(max(0.0, 1.0 - sineLimitSquared));\n // Keep the small solid angle and sampled sine without subtracting\n // nearly equal floats; otherwise small emitters produce irradiance bands.\n float oneMinusCosineLimit = sineLimitSquared / (1.0 + cosineLimit);\n float oneMinusCosine = samplePoint.x * oneMinusCosineLimit;\n float cosine = 1.0 - oneMinusCosine;\n float sine = sqrt(max(0.0, oneMinusCosine * (2.0 - oneMinusCosine)));\n float azimuth = TWOPI * samplePoint.y;\n vec3 tangent;\n vec3 bitangent;\n sampling_orthonormal_basis(axis, tangent, bitangent);\n direction = axis * cosine\n + tangent * (sine * cos(azimuth))\n + bitangent * (sine * sin(azimuth));\n result.pdf = 1.0 / max(TWOPI * oneMinusCosineLimit, 1e-8);\n }\n\n float projectedCenter = dot(direction, toCenter);\n float discriminant = projectedCenter * projectedCenter\n - dot(toCenter, toCenter) + radius * radius;\n float root = sqrt(max(0.0, discriminant));\n float nearDistance = projectedCenter - root;\n float farDistance = projectedCenter + root;\n result.dist = nearDistance > 1e-6 ? nearDistance : farDistance;\n result.direction = direction;\n vec3 surfacePoint = shadingPoint + direction * result.dist;\n result.normal = normalize(surfacePoint - light.position);\n result.emission = light.emission;\n}\n\nvoid light_apply_rect_texture(\n in Light light,\n in vec3 surfacePoint,\n in vec3 lowerLeft,\n inout LightSampleRec result\n) {\n#ifdef NUM_MAPS\n if (light.textureIndex >= 0) {\n float width = length(light.u);\n float height = length(light.v);\n vec3 relative = surfacePoint - lowerLeft;\n vec2 uv = vec2(\n dot(relative, light.u / width) / width,\n dot(relative, light.v / height) / height\n );\n result.emission *= sampleMap(light.textureIndex, uv).rgb;\n }\n#endif\n}\n\nvoid light_sample_rect(\n in Light light,\n in vec3 shadingPoint,\n in vec2 samplePoint,\n inout LightSampleRec result\n) {\n vec3 normal = normalize(cross(light.u, light.v));\n vec3 lowerLeft = light.position - 0.5 * (light.u + light.v);\n float spread = clamp(light.spread, 0.0, 1.0);\n\n if (spread <= 0.001) {\n float planeDistance = dot(normal, shadingPoint - light.position);\n vec3 surfacePoint = shadingPoint - normal * planeDistance;\n vec3 relative = surfacePoint - lowerLeft;\n float width = length(light.u);\n float height = length(light.v);\n vec2 uv = vec2(\n dot(relative, light.u / width) / width,\n dot(relative, light.v / height) / height\n );\n if (planeDistance <= 0.0 || any(lessThan(uv, vec2(0.0)))\n || any(greaterThan(uv, vec2(1.0)))) {\n result.emission = vec3(0.0);\n result.pdf = 0.0;\n return;\n }\n result.direction = -normal;\n result.dist = planeDistance;\n result.normal = normal;\n result.emission = light.emission;\n result.pdf = 1.0;\n result.isDelta = true;\n light_apply_rect_texture(light, surfacePoint, lowerLeft, result);\n return;\n }\n\n vec3 surfacePoint = lowerLeft\n + light.u * samplePoint.x\n + light.v * samplePoint.y;\n vec3 offset = surfacePoint - shadingPoint;\n float distanceSquared = dot(offset, offset);\n result.dist = sqrt(distanceSquared);\n result.direction = offset / result.dist;\n result.normal = normal;\n float emitterCosine = dot(-result.direction, normal);\n float spreadCosine = cos(0.5 * PI * spread);\n if (emitterCosine <= 0.0 || emitterCosine < spreadCosine) {\n result.emission = vec3(0.0);\n result.pdf = 0.0;\n return;\n }\n result.emission = light.emission;\n result.pdf = distanceSquared / max(light.area * emitterCosine, 1e-8);\n light_apply_rect_texture(light, surfacePoint, lowerLeft, result);\n}\n\nvoid light_sample_directional(\n in Light light,\n in vec3 shadingPoint,\n inout LightSampleRec result\n) {\n result.direction = normalize(-light.u);\n result.normal = -result.direction;\n result.emission = light.emission;\n result.dist = INF;\n result.pdf = 1.0;\n result.isDelta = true;\n}\n\nvoid light_sample_spot(\n in Light light,\n in vec3 shadingPoint,\n in vec2 samplePoint,\n inout LightSampleRec result\n) {\n light_sample_sphere(light, shadingPoint, samplePoint, result);\n vec3 surfacePoint = shadingPoint + result.direction * result.dist;\n result.emission *= evaluateSpotEmissionMultiplier(\n light,\n normalize(shadingPoint - surfacePoint)\n );\n}\n\nvoid sampleAnalyticLight(\n in Light light,\n in vec3 shadingPoint,\n in vec2 samplePoint,\n inout LightSampleRec result\n) {\n int lightType = int(light.type);\n#ifdef HAS_RECT_LIGHTS\n if (lightType == RECT_LIGHT) {\n light_sample_rect(light, shadingPoint, samplePoint, result);\n return;\n }\n#endif\n#ifdef HAS_SPHERE_LIGHTS\n if (lightType == SPHERE_LIGHT) {\n light_sample_sphere(light, shadingPoint, samplePoint, result);\n return;\n }\n#endif\n#ifdef HAS_SPOT_LIGHTS\n if (lightType == SPOT_LIGHT) {\n light_sample_spot(light, shadingPoint, samplePoint, result);\n return;\n }\n#endif\n#ifdef HAS_DIRECTIONAL_LIGHTS\n if (lightType == DIRECTIONAL_LIGHT) {\n light_sample_directional(light, shadingPoint, result);\n }\n#endif\n}\n#endif\n\nvoid sampleEnvLight(in vec2 samplePoint, inout LightSampleRec result) {\n vec2 uv;\n result.direction = sampleEnvmap(samplePoint, uv, result.pdf);\n result.emission = textureLinear(envMap, uv).rgb;\n result.dist = INF;\n}\n\n#ifdef HAS_EMISSIVE_LIGHTS\n${emissive}\n#endif\n\nvoid sampleLight(\n in vec3 shadingPoint,\n in vec3 randomSampleValue,\n inout LightSampleRec result,\n bool distByIntensity\n) {\n#ifdef HAS_EMISSIVE_LIGHTS\n sampleEmissiveSceneLight(shadingPoint,randomSampleValue,result);return;\n#endif\n result.sourceType=0;result.sourceIndex=-1;\n result.isDelta = false;\n float selector = randomSampleValue.z;\n bool choseEnvironment = false;\n#ifdef USE_ENVMAP\n choseEnvironment = selector < envLightSelectionPmf;\n if (choseEnvironment) {\n sampleEnvLight(randomSampleValue.xy, result);\n result.pdf *= envLightSelectionPmf;\n }\n#endif\n\n#ifdef NUM_LIGHTS\n if (!choseEnvironment) {\n int selectedIndex = NUM_LIGHTS - 1;\n for (int index = 0; index < NUM_LIGHTS; index++) {\n if (selector < fetchLight(index).selectionCdf) {\n selectedIndex = index;\n break;\n }\n }\n result.sourceType=2;result.sourceIndex=selectedIndex;\n Light selectedLight = fetchLight(selectedIndex);\n sampleAnalyticLight(\n selectedLight,\n shadingPoint,\n randomSampleValue.xy,\n result\n );\n result.pdf *= selectedLight.selectionPmf;\n }\n#endif\n}\n`;\n"]}
@@ -1,2 +1,3 @@
1
+ export declare function createSampleMaterialGlsl(fragments?: Readonly<Record<string, string>>): string;
1
2
  declare const _default: string;
2
3
  export default _default;
@@ -1,42 +1,57 @@
1
1
  // Estimate the direct lighting integral using multiple importance sampling
2
2
  // http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Direct_Lighting.html#EstimatingtheDirectLightingIntegral
3
3
  import { glsl } from '../../shader.js';
4
- export default glsl `
5
- vec3 ta(SurfaceInteraction si, OpenPbrResolvedMaterial mat, ag prepared, inout Path aa, vec3 ffnormal, bool eM) {
6
- float lY = 1.0;
7
- vec3 lV = vec3(dZ(cE(LIGHT_POSITION)), dp(cE(LIGHT_SELECT)));
4
+ export function createSampleMaterialGlsl(fragments = {}) {
5
+ return glsl `
6
+ float emissiveHitMisWeight(SurfaceInteraction si,Path path) {
7
+
8
+ float ys=dot(si.faceNormal,-path.ray.d);
9
+ if(ys<=0.0&&fetchMatData(si.materialIndex*MAT_DATA_SIZE+MAT_ROW_ALPHA).z<0.5)return 0.0;
10
+ #ifdef HAS_EMISSIVE_LIGHTS
11
+ if(!path.fromEye && emissiveWeightTotal>0.0) {
12
+ vec3 emission=fetchMatData(si.materialIndex*MAT_DATA_SIZE+MAT_ROW_EMISSION).rgb;
13
+ float value=dot(emission,vec3(.2126,.7152,.0722)),cosine=dot(si.faceNormal,-path.ray.d);
14
+ if(value>0.0&&cosine>0.0){float d=distance(si.position,path.ray.o);return aF(path.scatteringPdf,value/emissiveWeightTotal*d*d/cosine);}
15
+ }
16
+ #endif
17
+ return 1.0;
18
+ }
19
+
20
+ vec3 sX(SurfaceInteraction si, OpenPbrResolvedMaterial mat, OpenPbrPreparedBsdf prepared, inout Path path, vec3 ffnormal, bool isSurface) {
21
+ float lH = 1.0;
22
+ vec3 mZ = vec3(sample2D(bE(LIGHT_POSITION)), sample1D(bE(LIGHT_SELECT)));
8
23
 
9
24
  LightSampleRec lightSample;
10
- sampleLight(si.position, lV, lightSample, false);
11
- lightSample.emission = aj(lightSample.emission, bp);
25
+ sampleLight(si.position, mZ, lightSample, false);${fragments['sample0'] ?? ''}
26
+ lightSample.emission = sampling_spectral_rgb_filter(lightSample.emission, pathChannel);
12
27
 
13
28
  vec3 lightDir = lightSample.direction;
14
- vec3 qY = -aa.ray.d;
15
- float fA = lightSample.pdf;
29
+ vec3 qR = -path.ray.d;
30
+ float lightPdf = lightSample.pdf;
16
31
 
17
32
 
18
33
 
19
34
 
20
35
  float cosThetaL = dot(ffnormal, lightDir);
21
36
  #ifdef USE_COAT_NORMAL_FRAMES
22
- if (eM && mat.coatNormalEnabled) { cosThetaL = dot(mat.geometricNormal, lightDir); }
37
+ if (isSurface && mat.coatNormalEnabled) { cosThetaL = dot(mat.geometricNormal, lightDir); }
23
38
  #endif
24
39
 
25
- if (fA <= 0.0 || max(max(lightSample.emission.x, lightSample.emission.y), lightSample.emission.z) <= 0.0) {
40
+ if (lightPdf <= 0.0 || max(max(lightSample.emission.x, lightSample.emission.y), lightSample.emission.z) <= 0.0) {
26
41
  return vec3(0.0);
27
42
  }
28
43
 
29
44
  float scatteringPdf = 0.0;
30
45
  vec3 f = vec3(0.0);
31
- if (eM) {
46
+ if (isSurface) {
32
47
  if (cosThetaL <= 0.0 && mat.transmissionWeight <= 0.0 && mat.subsurfaceWeight <= 0.0) {
33
48
  return vec3(0.0);
34
49
  }
35
- f = bO(prepared, lightDir, scatteringPdf);
50
+ f = openpbr_eval_prepared(prepared, lightDir, scatteringPdf);
36
51
  }
37
52
  else {
38
- scatteringPdf = bL(
39
- dot(qY, lightDir),
53
+ scatteringPdf = bB(
54
+ dot(qR, lightDir),
40
55
  si.mediumAnisotropy
41
56
  );
42
57
  f = vec3(scatteringPdf);
@@ -46,83 +61,89 @@ if (scatteringPdf <= 0.0 || max(max(f.x, f.y), f.z) <= 0.0) {
46
61
  return vec3(0.0);
47
62
  }
48
63
 
49
- Ray ns;
50
- vec3 hw = si.position + EPS * lightDir;
51
- if (eM) {
52
- const float ch = 0.1;
53
- float dL = dot(si.normal, lightDir);
64
+ Ray mY;
65
+ vec3 eO = si.position + EPS * lightDir;
66
+ if (isSurface) {
67
+ const float shadowTerminatorCutoff = 0.1;
68
+ float dj = dot(si.normal, lightDir);
54
69
  #ifdef USE_COAT_NORMAL_FRAMES
55
- if (mat.coatNormalEnabled) { dL = dot(si.faceNormal, lightDir); }
70
+ if (mat.coatNormalEnabled) { dj = dot(si.faceNormal, lightDir); }
56
71
  #endif
57
- vec3 geometricNormal = dL < 0.0 ? -si.faceNormal : si.faceNormal;
58
- dL = abs(dL);
59
- float iq = dot(geometricNormal, lightDir);
60
- float pq = dL < ch
72
+ vec3 geometricNormal = dj < 0.0 ? -si.faceNormal : si.faceNormal;
73
+ dj = abs(dj);
74
+ float hO = dot(geometricNormal, lightDir);
75
+ float pf = dj < shadowTerminatorCutoff
61
76
  ? clamp(
62
- 2.0 - (iq + dL) / ch,
77
+ 2.0 - (hO + dj) / shadowTerminatorCutoff,
63
78
  0.0,
64
79
  1.0
65
80
  )
66
- : clamp(1.0 - iq / ch, 0.0, 1.0);
67
- hw += si.shadowOffset * pq;
81
+ : clamp(1.0 - hO / shadowTerminatorCutoff, 0.0, 1.0);
82
+ eO += si.shadowOffset * pf;
68
83
  }
69
- gC(ns, hw, lightDir);
70
- if (intersectSceneShadow(ns, lightSample.dist - EPS, si.triangleIndex)) {
84
+ float qz=lightSample.dist-EPS;
85
+ vec3 pO=lightDir;
86
+ if(lightSample.sourceType==1){
87
+ vec3 endpoint=si.position+lightDir*lightSample.dist+EPS*lightSample.normal;
88
+ pO=normalize(endpoint-eO);qz=distance(endpoint,eO);
89
+ }
90
+ initRay(mY, eO, pO);
91
+ if (intersectSceneShadow(mY, qz, si.triangleIndex)) {
71
92
  return vec3(0.0);
72
93
  }
73
94
 
74
95
  #ifdef USE_ENVMAP
75
96
 
76
97
 
77
- if (eM && cosThetaL < 0.0 && lV.z < envLightSelectionPmf) {
78
- vec4 background = cG(ns.o, lightDir);
79
- lightSample.emission = aj(background.rgb * background.a, bp);
98
+ if (isSurface && cosThetaL < 0.0 && lightSample.sourceType == 0) {
99
+ vec4 background = cn(mY.o, lightDir);
100
+ lightSample.emission = sampling_spectral_rgb_filter(background.rgb * background.a, pathChannel);
80
101
  }
81
102
  #endif
82
103
 
83
104
 
84
105
 
85
- float fl = lightSample.isDelta
106
+ float misWeight = lightSample.isDelta
86
107
  ? 1.0
87
- : aK(fA, scatteringPdf);
88
- return fl > 0.0
89
- ? fl * lightSample.emission * f / fA
108
+ : aF(lightPdf, scatteringPdf);
109
+ ${fragments['sample1'] ?? ''} return misWeight > 0.0
110
+ ? misWeight * lightSample.emission * f / lightPdf
90
111
  : vec3(0.0);
91
112
  }
92
113
 
93
114
  #ifdef USE_COAT_NORMAL_FRAMES
94
- vec3 fT(vec3 qt, vec3 face, vec3 view) {
115
+ vec3 fE(vec3 ql, vec3 face, vec3 view) {
95
116
  bool front = dot(face, view) >= 0.0;
96
- vec3 vP = front ? face : -face;
97
- vec3 sc = front ? qt : -qt;
98
- if (dot(qt, face) <= 0.0 || dot(sc, view) <= 1e-6) { return vP; }
99
- return sc;
117
+ vec3 wd = front ? face : -face;
118
+ vec3 rY = front ? ql : -ql;
119
+ if (dot(ql, face) <= 0.0 || dot(rY, view) <= 1e-6) { return wd; }
120
+ return rY;
100
121
  }
101
122
  #endif
102
- void mL(inout SurfaceInteraction si, OpenPbrResolvedMaterial mat, inout Path aa) {
103
- vec3 vR = vec3(dZ(cE(BSDF_DIRECTION)), dp(cE(BSDF_LOBE)));
123
+ void mx(inout SurfaceInteraction si, OpenPbrResolvedMaterial mat, inout Path path) {
124
+ vec3 wh = vec3(sample2D(bE(BSDF_DIRECTION)), sample1D(bE(BSDF_LOBE)));
104
125
  #ifdef USE_COAT_NORMAL_FRAMES
105
- vec3 bK = si.faceNormal;
106
- vec3 nB = si.normal;
126
+ vec3 bA = si.faceNormal;
127
+ vec3 ni = si.normal;
107
128
  #endif
108
- float sO = -dot(si.faceNormal, aa.ray.d);
109
- float tV = -dot(si.normal, aa.ray.d);
129
+ float sL = -dot(si.faceNormal, path.ray.d);
130
+ float tY = -dot(si.normal, path.ray.d);
110
131
  if ((mat.transmissionWeight > 0.0 || mat.subsurfaceWeight > 0.0) && mat.thinWalled) {
111
132
 
112
- if (sO < 0.0) {
133
+ if (sL < 0.0) {
113
134
  si.faceNormal = -si.faceNormal;
114
- sO = -sO;
135
+ sL = -sL;
115
136
  }
116
- if (tV < 0.0) {
137
+ if (tY < 0.0) {
117
138
  si.normal = -si.normal;
118
- tV = -tV;
139
+ tY = -tY;
119
140
  }
120
141
  }
121
142
 
122
143
 
123
- bool qM = sO >= 0.0 && tV < 0.0;
124
- float ndv = qM ? sO : tV;
125
- vec3 normal = qM ? si.faceNormal : si.normal;
144
+ bool qE = sL >= 0.0 && tY < 0.0;
145
+ float ndv = qE ? sL : tY;
146
+ vec3 normal = qE ? si.faceNormal : si.normal;
126
147
 
127
148
  mat.inside = ndv <= 0.0 && !mat.thinWalled;
128
149
 
@@ -130,137 +151,137 @@ mat.relativeIor = ndv > 0.0 ? (1.0 / mat.specularIor) : mat.specularIor;
130
151
  vec3 ffnormal = ndv >= 0.0 ? normal : -normal;
131
152
  #ifdef USE_COAT_NORMAL_FRAMES
132
153
  if (mat.coatNormalEnabled) {
133
- vec3 view = -aa.ray.d;
134
- float gp = dot(bK, view);
135
- mat.geometricNormal = gp >= 0.0 ? bK : -bK;
136
- ffnormal = fT(nB, bK, view);
137
- mat.coatNormal = fT(si.coatNormal, bK, view);
138
- mat.inside = gp <= 0.0 && !mat.thinWalled;
139
- mat.relativeIor = (gp > 0.0 || mat.thinWalled) ? 1.0 / mat.specularIor : mat.specularIor;
154
+ vec3 view = -path.ray.d;
155
+ float fZ = dot(bA, view);
156
+ mat.geometricNormal = fZ >= 0.0 ? bA : -bA;
157
+ ffnormal = fE(ni, bA, view);
158
+ mat.coatNormal = fE(si.coatNormal, bA, view);
159
+ mat.inside = fZ <= 0.0 && !mat.thinWalled;
160
+ mat.relativeIor = (fZ > 0.0 || mat.thinWalled) ? 1.0 / mat.specularIor : mat.specularIor;
140
161
  }
141
162
  #endif
142
163
 
143
164
 
144
165
  float scatteringPdf;
145
- vec3 dH;
146
- aa.scatteringIsDelta = false;
147
- bool bs = false;
148
- bool hC = false;
149
- ag prepared;
166
+ vec3 df;
167
+ path.scatteringIsDelta = false;
168
+ bool mediumSampled = false;
169
+ bool hj = false;
170
+ OpenPbrPreparedBsdf prepared;
150
171
 
151
172
  #ifdef HAS_MEDIUM
152
173
  int mediumType = si.mediumType;
153
- float d = distance(si.position, aa.ray.o);
154
- vec3 os = aa.ray.o;
174
+ float d = distance(si.position, path.ray.o);
175
+ vec3 od = path.ray.o;
155
176
  vec3 extinction = si.mediumExtinction;
156
177
  vec3 scattering = min(si.mediumScattering, extinction);
157
- float tR = max(max(extinction.x, extinction.y), extinction.z);
158
- if (mediumType > 0 && tR > 0.0) {
178
+ float tU = max(max(extinction.x, extinction.y), extinction.z);
179
+ if (mediumType > 0 && tU > 0.0) {
159
180
 
160
181
 
161
182
 
162
183
 
163
- int oi = min(int(dp(cE(MEDIUM_CHANNEL)) * 3.0), 2);
164
- float fS = extinction[oi];
165
- float iH = fS > 0.0
166
- ? min(-log(max(dp(cE(MEDIUM_DISTANCE)), 1e-7)) / fS, d)
184
+ int nX = min(int(sample1D(bE(MEDIUM_CHANNEL)) * 3.0), 2);
185
+ float fD = extinction[nX];
186
+ float ij = fD > 0.0
187
+ ? min(-log(max(sample1D(bE(MEDIUM_DISTANCE)), 1e-7)) / fD, d)
167
188
  : d;
168
- bs = iH < d;
169
- vec3 transmittance = exp(-extinction * iH);
170
- vec3 oU = bs
189
+ mediumSampled = ij < d;
190
+ vec3 transmittance = exp(-extinction * ij);
191
+ vec3 oK = mediumSampled
171
192
  ? extinction * transmittance
172
193
  : transmittance;
173
- float oE = max(
174
- (oU.x + oU.y + oU.z) / 3.0,
194
+ float mediumPdf = max(
195
+ (oK.x + oK.y + oK.z) / 3.0,
175
196
  1e-8
176
197
  );
177
- if (bs) {
178
- aa.beta *= transmittance * scattering / oE;
198
+ if (mediumSampled) {
199
+ path.beta *= transmittance * scattering / mediumPdf;
179
200
 
180
- aa.ray.o = os + iH * aa.ray.d;
181
- si.position = aa.ray.o;
182
- aa.li += ta(si, mat, prepared, aa, ffnormal, false) * aa.beta;
201
+ path.ray.o = od + ij * path.ray.d;
202
+ si.position = path.ray.o;
203
+ path.li += sX(si, mat, prepared, path, ffnormal, false) * path.beta;
183
204
 
184
205
 
185
- dH = fH(
186
- -aa.ray.d,
206
+ df = fj(
207
+ -path.ray.d,
187
208
  si.mediumAnisotropy,
188
- dZ(cE(PHASE_DIRECTION))
209
+ sample2D(bE(PHASE_DIRECTION))
189
210
  );
190
- scatteringPdf = bL(
191
- dot(dH, -aa.ray.d),
211
+ scatteringPdf = bB(
212
+ dot(df, -path.ray.d),
192
213
  si.mediumAnisotropy
193
214
  );
194
215
  }
195
216
  else {
196
- aa.beta *= transmittance / oE;
217
+ path.beta *= transmittance / mediumPdf;
197
218
  }
198
219
  }
199
220
  #endif
200
221
 
201
222
 
202
- if (!bs) {
203
- aa.li += mat.emission * aa.beta;
223
+ if (!mediumSampled) {
224
+ ${fragments['sample2'] ?? ''}path.li += emissiveHitMisWeight(si,path) * mat.emission * path.beta;${fragments['sample3'] ?? ''}
204
225
  if (mat.materialType == UNLIT) {
205
- aa.abort = true;
226
+ path.abort = true;
206
227
  return;
207
228
  }
208
- vec3 qJ = vec3(0.0);
229
+ vec3 qB = vec3(0.0);
209
230
  #ifdef USE_TANGENT
210
- qJ = si.tangent;
231
+ qB = si.tangent;
211
232
  #endif
212
233
  mat.directionTangent = si.directionTangent;
213
234
  mat.directionBitangent = si.directionBitangent;
214
235
  mat.geometryTangent = si.geometryTangent;
215
236
  mat.geometryBitangent = si.geometryBitangent;
216
- prepared = ds(qJ, mat, -aa.ray.d, ffnormal);
217
- aa.li += ta(si, mat, prepared, aa, ffnormal, true) * aa.beta;
218
-
237
+ prepared = bV(qB, mat, -path.ray.d, ffnormal);
238
+ path.li += sX(si, mat, prepared, path, ffnormal, true) * path.beta;
239
+ ${fragments['sample4'] ?? ''}
219
240
  bool isNextRefraction;
220
- vec3 f = eN(
241
+ vec3 f = dl(
221
242
  prepared,
222
- vR,
223
- dH,
243
+ wh,
244
+ df,
224
245
  scatteringPdf,
225
246
  isNextRefraction
226
247
  );
227
248
 
228
- aa.scatteringIsDelta = !prepared.material.thinWalled &&
249
+ path.scatteringIsDelta = !prepared.material.thinWalled &&
229
250
  abs(prepared.material.relativeIor - 1.0) <= 1e-6 &&
230
251
  prepared.probabilities[OPENPBR_LOBE_TRANSMISSION] >= 1.0 - 1e-6;
231
252
 
232
253
 
233
254
 
234
255
  if (!(scatteringPdf > 0.0) || any(isnan(f)) || any(isinf(f)) ||
235
- dot(dH, dH) < 0.5) {
236
- aa.scatteringPdf = 0.0;
237
- aa.abort = true;
256
+ dot(df, df) < 0.5) {
257
+ path.scatteringPdf = 0.0;
258
+ path.abort = true;
238
259
  return;
239
260
  }
240
- aa.beta *= f / scatteringPdf;
241
- hC = !mat.thinWalled &&
261
+ ${fragments['sample5'] ?? ''} path.beta *= f / scatteringPdf;
262
+ hj = !mat.thinWalled &&
242
263
  isNextRefraction;
243
- aa.isNextRefraction = isNextRefraction;
264
+ path.isNextRefraction = isNextRefraction;
244
265
 
245
266
 
246
- aa.ray.o = si.position + EPS * dH;
267
+ path.ray.o = si.position + EPS * df;
247
268
  #ifdef USE_COAT_NORMAL_FRAMES
248
- if (mat.coatNormalEnabled) { aa.ray.o = si.position + EPS * (dot(bK, dH) >= 0.0 ? bK : -bK); }
269
+ if (mat.coatNormalEnabled) { path.ray.o = si.position + EPS * (dot(bA, df) >= 0.0 ? bA : -bA); }
249
270
  #endif
250
271
  }
251
272
 
252
- aa.scatteringPdf = scatteringPdf;
253
- gC(aa.ray, aa.ray.o, dH);
254
- if (bs) {
255
- aa.isNextRefraction = false;
273
+ path.scatteringPdf = scatteringPdf;
274
+ initRay(path.ray, path.ray.o, df);
275
+ if (mediumSampled) {${fragments['sample6'] ?? ''}
276
+ path.isNextRefraction = false;
256
277
  }
257
- aa.fromEye = false;
278
+ path.fromEye = false;
258
279
 
259
280
  #ifdef HAS_MEDIUM
260
281
 
261
282
 
262
- if (!bs && hC && mat.mediumType > 0) {
263
- if (sO >= 0.0) {
283
+ if (!mediumSampled && hj && mat.mediumType > 0) {
284
+ if (sL >= 0.0) {
264
285
  si.mediumType = mat.mediumType;
265
286
  si.mediumExtinction = mat.mediumExtinction;
266
287
  si.mediumScattering = mat.mediumScattering;
@@ -276,4 +297,6 @@ si.mediumAnisotropy = 0.0;
276
297
  #endif
277
298
  }
278
299
  `;
300
+ }
301
+ export default createSampleMaterialGlsl();
279
302
  //# sourceMappingURL=sampleMaterial.glsl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sampleMaterial.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/sampleMaterial.glsl.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,iIAAiI;AAEjI,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,eAAe,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkRlB,CAAC","sourcesContent":["// Estimate the direct lighting integral using multiple importance sampling\n// http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Direct_Lighting.html#EstimatingtheDirectLightingIntegral\n\nimport { glsl } from '../../shader.js';\nexport default glsl`\nvec3 DirectLight(SurfaceInteraction si, OpenPbrResolvedMaterial mat, OpenPbrPreparedBsdf prepared, inout Path path, vec3 ffnormal, bool isSurface) {\n float occluded = 1.0;\n vec3 lightRnd = vec3(sample2D(bounceDim(LIGHT_POSITION)), sample1D(bounceDim(LIGHT_SELECT)));\n\n LightSampleRec lightSample;\n sampleLight(si.position, lightRnd, lightSample, false);\n lightSample.emission = sampling_spectral_rgb_filter(lightSample.emission, pathWavelength);\n\n vec3 lightDir = lightSample.direction;\n vec3 viewDir = -path.ray.d;\n float lightPdf = lightSample.pdf;\n // Opaque surfaces are rendered two-sided by the core renderer. Use the\n // face-forward shading normal here as well, otherwise the early-out rejects\n // every direct-light sample seen from the back of a triangle (for example\n // the inside of an imported room wall).\n float cosThetaL = dot(ffnormal, lightDir);\n#ifdef USE_COAT_NORMAL_FRAMES\n if (isSurface && mat.coatNormalEnabled) { cosThetaL = dot(mat.geometricNormal, lightDir); }\n#endif\n\n if (lightPdf <= 0.0 || max(max(lightSample.emission.x, lightSample.emission.y), lightSample.emission.z) <= 0.0) {\n return vec3(0.0);\n }\n\n float scatteringPdf = 0.0;\n vec3 f = vec3(0.0);\n if (isSurface) {\n if (cosThetaL <= 0.0 && mat.transmissionWeight <= 0.0 && mat.subsurfaceWeight <= 0.0) {\n return vec3(0.0);\n }\n f = openpbr_eval_prepared(prepared, lightDir, scatteringPdf);\n }\n else {\n scatteringPdf = sampling_henyey_greenstein_phase(\n dot(viewDir, lightDir),\n si.mediumAnisotropy\n );\n f = vec3(scatteringPdf);\n }\n\n if (scatteringPdf <= 0.0 || max(max(f.x, f.y), f.z) <= 0.0) {\n return vec3(0.0);\n }\n\n Ray lightRay;\n vec3 shadowOrigin = si.position + EPS * lightDir;\n if (isSurface) {\n const float shadowTerminatorCutoff = 0.1;\n float normalLight = dot(si.normal, lightDir);\n#ifdef USE_COAT_NORMAL_FRAMES\n if (mat.coatNormalEnabled) { normalLight = dot(si.faceNormal, lightDir); }\n#endif\n vec3 geometricNormal = normalLight < 0.0 ? -si.faceNormal : si.faceNormal;\n normalLight = abs(normalLight);\n float geometricLight = dot(geometricNormal, lightDir);\n float offsetAmount = normalLight < shadowTerminatorCutoff\n ? clamp(\n 2.0 - (geometricLight + normalLight) / shadowTerminatorCutoff,\n 0.0,\n 1.0\n )\n : clamp(1.0 - geometricLight / shadowTerminatorCutoff, 0.0, 1.0);\n shadowOrigin += si.shadowOffset * offsetAmount;\n }\n initRay(lightRay, shadowOrigin, lightDir);\n if (intersectSceneShadow(lightRay, lightSample.dist - EPS, si.triangleIndex)) {\n return vec3(0.0);\n }\n\n#ifdef USE_ENVMAP\n // Match the endpoint used by a BSDF-sampled surface transmission, including\n // a separately displayed or grounded background and its coverage.\n if (isSurface && cosThetaL < 0.0 && lightRnd.z < envLightSelectionPmf) {\n vec4 background = sampleBackgroundFromRay(lightRay.o, lightDir);\n lightSample.emission = sampling_spectral_rgb_filter(background.rgb * background.a, pathWavelength);\n }\n#endif\n\n // Delta lights cannot be sampled by the BSDF strategy, so their MIS weight\n // is exactly one even when their light-selection PMF is small.\n float misWeight = lightSample.isDelta\n ? 1.0\n : sampling_power_heuristic(lightPdf, scatteringPdf);\n return misWeight > 0.0\n ? misWeight * lightSample.emission * f / lightPdf\n : vec3(0.0);\n}\n\n#ifdef USE_COAT_NORMAL_FRAMES\nvec3 coat_frame_normal(vec3 inputNormal, vec3 face, vec3 view) {\n bool front = dot(face, view) >= 0.0;\n vec3 ffFace = front ? face : -face;\n vec3 ffInput = front ? inputNormal : -inputNormal;\n if (dot(inputNormal, face) <= 0.0 || dot(ffInput, view) <= 1e-6) { return ffFace; }\n return ffInput;\n}\n#endif\nvoid SampleMaterial(inout SurfaceInteraction si, OpenPbrResolvedMaterial mat, inout Path path) {\n vec3 matRnd = vec3(sample2D(bounceDim(BSDF_DIRECTION)), sample1D(bounceDim(BSDF_LOBE)));\n#ifdef USE_COAT_NORMAL_FRAMES\n vec3 originalFace = si.faceNormal;\n vec3 originalNormal = si.normal;\n#endif\n float ndv1 = -dot(si.faceNormal, path.ray.d);\n float ndv2 = -dot(si.normal, path.ray.d);\n if ((mat.transmissionWeight > 0.0 || mat.subsurfaceWeight > 0.0) && mat.thinWalled) {\n // Always treat thin material as entering medium\n if (ndv1 < 0.0) {\n si.faceNormal = -si.faceNormal;\n ndv1 = -ndv1;\n }\n if (ndv2 < 0.0) {\n si.normal = -si.normal;\n ndv2 = -ndv2;\n }\n }\n // In same edge cases normal is not torwards the camera while we can still see the face.\n // We need to choose faceNormal in this case.\n bool useFaceN = ndv1 >= 0.0 && ndv2 < 0.0;\n float ndv = useFaceN ? ndv1 : ndv2;\n vec3 normal = useFaceN ? si.faceNormal : si.normal;\n\n mat.inside = ndv <= 0.0 && !mat.thinWalled;\n // In or out\n mat.relativeIor = ndv > 0.0 ? (1.0 / mat.specularIor) : mat.specularIor;\n vec3 ffnormal = ndv >= 0.0 ? normal : -normal;\n#ifdef USE_COAT_NORMAL_FRAMES\n if (mat.coatNormalEnabled) {\n vec3 view = -path.ray.d;\n float geometricView = dot(originalFace, view);\n mat.geometricNormal = geometricView >= 0.0 ? originalFace : -originalFace;\n ffnormal = coat_frame_normal(originalNormal, originalFace, view);\n mat.coatNormal = coat_frame_normal(si.coatNormal, originalFace, view);\n mat.inside = geometricView <= 0.0 && !mat.thinWalled;\n mat.relativeIor = (geometricView > 0.0 || mat.thinWalled) ? 1.0 / mat.specularIor : mat.specularIor;\n }\n#endif\n\n\n float scatteringPdf;\n vec3 scatterDir;\n path.scatteringIsDelta = false;\n bool mediumSampled = false;\n bool crossedBoundary = false;\n OpenPbrPreparedBsdf prepared;\n\n#ifdef HAS_MEDIUM\n int mediumType = si.mediumType;\n float d = distance(si.position, path.ray.o);\n vec3 segmentOrigin = path.ray.o;\n vec3 extinction = si.mediumExtinction;\n vec3 scattering = min(si.mediumScattering, extinction);\n float majorant = max(max(extinction.x, extinction.y), extinction.z);\n if (mediumType > 0 && majorant > 0.0) {\n // Sample one RGB extinction channel and divide by the mixture PDF. This\n // is the homogeneous-medium estimator used by PBRT and avoids the very\n // large exp((majorant - extinction) * distance) weights produced by\n // majorant tracking when OpenPBR SSS radii differ strongly by channel.\n int mediumChannel = min(int(sample1D(bounceDim(MEDIUM_CHANNEL)) * 3.0), 2);\n float channelExtinction = extinction[mediumChannel];\n float scatterDist = channelExtinction > 0.0\n ? min(-log(max(sample1D(bounceDim(MEDIUM_DISTANCE)), 1e-7)) / channelExtinction, d)\n : d;\n mediumSampled = scatterDist < d;\n vec3 transmittance = exp(-extinction * scatterDist);\n vec3 density = mediumSampled\n ? extinction * transmittance\n : transmittance;\n float mediumPdf = max(\n (density.x + density.y + density.z) / 3.0,\n 1e-8\n );\n if (mediumSampled) {\n path.beta *= transmittance * scattering / mediumPdf;\n // Move from the start of the segment, not from its end surface.\n path.ray.o = segmentOrigin + scatterDist * path.ray.d;\n si.position = path.ray.o;\n path.li += DirectLight(si, mat, prepared, path, ffnormal, false) * path.beta;\n\n // Pick a new direction based on the phase function\n scatterDir = sampling_henyey_greenstein(\n -path.ray.d,\n si.mediumAnisotropy,\n sample2D(bounceDim(PHASE_DIRECTION))\n );\n scatteringPdf = sampling_henyey_greenstein_phase(\n dot(scatterDir, -path.ray.d),\n si.mediumAnisotropy\n );\n }\n else {\n path.beta *= transmittance / mediumPdf;\n }\n }\n#endif\n\n // Direct lighting\n if (!mediumSampled) {\n path.li += mat.emission * path.beta;\n if (mat.materialType == UNLIT) {\n path.abort = true;\n return;\n }\n vec3 surfaceTangent = vec3(0.0);\n#ifdef USE_TANGENT\n surfaceTangent = si.tangent;\n#endif\n mat.directionTangent = si.directionTangent;\n mat.directionBitangent = si.directionBitangent;\n mat.geometryTangent = si.geometryTangent;\n mat.geometryBitangent = si.geometryBitangent;\n prepared = openpbr_prepare(surfaceTangent, mat, -path.ray.d, ffnormal);\n path.li += DirectLight(si, mat, prepared, path, ffnormal, true) * path.beta;\n // Sample BSDF for color and outgoing direction\n bool isNextRefraction;\n vec3 f = openpbr_sample_prepared(\n prepared,\n matRnd,\n scatterDir,\n scatteringPdf,\n isNextRefraction\n );\n // Null-boundary transmission is discrete and has no NEE competitor.\n path.scatteringIsDelta = !prepared.material.thinWalled &&\n abs(prepared.material.relativeIor - 1.0) <= 1e-6 &&\n prepared.probabilities[OPENPBR_LOBE_TRANSMISSION] >= 1.0 - 1e-6;\n // Match WebGPU: rejected VNDF/refraction proposals have no continuation.\n // Tracing their fallback direction with the incoming throughput invents\n // light paths, especially after total internal reflection inside glass.\n if (!(scatteringPdf > 0.0) || any(isnan(f)) || any(isinf(f)) ||\n dot(scatterDir, scatterDir) < 0.5) {\n path.scatteringPdf = 0.0;\n path.abort = true;\n return;\n }\n path.beta *= f / scatteringPdf;\n crossedBoundary = !mat.thinWalled &&\n isNextRefraction;\n path.isNextRefraction = isNextRefraction;\n // TODO prefer the opaque surface when transmission surface and opaque surface are overlapped.\n // Or the ray go out from transmission material will miss the opque surface.\n path.ray.o = si.position + EPS * scatterDir;\n#ifdef USE_COAT_NORMAL_FRAMES\n if (mat.coatNormalEnabled) { path.ray.o = si.position + EPS * (dot(originalFace, scatterDir) >= 0.0 ? originalFace : -originalFace); }\n#endif\n }\n\n path.scatteringPdf = scatteringPdf;\n initRay(path.ray, path.ray.o, scatterDir);\n if (mediumSampled) {\n path.isNextRefraction = false;\n }\n path.fromEye = false;\n\n#ifdef HAS_MEDIUM\n // A reflected ray stays in its current medium. Only a sampled transmission\n // event crosses the boundary of a medium-bearing closed mesh.\n if (!mediumSampled && crossedBoundary && mat.mediumType > 0) {\n if (ndv1 >= 0.0) {\n si.mediumType = mat.mediumType;\n si.mediumExtinction = mat.mediumExtinction;\n si.mediumScattering = mat.mediumScattering;\n si.mediumAnisotropy = mat.mediumAnisotropy;\n }\n else {\n si.mediumType = 0;\n si.mediumExtinction = vec3(0.0);\n si.mediumScattering = vec3(0.0);\n si.mediumAnisotropy = 0.0;\n }\n }\n#endif\n}\n`;\n"]}
1
+ {"version":3,"file":"sampleMaterial.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/sampleMaterial.glsl.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,iIAAiI;AAEjI,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,MAAM,UAAU,wBAAwB,CAAC,YAA8C,EAAE;IAAI,OAAO,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;mDAoB7C,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoFnF,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmHtB,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,uEAAuE,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE;;;;;;;;;;;;;;;EAe/H,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;EAsB1B,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE;;;;;;;;;;;;;;sBAcJ,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;CAwBjD,CAAC;AACF,CAAC;AACD,eAAe,wBAAwB,EAAE,CAAC","sourcesContent":["// Estimate the direct lighting integral using multiple importance sampling\n// http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Direct_Lighting.html#EstimatingtheDirectLightingIntegral\n\nimport { glsl } from '../../shader.js';\nexport function createSampleMaterialGlsl(fragments: Readonly<Record<string, string>> = {}) { return glsl`\nfloat emissiveHitMisWeight(SurfaceInteraction si,Path path) {\n // Secondary intersections remain two-sided, but one-sided emission does not.\n float facing=dot(si.faceNormal,-path.ray.d);\n if(facing<=0.0&&fetchMatData(si.materialIndex*MAT_DATA_SIZE+MAT_ROW_ALPHA).z<0.5)return 0.0;\n#ifdef HAS_EMISSIVE_LIGHTS\n if(!path.fromEye && emissiveWeightTotal>0.0) {\n vec3 emission=fetchMatData(si.materialIndex*MAT_DATA_SIZE+MAT_ROW_EMISSION).rgb;\n float value=dot(emission,vec3(.2126,.7152,.0722)),cosine=dot(si.faceNormal,-path.ray.d);\n if(value>0.0&&cosine>0.0){float d=distance(si.position,path.ray.o);return sampling_power_heuristic(path.scatteringPdf,value/emissiveWeightTotal*d*d/cosine);}\n }\n#endif\n return 1.0;\n}\n\nvec3 DirectLight(SurfaceInteraction si, OpenPbrResolvedMaterial mat, OpenPbrPreparedBsdf prepared, inout Path path, vec3 ffnormal, bool isSurface) {\n float occluded = 1.0;\n vec3 lightRnd = vec3(sample2D(bounceDim(LIGHT_POSITION)), sample1D(bounceDim(LIGHT_SELECT)));\n\n LightSampleRec lightSample;\n sampleLight(si.position, lightRnd, lightSample, false);${fragments['sample0'] ?? ''}\n lightSample.emission = sampling_spectral_rgb_filter(lightSample.emission, pathChannel);\n\n vec3 lightDir = lightSample.direction;\n vec3 viewDir = -path.ray.d;\n float lightPdf = lightSample.pdf;\n // Opaque surfaces are rendered two-sided by the core renderer. Use the\n // face-forward shading normal here as well, otherwise the early-out rejects\n // every direct-light sample seen from the back of a triangle (for example\n // the inside of an imported room wall).\n float cosThetaL = dot(ffnormal, lightDir);\n#ifdef USE_COAT_NORMAL_FRAMES\n if (isSurface && mat.coatNormalEnabled) { cosThetaL = dot(mat.geometricNormal, lightDir); }\n#endif\n\n if (lightPdf <= 0.0 || max(max(lightSample.emission.x, lightSample.emission.y), lightSample.emission.z) <= 0.0) {\n return vec3(0.0);\n }\n\n float scatteringPdf = 0.0;\n vec3 f = vec3(0.0);\n if (isSurface) {\n if (cosThetaL <= 0.0 && mat.transmissionWeight <= 0.0 && mat.subsurfaceWeight <= 0.0) {\n return vec3(0.0);\n }\n f = openpbr_eval_prepared(prepared, lightDir, scatteringPdf);\n }\n else {\n scatteringPdf = sampling_henyey_greenstein_phase(\n dot(viewDir, lightDir),\n si.mediumAnisotropy\n );\n f = vec3(scatteringPdf);\n }\n\n if (scatteringPdf <= 0.0 || max(max(f.x, f.y), f.z) <= 0.0) {\n return vec3(0.0);\n }\n\n Ray lightRay;\n vec3 shadowOrigin = si.position + EPS * lightDir;\n if (isSurface) {\n const float shadowTerminatorCutoff = 0.1;\n float normalLight = dot(si.normal, lightDir);\n#ifdef USE_COAT_NORMAL_FRAMES\n if (mat.coatNormalEnabled) { normalLight = dot(si.faceNormal, lightDir); }\n#endif\n vec3 geometricNormal = normalLight < 0.0 ? -si.faceNormal : si.faceNormal;\n normalLight = abs(normalLight);\n float geometricLight = dot(geometricNormal, lightDir);\n float offsetAmount = normalLight < shadowTerminatorCutoff\n ? clamp(\n 2.0 - (geometricLight + normalLight) / shadowTerminatorCutoff,\n 0.0,\n 1.0\n )\n : clamp(1.0 - geometricLight / shadowTerminatorCutoff, 0.0, 1.0);\n shadowOrigin += si.shadowOffset * offsetAmount;\n }\n float shadowDistance=lightSample.dist-EPS;\n vec3 shadowDirection=lightDir;\n if(lightSample.sourceType==1){\n vec3 endpoint=si.position+lightDir*lightSample.dist+EPS*lightSample.normal;\n shadowDirection=normalize(endpoint-shadowOrigin);shadowDistance=distance(endpoint,shadowOrigin);\n }\n initRay(lightRay, shadowOrigin, shadowDirection);\n if (intersectSceneShadow(lightRay, shadowDistance, si.triangleIndex)) {\n return vec3(0.0);\n }\n\n#ifdef USE_ENVMAP\n // Match the endpoint used by a BSDF-sampled surface transmission, including\n // a separately displayed or grounded background and its coverage.\n if (isSurface && cosThetaL < 0.0 && lightSample.sourceType == 0) {\n vec4 background = sampleBackgroundFromRay(lightRay.o, lightDir);\n lightSample.emission = sampling_spectral_rgb_filter(background.rgb * background.a, pathChannel);\n }\n#endif\n\n // Delta lights cannot be sampled by the BSDF strategy, so their MIS weight\n // is exactly one even when their light-selection PMF is small.\n float misWeight = lightSample.isDelta\n ? 1.0\n : sampling_power_heuristic(lightPdf, scatteringPdf);\n${fragments['sample1'] ?? ''} return misWeight > 0.0\n ? misWeight * lightSample.emission * f / lightPdf\n : vec3(0.0);\n}\n\n#ifdef USE_COAT_NORMAL_FRAMES\nvec3 coat_frame_normal(vec3 inputNormal, vec3 face, vec3 view) {\n bool front = dot(face, view) >= 0.0;\n vec3 ffFace = front ? face : -face;\n vec3 ffInput = front ? inputNormal : -inputNormal;\n if (dot(inputNormal, face) <= 0.0 || dot(ffInput, view) <= 1e-6) { return ffFace; }\n return ffInput;\n}\n#endif\nvoid SampleMaterial(inout SurfaceInteraction si, OpenPbrResolvedMaterial mat, inout Path path) {\n vec3 matRnd = vec3(sample2D(bounceDim(BSDF_DIRECTION)), sample1D(bounceDim(BSDF_LOBE)));\n#ifdef USE_COAT_NORMAL_FRAMES\n vec3 originalFace = si.faceNormal;\n vec3 originalNormal = si.normal;\n#endif\n float ndv1 = -dot(si.faceNormal, path.ray.d);\n float ndv2 = -dot(si.normal, path.ray.d);\n if ((mat.transmissionWeight > 0.0 || mat.subsurfaceWeight > 0.0) && mat.thinWalled) {\n // Always treat thin material as entering medium\n if (ndv1 < 0.0) {\n si.faceNormal = -si.faceNormal;\n ndv1 = -ndv1;\n }\n if (ndv2 < 0.0) {\n si.normal = -si.normal;\n ndv2 = -ndv2;\n }\n }\n // In same edge cases normal is not torwards the camera while we can still see the face.\n // We need to choose faceNormal in this case.\n bool useFaceN = ndv1 >= 0.0 && ndv2 < 0.0;\n float ndv = useFaceN ? ndv1 : ndv2;\n vec3 normal = useFaceN ? si.faceNormal : si.normal;\n\n mat.inside = ndv <= 0.0 && !mat.thinWalled;\n // In or out\n mat.relativeIor = ndv > 0.0 ? (1.0 / mat.specularIor) : mat.specularIor;\n vec3 ffnormal = ndv >= 0.0 ? normal : -normal;\n#ifdef USE_COAT_NORMAL_FRAMES\n if (mat.coatNormalEnabled) {\n vec3 view = -path.ray.d;\n float geometricView = dot(originalFace, view);\n mat.geometricNormal = geometricView >= 0.0 ? originalFace : -originalFace;\n ffnormal = coat_frame_normal(originalNormal, originalFace, view);\n mat.coatNormal = coat_frame_normal(si.coatNormal, originalFace, view);\n mat.inside = geometricView <= 0.0 && !mat.thinWalled;\n mat.relativeIor = (geometricView > 0.0 || mat.thinWalled) ? 1.0 / mat.specularIor : mat.specularIor;\n }\n#endif\n\n\n float scatteringPdf;\n vec3 scatterDir;\n path.scatteringIsDelta = false;\n bool mediumSampled = false;\n bool crossedBoundary = false;\n OpenPbrPreparedBsdf prepared;\n\n#ifdef HAS_MEDIUM\n int mediumType = si.mediumType;\n float d = distance(si.position, path.ray.o);\n vec3 segmentOrigin = path.ray.o;\n vec3 extinction = si.mediumExtinction;\n vec3 scattering = min(si.mediumScattering, extinction);\n float majorant = max(max(extinction.x, extinction.y), extinction.z);\n if (mediumType > 0 && majorant > 0.0) {\n // Sample one RGB extinction channel and divide by the mixture PDF. This\n // is the homogeneous-medium estimator used by PBRT and avoids the very\n // large exp((majorant - extinction) * distance) weights produced by\n // majorant tracking when OpenPBR SSS radii differ strongly by channel.\n int mediumChannel = min(int(sample1D(bounceDim(MEDIUM_CHANNEL)) * 3.0), 2);\n float channelExtinction = extinction[mediumChannel];\n float scatterDist = channelExtinction > 0.0\n ? min(-log(max(sample1D(bounceDim(MEDIUM_DISTANCE)), 1e-7)) / channelExtinction, d)\n : d;\n mediumSampled = scatterDist < d;\n vec3 transmittance = exp(-extinction * scatterDist);\n vec3 density = mediumSampled\n ? extinction * transmittance\n : transmittance;\n float mediumPdf = max(\n (density.x + density.y + density.z) / 3.0,\n 1e-8\n );\n if (mediumSampled) {\n path.beta *= transmittance * scattering / mediumPdf;\n // Move from the start of the segment, not from its end surface.\n path.ray.o = segmentOrigin + scatterDist * path.ray.d;\n si.position = path.ray.o;\n path.li += DirectLight(si, mat, prepared, path, ffnormal, false) * path.beta;\n\n // Pick a new direction based on the phase function\n scatterDir = sampling_henyey_greenstein(\n -path.ray.d,\n si.mediumAnisotropy,\n sample2D(bounceDim(PHASE_DIRECTION))\n );\n scatteringPdf = sampling_henyey_greenstein_phase(\n dot(scatterDir, -path.ray.d),\n si.mediumAnisotropy\n );\n }\n else {\n path.beta *= transmittance / mediumPdf;\n }\n }\n#endif\n\n // Direct lighting\n if (!mediumSampled) {\n ${fragments['sample2'] ?? ''}path.li += emissiveHitMisWeight(si,path) * mat.emission * path.beta;${fragments['sample3'] ?? ''}\n if (mat.materialType == UNLIT) {\n path.abort = true;\n return;\n }\n vec3 surfaceTangent = vec3(0.0);\n#ifdef USE_TANGENT\n surfaceTangent = si.tangent;\n#endif\n mat.directionTangent = si.directionTangent;\n mat.directionBitangent = si.directionBitangent;\n mat.geometryTangent = si.geometryTangent;\n mat.geometryBitangent = si.geometryBitangent;\n prepared = openpbr_prepare(surfaceTangent, mat, -path.ray.d, ffnormal);\n path.li += DirectLight(si, mat, prepared, path, ffnormal, true) * path.beta;\n${fragments['sample4'] ?? ''} // Sample BSDF for color and outgoing direction\n bool isNextRefraction;\n vec3 f = openpbr_sample_prepared(\n prepared,\n matRnd,\n scatterDir,\n scatteringPdf,\n isNextRefraction\n );\n // Null-boundary transmission is discrete and has no NEE competitor.\n path.scatteringIsDelta = !prepared.material.thinWalled &&\n abs(prepared.material.relativeIor - 1.0) <= 1e-6 &&\n prepared.probabilities[OPENPBR_LOBE_TRANSMISSION] >= 1.0 - 1e-6;\n // Match WebGPU: rejected VNDF/refraction proposals have no continuation.\n // Tracing their fallback direction with the incoming throughput invents\n // light paths, especially after total internal reflection inside glass.\n if (!(scatteringPdf > 0.0) || any(isnan(f)) || any(isinf(f)) ||\n dot(scatterDir, scatterDir) < 0.5) {\n path.scatteringPdf = 0.0;\n path.abort = true;\n return;\n }\n${fragments['sample5'] ?? ''} path.beta *= f / scatteringPdf;\n crossedBoundary = !mat.thinWalled &&\n isNextRefraction;\n path.isNextRefraction = isNextRefraction;\n // TODO prefer the opaque surface when transmission surface and opaque surface are overlapped.\n // Or the ray go out from transmission material will miss the opque surface.\n path.ray.o = si.position + EPS * scatterDir;\n#ifdef USE_COAT_NORMAL_FRAMES\n if (mat.coatNormalEnabled) { path.ray.o = si.position + EPS * (dot(originalFace, scatterDir) >= 0.0 ? originalFace : -originalFace); }\n#endif\n }\n\n path.scatteringPdf = scatteringPdf;\n initRay(path.ray, path.ray.o, scatterDir);\n if (mediumSampled) {${fragments['sample6'] ?? ''}\n path.isNextRefraction = false;\n }\n path.fromEye = false;\n\n#ifdef HAS_MEDIUM\n // A reflected ray stays in its current medium. Only a sampled transmission\n // event crosses the boundary of a medium-bearing closed mesh.\n if (!mediumSampled && crossedBoundary && mat.mediumType > 0) {\n if (ndv1 >= 0.0) {\n si.mediumType = mat.mediumType;\n si.mediumExtinction = mat.mediumExtinction;\n si.mediumScattering = mat.mediumScattering;\n si.mediumAnisotropy = mat.mediumAnisotropy;\n }\n else {\n si.mediumType = 0;\n si.mediumExtinction = vec3(0.0);\n si.mediumScattering = vec3(0.0);\n si.mediumAnisotropy = 0.0;\n }\n }\n#endif\n}\n`;\n}\nexport default createSampleMaterialGlsl();\n"]}
@@ -12,39 +12,39 @@ export default glsl `
12
12
 
13
13
  const uint SHADOW_CATCHER_LIGHT_SAMPLES = 4u;
14
14
 
15
- uint lc(uint candidate, uint slot) {
16
- return candidate == 0u ? cE(slot) : cE(9u + (candidate - 1u) * 3u + slot);
15
+ uint kJ(uint candidate, uint slot) {
16
+ return candidate == 0u ? bE(slot) : bE(9u + (candidate - 1u) * 3u + slot);
17
17
  }
18
18
 
19
19
 
20
20
 
21
- vec2 sampleShadowCatcher(SurfaceInteraction si, int bounce, inout Path aa) {
22
- vec3 qY = -aa.ray.d;
21
+ vec2 sampleShadowCatcher(SurfaceInteraction si, int bounce, inout Path path) {
22
+ vec3 qR = -path.ray.d;
23
23
  vec2 counts = vec2(0.0);
24
24
 
25
25
  #if defined(NUM_LIGHTS) || defined(USE_ENVMAP)
26
26
  for (uint candidate = 0u; candidate < SHADOW_CATCHER_LIGHT_SAMPLES; candidate++) {
27
27
  LightSampleRec lightSample;
28
- vec3 lV = vec3(
29
- dZ(lc(candidate, LIGHT_POSITION)),
30
- dp(lc(candidate, LIGHT_SELECT))
28
+ vec3 mZ = vec3(
29
+ sample2D(kJ(candidate, LIGHT_POSITION)),
30
+ sample1D(kJ(candidate, LIGHT_SELECT))
31
31
  );
32
- sampleLight(si.position, lV, lightSample, false);
32
+ sampleLight(si.position, mZ, lightSample, false);
33
33
 
34
34
  vec3 lightDir = lightSample.direction;
35
35
  float cosThetaL = dot(si.normal, lightDir);
36
- float qB = dot(si.normal, qY) * cosThetaL;
37
- if (lightSample.pdf > 0.0 && qB >= 0.0) {
36
+ float qt = dot(si.normal, qR) * cosThetaL;
37
+ if (lightSample.pdf > 0.0 && qt >= 0.0) {
38
38
  if (dot(lightSample.emission, luminance) * abs(cosThetaL) > 0.0) {
39
39
  Ray ray;
40
- gC(ray, si.position + EPS * lightDir, lightDir);
41
- bool lY = intersectSceneShadow(ray, lightSample.dist - EPS, si.triangleIndex);
42
- counts += vec2(lY ? 1.0 : 0.0, 1.0);
40
+ initRay(ray, si.position + EPS * lightDir, lightDir);
41
+ bool lH = intersectSceneShadow(ray, lightSample.dist - EPS, si.triangleIndex);
42
+ counts += vec2(lH ? 1.0 : 0.0, 1.0);
43
43
  }
44
44
  }
45
45
  }
46
46
  #endif
47
- aa.fromEye = false;
47
+ path.fromEye = false;
48
48
  return counts;
49
49
  }
50
50
  `;