@meimu/mutracer 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +4475 -4337
- package/docs/api.md +11 -9
- package/docs/threejs.md +30 -4
- package/lib/Engine.js +18 -1
- package/lib/Engine.js.map +1 -1
- package/lib/main.d.ts +1 -1
- package/lib/main.js.map +1 -1
- package/lib/oidn.d.ts +1 -1
- package/lib/oidnBundledWeights.cdn.d.ts +2 -2
- package/lib/renderer/MaterialTable.d.ts +1 -0
- package/lib/renderer/MaterialTable.js +10 -4
- package/lib/renderer/MaterialTable.js.map +1 -1
- package/lib/renderer/WGPUBloomPass.js +18 -18
- package/lib/renderer/WGPUFullQuadPass.js +6 -6
- package/lib/renderer/WGPUJNLMDenoiser.js +68 -68
- package/lib/renderer/WGPURayTraceOutput.js +16 -14
- package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
- package/lib/renderer/WGPURayTracePass.d.ts +2 -0
- package/lib/renderer/WGPURayTracePass.js +13 -7
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.js +24 -22
- package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLJNLMDenoiser.js +68 -68
- package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
- package/lib/renderer/WebGLRayTracePass.d.ts +1 -0
- package/lib/renderer/WebGLRayTracePass.js +7 -1
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/envMapCreation.d.ts +5 -1
- package/lib/renderer/envMapCreation.js +42 -5
- package/lib/renderer/envMapCreation.js.map +1 -1
- package/lib/renderer/geometryUpdates.js +1 -1
- package/lib/renderer/geometryUpdates.js.map +1 -1
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +12 -12
- package/lib/renderer/glsl/chunks/envMap.glsl.js +69 -57
- package/lib/renderer/glsl/chunks/envMap.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/intersect.glsl.js +316 -310
- package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +153 -121
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +540 -540
- package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
- package/lib/renderer/glsl/chunks/random.glsl.js +4 -4
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +40 -39
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js +71 -71
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +84 -84
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +13 -13
- package/lib/renderer/glsl/chunks/sampling.glsl.js +77 -77
- package/lib/renderer/glsl/chunks/sobol.glsl.js +65 -65
- package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
- package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
- package/lib/renderer/glsl/depthQuery.frag.js +5 -5
- package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +8 -8
- package/lib/renderer/glsl/rayTrace.frag.js +165 -165
- package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/shadowCatcher.frag.js +14 -14
- package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
- package/lib/renderer/glsl/webglPipeline.frag.js +56 -56
- package/lib/renderer/lightsUtil.js +7 -5
- package/lib/renderer/lightsUtil.js.map +1 -1
- package/lib/renderer/materialChanges.js +1 -1
- package/lib/renderer/materialChanges.js.map +1 -1
- package/lib/renderer/materialComparison.generated.js +36 -18
- package/lib/renderer/materialComparison.generated.js.map +1 -1
- package/lib/renderer/prepareMeshBVH.js +2 -2
- package/lib/renderer/prepareMeshBVH.js.map +1 -1
- package/lib/renderer/sceneChangeDetector.js +4 -1
- package/lib/renderer/sceneChangeDetector.js.map +1 -1
- package/lib/renderer/sceneLightChanges.js +3 -2
- package/lib/renderer/sceneLightChanges.js.map +1 -1
- package/lib/renderer/scratchSampling.js +18 -18
- package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/envMap.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +39 -31
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +475 -469
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +8 -8
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +95 -61
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +562 -562
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +117 -120
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +25 -22
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +128 -128
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +143 -143
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +6 -6
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js +75 -75
- package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.d.ts +3 -3
- package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.js +2 -2
- package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +69 -69
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
- package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -2
- package/lib/renderer/wgsl/compose.frag.wgsl.js +130 -102
- package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
- package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/depthQuery.wgsl.js +5 -5
- package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +28 -28
- package/lib/renderer/wgsl/rayTrace.compute.js +289 -277
- package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
- package/lib/scene/EnvironmentLight.d.ts +9 -3
- package/lib/scene/EnvironmentLight.js +9 -2
- package/lib/scene/EnvironmentLight.js.map +1 -1
- package/lib/scene/Scene.d.ts +4 -0
- package/lib/scene/Scene.js.map +1 -1
- package/lib/scene/SpotLight.d.ts +2 -2
- package/lib/scene/SpotLight.js +1 -1
- package/lib/scene/SpotLight.js.map +1 -1
- package/lib/scene/Texture.d.ts +17 -6
- package/lib/scene/Texture.js +13 -4
- package/lib/scene/Texture.js.map +1 -1
- package/lib/util/fromTHREE.js +151 -29
- package/lib/util/fromTHREE.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
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(environmentUv(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 = environmentUv(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, si.vertexColor);\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(environmentUv(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 = environmentUv(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, backSi.vertexColor);\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, si.vertexColor);\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"]}
|
|
@@ -52,7 +52,7 @@ ${cameraRayGlsl}
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
vec4
|
|
55
|
+
vec4 my(inout Ray ray, out vec4 background) {
|
|
56
56
|
background = vec4(0.0);
|
|
57
57
|
Path aa;
|
|
58
58
|
aa.ray = ray;
|
|
@@ -65,7 +65,7 @@ aa.abort = false;
|
|
|
65
65
|
|
|
66
66
|
ad si;
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
cZ(aa.ray, si, false);
|
|
69
69
|
if (!si.hit) {
|
|
70
70
|
|
|
71
71
|
|
|
@@ -74,14 +74,14 @@ return vec4(0.0, 0.0, 0.0, 1.0);
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
J mat;
|
|
77
|
-
|
|
77
|
+
eX(mat, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);
|
|
78
78
|
if (mat.materialType != SHADOW_CATCHER) {
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
return vec4(0.0, 0.0, 0.0, 1.0);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
vec4 bg =
|
|
84
|
+
vec4 bg = cx(ray.o, ray.d);
|
|
85
85
|
background = vec4(bg.rgb * bg.a, bg.a);
|
|
86
86
|
if (!aa.abort) {
|
|
87
87
|
sampleShadowCatcher(si, 0, aa);
|
|
@@ -90,28 +90,28 @@ return vec4(1.0, bg.a, bg.a >= 0.01 ? 1.0 : 0.0, aa.alpha);
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
void main() {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
su();
|
|
94
|
+
kh(v_Texcoord * viewportSize, int(uint(sampleCount) ^ eg(uint(sequenceSeed))));
|
|
95
95
|
|
|
96
96
|
if (dispersionWavelengths == 1 && wavelength > 0.0) { dz(CAMERA_WAVELENGTH); }
|
|
97
|
-
Ray cam =
|
|
97
|
+
Ray cam = nN();
|
|
98
98
|
vec4 background;
|
|
99
|
-
vec4
|
|
99
|
+
vec4 aF = my(cam, background);
|
|
100
100
|
|
|
101
|
-
if (!(
|
|
102
|
-
|
|
101
|
+
if (!(aF.x < INF && aF.x > -EPS)) {
|
|
102
|
+
aF = vec4(0, 0, 0, 1);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
out_shadowCatcher =
|
|
105
|
+
out_shadowCatcher = aF;
|
|
106
106
|
out_catcherBackground = background;
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
|
|
110
|
-
float
|
|
110
|
+
float jq = aF.a * (background.a >= 0.01
|
|
111
111
|
? dot(background.rgb / background.a, vec3(0.2126, 0.7152, 0.0722))
|
|
112
112
|
: 1.0);
|
|
113
|
-
out_catcherMoments =
|
|
114
|
-
? vec4(
|
|
113
|
+
out_catcherMoments = aF.r > 0.0
|
|
114
|
+
? vec4(jq, jq * jq, 1.0, 0.0)
|
|
115
115
|
: vec4(0.0);
|
|
116
116
|
|
|
117
117
|
}`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadowCatcher.frag.js","sourceRoot":"","sources":["../../../src/renderer/glsl/shadowCatcher.frag.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,sDAAsD;AACtD,kFAAkF;AAElF,OAAO,aAAa,MAAM,4BAA4B,CAAC;AACvD,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,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,mBAAmB,MAAM,sCAAsC,CAAC;AACvE,OAAO,WAAW,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,YAAY,MAAM,2BAA2B,CAAC;AACrD,OAAO,gBAAgB,MAAM,+BAA+B,CAAC;AAE7D,eAAe,IAAI,cAAc,CAAC;IAChC,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,CAAC,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,CAAC;IACjE,OAAO,EAAE;QACP,OAAO,EAAE,CAAC;KACX;IACD,QAAQ,EAAE;QACR,qBAAqB,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/C,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACtC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QACpC,iBAAiB,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QACzC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC/B,oBAAoB,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,mBAAmB,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;KACjD;IACD,QAAQ,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC;IACvE,IAAI,EAAE,IAAI,CAAA;EACV,MAAM;EACN,QAAQ;EACR,MAAM,CAAC,IAAI;;EAEX,aAAa;;EAEb,YAAY,CAAC,IAAI;EACjB,gBAAgB;;EAEhB,kBAAkB,CAAC,IAAI;EACvB,SAAS,CAAC,IAAI;EACd,MAAM,CAAC,IAAI;;EAEX,YAAY;EACZ,WAAW;EACX,mBAAmB;EACnB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkEb;CACD,CAAC,CAAC","sourcesContent":["// 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 cameraRayGlsl from './chunks/cameraRay.glsl.js';\nimport common 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 sampleShadowCatcher from './chunks/sampleShadowCatcher.glsl.js';\nimport sampleLight from './chunks/sampleLight.glsl.js';\nimport { FragmentShader, glsl, Shader } from '../shader.js';\nimport samplingGlsl from './chunks/sampling.glsl.js';\nimport uvProjectionGlsl from './chunks/uvProjection.glsl.js';\n\nexport default new FragmentShader({\n name: 'shadowCatcher',\n outputs: ['shadowCatcher', 'catcherBackground', 'catcherMoments'],\n defines: {\n BOUNCES: 2\n },\n uniforms: {\n dispersionWavelengths: Shader.uniform('int', 3),\n wavelength: Shader.uniform('float', 0),\n viewportSize: Shader.uniform('vec2'),\n viewProjectionInv: Shader.uniform('mat4'),\n viewInv: Shader.uniform('mat4'),\n cameraApertureRadius: Shader.uniform('float', 0),\n cameraFocusDistance: Shader.uniform('float', 10)\n },\n includes: [rayTraceCore, materialBufferGlsl, intersect, random, envMap],\n main: glsl`\n${common}\n${randGlsl}\n${random.main}\n\n${textureLinear}\n\n${rayTraceCore.main}\n${uvProjectionGlsl}\n\n${materialBufferGlsl.main}\n${intersect.main}\n${envMap.main}\n\n${samplingGlsl}\n${sampleLight}\n${sampleShadowCatcher}\n${cameraRayGlsl}\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(inout Ray ray, out vec4 background) {\n background = vec4(0.0);\n Path path;\n path.ray = ray;\n path.li = vec3(0);\n path.alpha = 1.0;\n path.beta = vec3(1.0);\n path.fromEye = true;\n path.scatteringPdf = 0.0;\n path.abort = false;\n\n SurfaceInteraction si;\n\n intersectScene(path.ray, si, false);\n if (!si.hit) {\n // Red stores catcher coverage. Keep it zero for pixels where the primary\n // ray did not hit the catcher so denoised shadows cannot bleed onto the\n // model silhouette during composition.\n return vec4(0.0, 0.0, 0.0, 1.0);\n }\n\n OpenPbrResolvedMaterial mat;\n getMaterial(mat, si.materialIndex, si.uv, si.uv1, si.instanceIndex);\n if (mat.materialType != SHADOW_CATCHER) {\n // The main path tracer already renders regular geometry. Only evaluate\n // shadow opacity when the primary ray actually hits the catcher.\n return vec4(0.0, 0.0, 0.0, 1.0);\n }\n\n vec4 bg = sampleBackgroundFromRay(ray.o, ray.d);\n background = vec4(bg.rgb * bg.a, bg.a);\n if (!path.abort) {\n sampleShadowCatcher(si, 0, path);\n }\n return vec4(1.0, bg.a, bg.a >= 0.01 ? 1.0 : 0.0, path.alpha);\n}\n\nvoid main() {\n initRandom();\n rng_initialize(v_Texcoord * viewportSize, int(uint(sampleCount) ^ sampleHash(uint(sequenceSeed))));\n // Match the beauty pass's random stream in hero-wavelength previews.\n if (dispersionWavelengths == 1 && wavelength > 0.0) { sample1D(CAMERA_WAVELENGTH); }\n Ray cam = primaryCameraRay();\n vec4 background;\n vec4 liAndAlpha = integrator(cam, background);\n\n if (!(liAndAlpha.x < INF && liAndAlpha.x > -EPS)) {\n liAndAlpha = vec4(0, 0, 0, 1);\n }\n\n out_shadowCatcher = liAndAlpha;\n out_catcherBackground = background;\n // The composed catcher is the background darkened by the shadow (its\n // visibility in alpha). Over a transparent background the shadow is only\n // its opacity.\n float guideValue = liAndAlpha.a * (background.a >= 0.01\n ? dot(background.rgb / background.a, vec3(0.2126, 0.7152, 0.0722))\n : 1.0);\n out_catcherMoments = liAndAlpha.r > 0.0\n ? vec4(guideValue, guideValue * guideValue, 1.0, 0.0)\n : vec4(0.0);\n\n}`\n});\n"]}
|
|
1
|
+
{"version":3,"file":"shadowCatcher.frag.js","sourceRoot":"","sources":["../../../src/renderer/glsl/shadowCatcher.frag.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,sDAAsD;AACtD,kFAAkF;AAElF,OAAO,aAAa,MAAM,4BAA4B,CAAC;AACvD,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,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,mBAAmB,MAAM,sCAAsC,CAAC;AACvE,OAAO,WAAW,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,YAAY,MAAM,2BAA2B,CAAC;AACrD,OAAO,gBAAgB,MAAM,+BAA+B,CAAC;AAE7D,eAAe,IAAI,cAAc,CAAC;IAChC,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,CAAC,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,CAAC;IACjE,OAAO,EAAE;QACP,OAAO,EAAE,CAAC;KACX;IACD,QAAQ,EAAE;QACR,qBAAqB,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/C,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACtC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QACpC,iBAAiB,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QACzC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC/B,oBAAoB,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,mBAAmB,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;KACjD;IACD,QAAQ,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC;IACvE,IAAI,EAAE,IAAI,CAAA;EACV,MAAM;EACN,QAAQ;EACR,MAAM,CAAC,IAAI;;EAEX,aAAa;;EAEb,YAAY,CAAC,IAAI;EACjB,gBAAgB;;EAEhB,kBAAkB,CAAC,IAAI;EACvB,SAAS,CAAC,IAAI;EACd,MAAM,CAAC,IAAI;;EAEX,YAAY;EACZ,WAAW;EACX,mBAAmB;EACnB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkEb;CACD,CAAC,CAAC","sourcesContent":["// 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 cameraRayGlsl from './chunks/cameraRay.glsl.js';\nimport common 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 sampleShadowCatcher from './chunks/sampleShadowCatcher.glsl.js';\nimport sampleLight from './chunks/sampleLight.glsl.js';\nimport { FragmentShader, glsl, Shader } from '../shader.js';\nimport samplingGlsl from './chunks/sampling.glsl.js';\nimport uvProjectionGlsl from './chunks/uvProjection.glsl.js';\n\nexport default new FragmentShader({\n name: 'shadowCatcher',\n outputs: ['shadowCatcher', 'catcherBackground', 'catcherMoments'],\n defines: {\n BOUNCES: 2\n },\n uniforms: {\n dispersionWavelengths: Shader.uniform('int', 3),\n wavelength: Shader.uniform('float', 0),\n viewportSize: Shader.uniform('vec2'),\n viewProjectionInv: Shader.uniform('mat4'),\n viewInv: Shader.uniform('mat4'),\n cameraApertureRadius: Shader.uniform('float', 0),\n cameraFocusDistance: Shader.uniform('float', 10)\n },\n includes: [rayTraceCore, materialBufferGlsl, intersect, random, envMap],\n main: glsl`\n${common}\n${randGlsl}\n${random.main}\n\n${textureLinear}\n\n${rayTraceCore.main}\n${uvProjectionGlsl}\n\n${materialBufferGlsl.main}\n${intersect.main}\n${envMap.main}\n\n${samplingGlsl}\n${sampleLight}\n${sampleShadowCatcher}\n${cameraRayGlsl}\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(inout Ray ray, out vec4 background) {\n background = vec4(0.0);\n Path path;\n path.ray = ray;\n path.li = vec3(0);\n path.alpha = 1.0;\n path.beta = vec3(1.0);\n path.fromEye = true;\n path.scatteringPdf = 0.0;\n path.abort = false;\n\n SurfaceInteraction si;\n\n intersectScene(path.ray, si, false);\n if (!si.hit) {\n // Red stores catcher coverage. Keep it zero for pixels where the primary\n // ray did not hit the catcher so denoised shadows cannot bleed onto the\n // model silhouette during composition.\n return vec4(0.0, 0.0, 0.0, 1.0);\n }\n\n OpenPbrResolvedMaterial mat;\n getMaterial(mat, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n if (mat.materialType != SHADOW_CATCHER) {\n // The main path tracer already renders regular geometry. Only evaluate\n // shadow opacity when the primary ray actually hits the catcher.\n return vec4(0.0, 0.0, 0.0, 1.0);\n }\n\n vec4 bg = sampleBackgroundFromRay(ray.o, ray.d);\n background = vec4(bg.rgb * bg.a, bg.a);\n if (!path.abort) {\n sampleShadowCatcher(si, 0, path);\n }\n return vec4(1.0, bg.a, bg.a >= 0.01 ? 1.0 : 0.0, path.alpha);\n}\n\nvoid main() {\n initRandom();\n rng_initialize(v_Texcoord * viewportSize, int(uint(sampleCount) ^ sampleHash(uint(sequenceSeed))));\n // Match the beauty pass's random stream in hero-wavelength previews.\n if (dispersionWavelengths == 1 && wavelength > 0.0) { sample1D(CAMERA_WAVELENGTH); }\n Ray cam = primaryCameraRay();\n vec4 background;\n vec4 liAndAlpha = integrator(cam, background);\n\n if (!(liAndAlpha.x < INF && liAndAlpha.x > -EPS)) {\n liAndAlpha = vec4(0, 0, 0, 1);\n }\n\n out_shadowCatcher = liAndAlpha;\n out_catcherBackground = background;\n // The composed catcher is the background darkened by the shadow (its\n // visibility in alpha). Over a transparent background the shadow is only\n // its opacity.\n float guideValue = liAndAlpha.a * (background.a >= 0.01\n ? dot(background.rgb / background.a, vec3(0.2126, 0.7152, 0.0722))\n : 1.0);\n out_catcherMoments = liAndAlpha.r > 0.0\n ? vec4(guideValue, guideValue * guideValue, 1.0, 0.0)\n : vec4(0.0);\n\n}`\n});\n"]}
|
|
@@ -7,22 +7,22 @@ export const brightFragment = new FragmentShader({
|
|
|
7
7
|
scale: Shader.uniform('float', 1)
|
|
8
8
|
},
|
|
9
9
|
main: glsl `
|
|
10
|
-
const vec3
|
|
11
|
-
vec4
|
|
10
|
+
const vec3 uQ = vec3(0.2125, 0.7154, 0.0721);
|
|
11
|
+
vec4 sD(vec4 a, vec4 b, vec4 c) {
|
|
12
12
|
return a + b + c - min(min(a, b), c) - max(max(a, b), c);
|
|
13
13
|
}
|
|
14
14
|
void main() {
|
|
15
15
|
vec2 d = 1.0 / vec2(colorTexRegion());
|
|
16
|
-
vec4
|
|
16
|
+
vec4 eA = sample_colorTex(v_Texcoord);
|
|
17
17
|
vec4 s1 = sample_colorTex(v_Texcoord - vec2(d.x, 0.0));
|
|
18
18
|
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
|
-
|
|
22
|
-
float lum = dot(
|
|
23
|
-
out_color = lum > threshold &&
|
|
24
|
-
? vec4(
|
|
25
|
-
: vec4(0.0, 0.0, 0.0,
|
|
21
|
+
eA = sD(sD(eA, s1, s2), s3, s4);
|
|
22
|
+
float lum = dot(eA.rgb, uQ);
|
|
23
|
+
out_color = lum > threshold && eA.a > 0.0
|
|
24
|
+
? vec4(eA.rgb * scale, eA.a)
|
|
25
|
+
: vec4(0.0, 0.0, 0.0, eA.a);
|
|
26
26
|
}`
|
|
27
27
|
});
|
|
28
28
|
export const composeFragment = new FragmentShader({
|
|
@@ -48,35 +48,35 @@ export const composeFragment = new FragmentShader({
|
|
|
48
48
|
sRGBOutput: Shader.uniform('bool', 1)
|
|
49
49
|
},
|
|
50
50
|
main: glsl `
|
|
51
|
-
vec3
|
|
51
|
+
vec3 jU(vec3 color) {
|
|
52
52
|
return clamp(color, 0.0, 1.0);
|
|
53
53
|
}
|
|
54
|
-
vec3
|
|
54
|
+
vec3 iP(vec3 color) {
|
|
55
55
|
return clamp(color / (vec3(1.0) + color), 0.0, 1.0);
|
|
56
56
|
}
|
|
57
|
-
vec3
|
|
57
|
+
vec3 jJ(vec3 color) {
|
|
58
58
|
color = max(vec3(0.0), color - 0.004);
|
|
59
59
|
return pow((color * (6.2 * color + 0.5)) /
|
|
60
60
|
(color * (6.2 * color + 1.7) + 0.06), vec3(2.2));
|
|
61
61
|
}
|
|
62
|
-
vec3
|
|
63
|
-
vec3
|
|
62
|
+
vec3 oJ(vec3 value) {
|
|
63
|
+
vec3 kO = value * (value + 0.0245786) - 0.000090537;
|
|
64
64
|
vec3 ap = value * (0.983729 * value + 0.4329510) + 0.238081;
|
|
65
|
-
return
|
|
65
|
+
return kO / ap;
|
|
66
66
|
}
|
|
67
|
-
vec3
|
|
68
|
-
const mat3
|
|
67
|
+
vec3 lv(vec3 color) {
|
|
68
|
+
const mat3 pw = mat3(
|
|
69
69
|
vec3(0.59719, 0.07600, 0.02840),
|
|
70
70
|
vec3(0.35458, 0.90834, 0.13383),
|
|
71
71
|
vec3(0.04823, 0.01566, 0.83777)
|
|
72
72
|
);
|
|
73
|
-
const mat3
|
|
73
|
+
const mat3 oE = mat3(
|
|
74
74
|
vec3(1.60475, -0.10208, -0.00327),
|
|
75
75
|
vec3(-0.53108, 1.10813, -0.07276),
|
|
76
76
|
vec3(-0.07367, -0.00605, 1.07602)
|
|
77
77
|
);
|
|
78
|
-
color =
|
|
79
|
-
return clamp(
|
|
78
|
+
color = pw * (color / 0.6);
|
|
79
|
+
return clamp(oE * oJ(color), 0.0, 1.0);
|
|
80
80
|
}
|
|
81
81
|
const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3(
|
|
82
82
|
vec3(1.6605, -0.1246, -0.0182),
|
|
@@ -88,44 +88,44 @@ vec3(0.6274, 0.0691, 0.0164),
|
|
|
88
88
|
vec3(0.3293, 0.9195, 0.0880),
|
|
89
89
|
vec3(0.0433, 0.0113, 0.8956)
|
|
90
90
|
);
|
|
91
|
-
vec3
|
|
92
|
-
vec3
|
|
93
|
-
vec3
|
|
94
|
-
return 15.5 *
|
|
95
|
-
+ 31.96 *
|
|
96
|
-
+ 0.4298 *
|
|
91
|
+
vec3 fT(vec3 value) {
|
|
92
|
+
vec3 mO = value * value;
|
|
93
|
+
vec3 qK = mO * mO;
|
|
94
|
+
return 15.5 * qK * mO - 40.14 * qK * value
|
|
95
|
+
+ 31.96 * qK - 6.868 * mO * value
|
|
96
|
+
+ 0.4298 * mO + 0.1191 * value - 0.00232;
|
|
97
97
|
}
|
|
98
|
-
vec3
|
|
99
|
-
const mat3
|
|
98
|
+
vec3 me(vec3 color) {
|
|
99
|
+
const mat3 pH = mat3(
|
|
100
100
|
vec3(0.856627153315983, 0.137318972929847, 0.11189821299995),
|
|
101
101
|
vec3(0.0951212405381588, 0.761241990602591, 0.0767994186031903),
|
|
102
102
|
vec3(0.0482516061458583, 0.101439036467562, 0.811302368396859)
|
|
103
103
|
);
|
|
104
|
-
const mat3
|
|
104
|
+
const mat3 oF = mat3(
|
|
105
105
|
vec3(1.1271005818144368, -0.1413297634984383, -0.14132976349843826),
|
|
106
106
|
vec3(-0.11060664309660323, 1.157823702216272, -0.11060664309660294),
|
|
107
107
|
vec3(-0.016493938717834573, -0.016493938717834257, 1.2519364065950405)
|
|
108
108
|
);
|
|
109
|
-
color =
|
|
109
|
+
color = pH * (LINEAR_SRGB_TO_LINEAR_REC2020 * color);
|
|
110
110
|
color = clamp((log2(max(color, 1e-10)) + 12.47393) / 16.5, 0.0, 1.0);
|
|
111
|
-
color =
|
|
111
|
+
color = oF * fT(color);
|
|
112
112
|
color = pow(max(color, 0.0), vec3(2.2));
|
|
113
113
|
return clamp(LINEAR_REC2020_TO_LINEAR_SRGB * color, 0.0, 1.0);
|
|
114
114
|
}
|
|
115
|
-
vec3
|
|
116
|
-
if (toneMappingMode == 1) return
|
|
117
|
-
if (toneMappingMode == 2) return
|
|
118
|
-
if (toneMappingMode == 3) return
|
|
119
|
-
if (toneMappingMode == 4) return
|
|
120
|
-
return
|
|
115
|
+
vec3 cl(vec3 color) {
|
|
116
|
+
if (toneMappingMode == 1) return lv(color);
|
|
117
|
+
if (toneMappingMode == 2) return jJ(color);
|
|
118
|
+
if (toneMappingMode == 3) return iP(color);
|
|
119
|
+
if (toneMappingMode == 4) return jU(color);
|
|
120
|
+
return me(color);
|
|
121
121
|
}
|
|
122
|
-
vec3
|
|
123
|
-
vec3
|
|
122
|
+
vec3 gb(vec3 color) {
|
|
123
|
+
vec3 tj = vec3(
|
|
124
124
|
1.0 + temperature * 0.16 + tint * 0.04,
|
|
125
125
|
1.0 - tint * 0.12,
|
|
126
126
|
1.0 - temperature * 0.16 + tint * 0.04
|
|
127
127
|
);
|
|
128
|
-
color = max(color *
|
|
128
|
+
color = max(color * tj, 0.0);
|
|
129
129
|
color = max((color - 0.18) * contrast + 0.18, 0.0);
|
|
130
130
|
float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
|
|
131
131
|
return max(mix(vec3(luminance), color, saturation), 0.0);
|
|
@@ -141,11 +141,11 @@ return;
|
|
|
141
141
|
vec4 color = sample_colorTex(uv);
|
|
142
142
|
vec4 bloom = sample_bloomTex(uv) * bloomIntensity;
|
|
143
143
|
color.rgb += bloom.rgb;
|
|
144
|
-
color.rgb =
|
|
144
|
+
color.rgb = gb(color.rgb * exposure);
|
|
145
145
|
if (sRGBOutput) {
|
|
146
146
|
float edge = smoothstep(0.2, 0.72, distance(uv, vec2(0.5)));
|
|
147
147
|
color.rgb *= 1.0 - vignette * edge;
|
|
148
|
-
color.rgb =
|
|
148
|
+
color.rgb = cl(color.rgb);
|
|
149
149
|
color.rgb = pow(max(color.rgb, 0.0), vec3(1.0 / 2.2));
|
|
150
150
|
}
|
|
151
151
|
color.a = max(color.a, min(dot(bloom.rgb, vec3(0.2125, 0.7154, 0.0721)), 1.0));
|
|
@@ -154,32 +154,32 @@ if (shadowCatcherEnabled > 0.5) {
|
|
|
154
154
|
float coverage = clamp(shadow.r, 0.0, 1.0);
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
float
|
|
157
|
+
float fR = clamp(
|
|
158
158
|
(1.0 - shadow.a) / max(coverage, 0.000001), 0.0, 1.0
|
|
159
159
|
);
|
|
160
|
-
|
|
160
|
+
fR = max(fR - (1.0 - shadowCatcherAlpha), 0.0);
|
|
161
161
|
if (coverage > 0.0) {
|
|
162
162
|
vec4 foreground = sample_foregroundTex(uv);
|
|
163
163
|
vec4 background = sample_catcherBackgroundTex(uv);
|
|
164
|
-
vec3 bK =
|
|
165
|
-
vec3 backgroundColor =
|
|
164
|
+
vec3 bK = gb((foreground.rgb + bloom.rgb) * exposure);
|
|
165
|
+
vec3 backgroundColor = gb((background.rgb / max(background.a, 0.000001) + bloom.rgb) * exposure);
|
|
166
166
|
if (sRGBOutput) {
|
|
167
167
|
float edge = smoothstep(0.2, 0.72, distance(uv, vec2(0.5)));
|
|
168
168
|
bK *= 1.0 - vignette * edge;
|
|
169
169
|
backgroundColor *= 1.0 - vignette * edge;
|
|
170
|
-
bK = pow(max(
|
|
171
|
-
backgroundColor = pow(max(
|
|
170
|
+
bK = pow(max(cl(bK), 0.0), vec3(1.0 / 2.2));
|
|
171
|
+
backgroundColor = pow(max(cl(backgroundColor), 0.0), vec3(1.0 / 2.2));
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
float opacity =
|
|
175
|
-
|
|
176
|
-
float
|
|
177
|
-
float
|
|
178
|
-
float
|
|
179
|
-
float alpha =
|
|
180
|
-
vec3 premultiplied = bK *
|
|
181
|
-
+ backgroundColor *
|
|
182
|
-
+ shadowCatcherColor *
|
|
174
|
+
float opacity = fR;
|
|
175
|
+
fR *= coverage;
|
|
176
|
+
float mg = clamp(dot(bloom.rgb, vec3(0.2125, 0.7154, 0.0721)), 0.0, 1.0);
|
|
177
|
+
float hv = max(foreground.a, mg * (1.0 - coverage));
|
|
178
|
+
float jd = max(background.a, mg * coverage);
|
|
179
|
+
float alpha = hv + jd * (1.0 - opacity) + fR;
|
|
180
|
+
vec3 premultiplied = bK * hv
|
|
181
|
+
+ backgroundColor * jd * (1.0 - opacity)
|
|
182
|
+
+ shadowCatcherColor * fR;
|
|
183
183
|
color = vec4(premultiplied / max(alpha, 0.000001), alpha);
|
|
184
184
|
}
|
|
185
185
|
}
|
|
@@ -31,7 +31,7 @@ export function packLightsToBuffer(lights, texturesIndices, selectionPmfs = [],
|
|
|
31
31
|
packVec3(off + 2, getRectLightUOrV(light, 0));
|
|
32
32
|
data[(off + 2) * 4 + 3] = light.spread;
|
|
33
33
|
packVec3(off + 3, getRectLightUOrV(light, 1));
|
|
34
|
-
data[(off + 3) * 4 + 3] = texturesIndices[i];
|
|
34
|
+
data[(off + 3) * 4 + 3] = texturesIndices[i] ?? -1;
|
|
35
35
|
packVec3(off + 4, [
|
|
36
36
|
light.spread,
|
|
37
37
|
getRectLightArea(light),
|
|
@@ -49,7 +49,7 @@ export function packLightsToBuffer(lights, texturesIndices, selectionPmfs = [],
|
|
|
49
49
|
packVec3(off + 2, getLightDirection(light));
|
|
50
50
|
packVec3(off + 3, [
|
|
51
51
|
Math.cos((light.outerAngle * Math.PI) / 180),
|
|
52
|
-
Math.
|
|
52
|
+
Math.cos((Math.min(light.innerAngle, light.outerAngle) * Math.PI) / 180),
|
|
53
53
|
0
|
|
54
54
|
]);
|
|
55
55
|
data[(off + 3) * 4 + 3] = iesProfileTexelOffsets[i] ?? -1;
|
|
@@ -106,9 +106,11 @@ export function getSpotLightRadiance(light) {
|
|
|
106
106
|
return light.intensity / (Math.PI * light.radius ** 2);
|
|
107
107
|
}
|
|
108
108
|
export function getSpotLightShapingFluxFactor(light) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
// Fraction of the full sphere covered by the cone. The smoothstep falloff
|
|
110
|
+
// between the inner and outer cosines integrates to half its width.
|
|
111
|
+
const cosOuter = Math.cos((light.outerAngle * Math.PI) / 180);
|
|
112
|
+
const cosInner = Math.cos((Math.min(light.innerAngle, light.outerAngle) * Math.PI) / 180);
|
|
113
|
+
return 0.5 * (1 - 0.5 * (cosInner + cosOuter));
|
|
112
114
|
}
|
|
113
115
|
const warnedLights = new WeakSet();
|
|
114
116
|
/** Sanitizes invalid mutable light values at the packing boundary; notifications are nonfatal. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lightsUtil.js","sourceRoot":"","sources":["../../src/renderer/lightsUtil.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SceneLight, AnalyticLight } from '../scene/Scene.js';\nimport type { Vec3 } from '../types.js';\nimport DirectionalLight from '../scene/DirectionalLight.js';\nimport SphereLight from '../scene/SphereLight.js';\nimport RectLight from '../scene/RectLight.js';\nimport SpotLight from '../scene/SpotLight.js';\n\nexport const CONSTANT_DIRECTIONAL_LIGHT = 1;\nexport const CONSTANT_SPHERE_LIGHT = 2;\nexport const CONSTANT_RECT_LIGHT = 3;\nexport const CONSTANT_SPOT_LIGHT = 4;\n\nexport function packLightsToBuffer(\n lights: (DirectionalLight | RectLight | SphereLight | SpotLight)[],\n texturesIndices: number[],\n selectionPmfs: ArrayLike<number> = [],\n selectionCdfs: ArrayLike<number> = [],\n iesProfileTexelOffsets: ArrayLike<number> = []\n) {\n validateSceneLights(lights);\n const numLights = lights.length;\n const PIXELS_PER_LIGHT = 6;\n const data = new Float32Array(numLights * PIXELS_PER_LIGHT * 4);\n\n function packVec3(idx: number, v: number[] | Float32Array) {\n for (let i = 0; i < 3; i++) {\n data[i + idx * 4] = v[i];\n }\n }\n\n for (let i = 0; i < numLights; i++) {\n const light = lights[i];\n const color = light.color;\n const intensity = light.intensity;\n const off = i * PIXELS_PER_LIGHT;\n packVec3(\n off,\n light.position\n );\n data[off * 4 + 3] = selectionPmfs[i] ?? 1 / Math.max(numLights, 1);\n // emission\n const emissionScale =\n light.type === 'SpotLight' ? getSpotLightRadiance(light) : intensity;\n packVec3(off + 1, [\n color[0] * emissionScale,\n color[1] * emissionScale,\n color[2] * emissionScale\n ]);\n data[(off + 1) * 4 + 3] = selectionCdfs[i] ?? (i + 1) / numLights;\n if (light.type === 'RectLight') {\n packVec3(off + 2, getRectLightUOrV(light, 0));\n data[(off + 2) * 4 + 3] = light.spread;\n packVec3(off + 3, getRectLightUOrV(light, 1));\n data[(off + 3) * 4 + 3] = texturesIndices[i];\n packVec3(off + 4, [\n light.spread,\n getRectLightArea(light),\n CONSTANT_RECT_LIGHT\n ]);\n } else if (light.type === 'SphereLight') {\n packVec3(off + 4, [\n light.radius,\n getSphereLightArea(light),\n CONSTANT_SPHERE_LIGHT\n ]);\n } else if (light.type === 'SpotLight') {\n packVec3(off + 2, getLightDirection(light));\n packVec3(off + 3, [\n Math.cos((light.outerAngle * Math.PI) / 180),\n Math.min(1, Math.max(0, 1 - light.innerAngle / light.outerAngle)),\n 0\n ]);\n data[(off + 3) * 4 + 3] = iesProfileTexelOffsets[i] ?? -1;\n packVec3(off + 4, [\n light.radius,\n getSpotLightArea(light),\n CONSTANT_SPOT_LIGHT\n ]);\n } else if (light.type === 'DirectionalLight') {\n packVec3(off + 2, getLightDirection(light));\n packVec3(off + 4, [0, 0, CONSTANT_DIRECTIONAL_LIGHT]);\n }\n packVec3(off + 5, getRigidLightFrame(light).x);\n }\n\n return data;\n}\n\nexport function getRigidLightFrame(light: AnalyticLight) {\n const m = light.transform;\n const normalize = (v: Vec3, fallback: Vec3): Vec3 => {\n const length = Math.hypot(v[0], v[1], v[2]);\n return length > 1e-12 ? [v[0]/length, v[1]/length, v[2]/length] : fallback;\n };\n const z = normalize([m[8],m[9],m[10]], [0,0,1]);\n const projection = m[0]*z[0] + m[1]*z[1] + m[2]*z[2];\n let x: Vec3 = [m[0]-projection*z[0],m[1]-projection*z[1],m[2]-projection*z[2]];\n if (Math.hypot(...x) <= 1e-12) x = Math.abs(z[1]) < .99 ? [z[2],0,-z[0]] : [1,0,0];\n x = normalize(x, [1,0,0]);\n const y: Vec3 = [z[1]*x[2]-z[2]*x[1], z[2]*x[0]-z[0]*x[2], z[0]*x[1]-z[1]*x[0]];\n return { x, y, direction: [-z[0] || 0,-z[1] || 0,-z[2] || 0] as Vec3 };\n}\nexport function getLightDirection(light: AnalyticLight) { return getRigidLightFrame(light).direction; }\nexport function getRectLightUOrV(light: RectLight, uOrV: 0 | 1) {\n const frame = getRigidLightFrame(light), axis = uOrV ? frame.y : frame.x;\n const size = light.size[uOrV] * (uOrV ? -1 : 1);\n return [axis[0]*size,axis[1]*size,axis[2]*size];\n}\nexport function getRectLightPosition(light: RectLight) { return light.position; }\n\nexport function getRectLightArea(light: RectLight) {\n return light.size[0] * light.size[1];\n}\n\nexport function getSphereLightArea(light: SphereLight) {\n return 4 * Math.PI * light.radius ** 2;\n}\n\nexport function getSpotLightArea(light: SpotLight) {\n return 4 * Math.PI * light.radius ** 2;\n}\n\nexport function getSpotLightRadiance(light: SpotLight) {\n // A zero-radius spot is a point emitter. Its stored emission is luminous\n // intensity (cd), and the shader applies inverse-square attenuation.\n if (light.radius === 0) return light.intensity;\n return light.intensity / (Math.PI * light.radius ** 2);\n}\n\nexport function getSpotLightShapingFluxFactor(light: SpotLight) {\n const cosConeAngle = Math.cos((light.outerAngle * Math.PI) / 180);\n const softness = Math.min(\n 1,\n Math.max(0, 1 - light.innerAngle / light.outerAngle)\n );\n return (1 - cosConeAngle) * (1 - 0.5 * softness) * 0.5;\n}\n\nconst warnedLights = new WeakSet<SceneLight>();\n/** Sanitizes invalid mutable light values at the packing boundary; notifications are nonfatal. */\nexport function validateSceneLights(lights: readonly SceneLight[], onError?: (error: Error) => void) {\n for (const light of lights) {\n if (light.type === 'EnvironmentLight') continue;\n let invalid = false;\n const clamp = (value: number, min: number, max: number, fallback: number) => {\n const normalized = Math.min(max,Math.max(min,Number.isFinite(value) ? value : fallback));\n invalid ||= !Object.is(normalized,value);\n return normalized;\n };\n light.intensity = clamp(light.intensity,0,Number.MAX_VALUE,1);\n for(let i=0;i<3;i++)light.color[i] = clamp(light.color[i],0,Number.MAX_VALUE,1);\n if (light.type !== 'AmbientLight') {\n for(let i=0;i<16;i++) if (!Number.isFinite(light.transform[i])) {\n light.transform[i] = i % 5 === 0 ? 1 : 0; invalid = true;\n }\n }\n if (light.type === 'RectLight') {\n for(let i=0;i<2;i++)light.size[i] = clamp(light.size[i],0,Number.MAX_VALUE,1);\n light.spread = clamp(light.spread,0,1,1);\n }\n if (light.type === 'SphereLight' || light.type === 'SpotLight') light.radius = clamp(light.radius,0,Number.MAX_VALUE,light.type === 'SpotLight' ? .01 : 1);\n if (light.type === 'SpotLight') {\n light.outerAngle = clamp(light.outerAngle,Number.EPSILON,180,45);\n light.innerAngle = clamp(light.innerAngle,0,light.outerAngle,30);\n }\n if (invalid && !warnedLights.has(light)) {\n warnedLights.add(light);\n const error = new Error(`Invalid ${light.type} values were clamped.`);\n console.warn(error.message);\n onError?.(error);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lightsUtil.js","sourceRoot":"","sources":["../../src/renderer/lightsUtil.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SceneLight, AnalyticLight } from '../scene/Scene.js';\nimport type { Vec3 } from '../types.js';\nimport DirectionalLight from '../scene/DirectionalLight.js';\nimport SphereLight from '../scene/SphereLight.js';\nimport RectLight from '../scene/RectLight.js';\nimport SpotLight from '../scene/SpotLight.js';\n\nexport const CONSTANT_DIRECTIONAL_LIGHT = 1;\nexport const CONSTANT_SPHERE_LIGHT = 2;\nexport const CONSTANT_RECT_LIGHT = 3;\nexport const CONSTANT_SPOT_LIGHT = 4;\n\nexport function packLightsToBuffer(\n lights: (DirectionalLight | RectLight | SphereLight | SpotLight)[],\n texturesIndices: number[],\n selectionPmfs: ArrayLike<number> = [],\n selectionCdfs: ArrayLike<number> = [],\n iesProfileTexelOffsets: ArrayLike<number> = []\n) {\n validateSceneLights(lights);\n const numLights = lights.length;\n const PIXELS_PER_LIGHT = 6;\n const data = new Float32Array(numLights * PIXELS_PER_LIGHT * 4);\n\n function packVec3(idx: number, v: number[] | Float32Array) {\n for (let i = 0; i < 3; i++) {\n data[i + idx * 4] = v[i];\n }\n }\n\n for (let i = 0; i < numLights; i++) {\n const light = lights[i];\n const color = light.color;\n const intensity = light.intensity;\n const off = i * PIXELS_PER_LIGHT;\n packVec3(\n off,\n light.position\n );\n data[off * 4 + 3] = selectionPmfs[i] ?? 1 / Math.max(numLights, 1);\n // emission\n const emissionScale =\n light.type === 'SpotLight' ? getSpotLightRadiance(light) : intensity;\n packVec3(off + 1, [\n color[0] * emissionScale,\n color[1] * emissionScale,\n color[2] * emissionScale\n ]);\n data[(off + 1) * 4 + 3] = selectionCdfs[i] ?? (i + 1) / numLights;\n if (light.type === 'RectLight') {\n packVec3(off + 2, getRectLightUOrV(light, 0));\n data[(off + 2) * 4 + 3] = light.spread;\n packVec3(off + 3, getRectLightUOrV(light, 1));\n data[(off + 3) * 4 + 3] = texturesIndices[i] ?? -1;\n packVec3(off + 4, [\n light.spread,\n getRectLightArea(light),\n CONSTANT_RECT_LIGHT\n ]);\n } else if (light.type === 'SphereLight') {\n packVec3(off + 4, [\n light.radius,\n getSphereLightArea(light),\n CONSTANT_SPHERE_LIGHT\n ]);\n } else if (light.type === 'SpotLight') {\n packVec3(off + 2, getLightDirection(light));\n packVec3(off + 3, [\n Math.cos((light.outerAngle * Math.PI) / 180),\n Math.cos((Math.min(light.innerAngle, light.outerAngle) * Math.PI) / 180),\n 0\n ]);\n data[(off + 3) * 4 + 3] = iesProfileTexelOffsets[i] ?? -1;\n packVec3(off + 4, [\n light.radius,\n getSpotLightArea(light),\n CONSTANT_SPOT_LIGHT\n ]);\n } else if (light.type === 'DirectionalLight') {\n packVec3(off + 2, getLightDirection(light));\n packVec3(off + 4, [0, 0, CONSTANT_DIRECTIONAL_LIGHT]);\n }\n packVec3(off + 5, getRigidLightFrame(light).x);\n }\n\n return data;\n}\n\nexport function getRigidLightFrame(light: AnalyticLight) {\n const m = light.transform;\n const normalize = (v: Vec3, fallback: Vec3): Vec3 => {\n const length = Math.hypot(v[0], v[1], v[2]);\n return length > 1e-12 ? [v[0]/length, v[1]/length, v[2]/length] : fallback;\n };\n const z = normalize([m[8],m[9],m[10]], [0,0,1]);\n const projection = m[0]*z[0] + m[1]*z[1] + m[2]*z[2];\n let x: Vec3 = [m[0]-projection*z[0],m[1]-projection*z[1],m[2]-projection*z[2]];\n if (Math.hypot(...x) <= 1e-12) x = Math.abs(z[1]) < .99 ? [z[2],0,-z[0]] : [1,0,0];\n x = normalize(x, [1,0,0]);\n const y: Vec3 = [z[1]*x[2]-z[2]*x[1], z[2]*x[0]-z[0]*x[2], z[0]*x[1]-z[1]*x[0]];\n return { x, y, direction: [-z[0] || 0,-z[1] || 0,-z[2] || 0] as Vec3 };\n}\nexport function getLightDirection(light: AnalyticLight) { return getRigidLightFrame(light).direction; }\nexport function getRectLightUOrV(light: RectLight, uOrV: 0 | 1) {\n const frame = getRigidLightFrame(light), axis = uOrV ? frame.y : frame.x;\n const size = light.size[uOrV] * (uOrV ? -1 : 1);\n return [axis[0]*size,axis[1]*size,axis[2]*size];\n}\nexport function getRectLightPosition(light: RectLight) { return light.position; }\n\nexport function getRectLightArea(light: RectLight) {\n return light.size[0] * light.size[1];\n}\n\nexport function getSphereLightArea(light: SphereLight) {\n return 4 * Math.PI * light.radius ** 2;\n}\n\nexport function getSpotLightArea(light: SpotLight) {\n return 4 * Math.PI * light.radius ** 2;\n}\n\nexport function getSpotLightRadiance(light: SpotLight) {\n // A zero-radius spot is a point emitter. Its stored emission is luminous\n // intensity (cd), and the shader applies inverse-square attenuation.\n if (light.radius === 0) return light.intensity;\n return light.intensity / (Math.PI * light.radius ** 2);\n}\n\nexport function getSpotLightShapingFluxFactor(light: SpotLight) {\n // Fraction of the full sphere covered by the cone. The smoothstep falloff\n // between the inner and outer cosines integrates to half its width.\n const cosOuter = Math.cos((light.outerAngle * Math.PI) / 180);\n const cosInner = Math.cos((Math.min(light.innerAngle, light.outerAngle) * Math.PI) / 180);\n return 0.5 * (1 - 0.5 * (cosInner + cosOuter));\n}\n\nconst warnedLights = new WeakSet<SceneLight>();\n/** Sanitizes invalid mutable light values at the packing boundary; notifications are nonfatal. */\nexport function validateSceneLights(lights: readonly SceneLight[], onError?: (error: Error) => void) {\n for (const light of lights) {\n if (light.type === 'EnvironmentLight') continue;\n let invalid = false;\n const clamp = (value: number, min: number, max: number, fallback: number) => {\n const normalized = Math.min(max,Math.max(min,Number.isFinite(value) ? value : fallback));\n invalid ||= !Object.is(normalized,value);\n return normalized;\n };\n light.intensity = clamp(light.intensity,0,Number.MAX_VALUE,1);\n for(let i=0;i<3;i++)light.color[i] = clamp(light.color[i],0,Number.MAX_VALUE,1);\n if (light.type !== 'AmbientLight') {\n for(let i=0;i<16;i++) if (!Number.isFinite(light.transform[i])) {\n light.transform[i] = i % 5 === 0 ? 1 : 0; invalid = true;\n }\n }\n if (light.type === 'RectLight') {\n for(let i=0;i<2;i++)light.size[i] = clamp(light.size[i],0,Number.MAX_VALUE,1);\n light.spread = clamp(light.spread,0,1,1);\n }\n if (light.type === 'SphereLight' || light.type === 'SpotLight') light.radius = clamp(light.radius,0,Number.MAX_VALUE,light.type === 'SpotLight' ? .01 : 1);\n if (light.type === 'SpotLight') {\n light.outerAngle = clamp(light.outerAngle,Number.EPSILON,180,45);\n light.innerAngle = clamp(light.innerAngle,0,light.outerAngle,30);\n }\n if (invalid && !warnedLights.has(light)) {\n warnedLights.add(light);\n const error = new Error(`Invalid ${light.type} values were clamped.`);\n console.warn(error.message);\n onError?.(error);\n }\n }\n}\n"]}
|
|
@@ -11,7 +11,7 @@ function textureKey(texture) {
|
|
|
11
11
|
if (!textureIds.has(texture.image))
|
|
12
12
|
textureIds.set(texture.image, nextTextureId++);
|
|
13
13
|
return [textureIds.get(texture), textureIds.get(texture.image), texture.version, texture.uvScale.join(','),
|
|
14
|
-
texture.uvOffset.join(','), texture.uvChannel, texture.flipY].join(':');
|
|
14
|
+
texture.uvOffset.join(','), texture.uvChannel, texture.flipY, texture.wrapS, texture.wrapT, texture.colorSpace].join(':');
|
|
15
15
|
}
|
|
16
16
|
function key(material, keys) {
|
|
17
17
|
return JSON.stringify(keys.map(k => material[k]));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materialChanges.js","sourceRoot":"","sources":["../../src/renderer/materialChanges.ts"],"names":[],"mappings":"AAAA,OAAwB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAE5H,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAUzD,MAAM,UAAU,GAAG,IAAI,OAAO,EAAkB,CAAC;AACjD,IAAI,aAAa,GAAG,CAAC,CAAC;AACtB,SAAS,UAAU,CAAC,OAA4B;IAC9C,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC;IACzB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IACvE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;IACnF,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QACxG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"materialChanges.js","sourceRoot":"","sources":["../../src/renderer/materialChanges.ts"],"names":[],"mappings":"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","sourcesContent":["import OpenPBRMaterial, { materialGeometryKeys, materialParamKeys, materialTextureKeys } from '../scene/OpenPBRMaterial.js';\nimport Texture from '../scene/Texture.js';\nimport { colorLuminance } from './lightSampling.js';\nimport { materialEmission } from './materialEmission.js';\n\nexport type MaterialUpdateLevel = 'params' | 'emission' | 'textures' | 'geometry';\ninterface MaterialSnapshot {\n paramKey: string;\n textureKey: string;\n geometryKey: string;\n emissive: string;\n emissiveMap: string;\n}\nconst textureIds = new WeakMap<object, number>();\nlet nextTextureId = 1;\nfunction textureKey(texture: Texture | undefined) {\n if (!texture) return '-';\n if (!textureIds.has(texture)) textureIds.set(texture, nextTextureId++);\n if (!textureIds.has(texture.image)) textureIds.set(texture.image, nextTextureId++);\n return [textureIds.get(texture), textureIds.get(texture.image), texture.version, texture.uvScale.join(','),\n texture.uvOffset.join(','), texture.uvChannel, texture.flipY, texture.wrapS, texture.wrapT, texture.colorSpace].join(':');\n}\nfunction key(material: OpenPBRMaterial, keys: readonly (keyof OpenPBRMaterial)[]) {\n return JSON.stringify(keys.map(k => material[k]));\n}\nfunction snapshot(material: OpenPBRMaterial): MaterialSnapshot {\n return {\n paramKey: key(material, materialParamKeys),\n textureKey: materialTextureKeys.map(k => { const value = material[k]; return typeof value === 'number' ? String(value) : textureKey(value); }).join('|'),\n // Height maps affect projective BLAS bounds.\n geometryKey: key(material, materialGeometryKeys) + '|' + textureKey(material.displacementMap),\n emissive: key(material, ['emissionColor', 'emissionLuminance', 'coatWeight', 'coatColor', 'unlit', 'shadowCatcher']) + '|' + textureKey(material.coatWeightMap),\n emissiveMap: textureKey(material.emissiveMap)\n };\n}\nexport function materialIsEmissive(material: OpenPBRMaterial) {\n return !material.unlit && !material.shadowCatcher &&\n colorLuminance(materialEmission(material)) > 1e-8;\n}\n\n/** Expensive field keys are computed only when receiving changed materials. */\nexport class MaterialChangeTracker {\n private _snapshots = new WeakMap<OpenPBRMaterial, MaterialSnapshot>();\n capture(materials: readonly OpenPBRMaterial[]) {\n for (const material of materials) this._snapshots.set(material, snapshot(material));\n }\n classify(materials: readonly OpenPBRMaterial[]): { level: MaterialUpdateLevel; emission: boolean; emissiveMap: boolean } {\n let level: MaterialUpdateLevel = 'params';\n let emission = false;\n let emissiveMap = false;\n const ranks = { params: 0, emission: 1, textures: 2, geometry: 3 };\n for (const material of materials) {\n const previous = this._snapshots.get(material);\n const next = snapshot(material);\n const emissionChanged = !previous || previous.emissive !== next.emissive;\n emission ||= emissionChanged;\n emissiveMap ||= !previous || previous.emissiveMap !== next.emissiveMap;\n const candidate: MaterialUpdateLevel = !previous || next.geometryKey !== previous.geometryKey\n ? 'geometry' : next.textureKey !== previous.textureKey ? 'textures' : emissionChanged ? 'emission' : 'params';\n if (ranks[candidate] > ranks[level]) level = candidate;\n }\n return { level, emission, emissiveMap };\n }\n}\n"]}
|