@call-me-sensei/toonlab 0.3.1 → 0.4.20
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/NPM-LIBRARY.md +225 -0
- package/README.md +588 -184
- package/agents/PROMPTS.md +87 -0
- package/agents/README.md +54 -0
- package/agents/claude/CLAUDE.md +8 -0
- package/agents/codex/AGENTS.md +159 -0
- package/agents/cursor/toonlab.mdc +37 -0
- package/agents/references/anime-art-direction.md +24 -0
- package/agents/references/asset-sourcing-policy.md +23 -0
- package/agents/references/custom-gap-report.md +16 -0
- package/agents/references/mcp-asset-discovery.md +104 -0
- package/agents/references/runtime-entry-points.md +175 -0
- package/agents/references/style-bundles.md +71 -0
- package/agents/skills/claude/asset-sourcing/SKILL.md +275 -0
- package/agents/skills/claude/environment/SKILL.md +59 -0
- package/agents/skills/claude/game-dev/SKILL.md +100 -0
- package/agents/skills/claude/post-processing/SKILL.md +44 -0
- package/agents/skills/claude/rock-ground-shaders/SKILL.md +134 -0
- package/agents/skills/claude/rockgen/SKILL.md +30 -0
- package/agents/skills/claude/scene-style-application/SKILL.md +74 -0
- package/agents/skills/claude/style-presets/SKILL.md +101 -0
- package/agents/skills/claude/toon-shading/SKILL.md +44 -0
- package/agents/skills/claude/vegetation-sky/SKILL.md +167 -0
- package/agents/skills/claude/visual-verification/SKILL.md +136 -0
- package/agents/skills/claude/water/SKILL.md +105 -0
- package/agents/skills/codex/asset-sourcing/SKILL.md +275 -0
- package/agents/skills/codex/environment/SKILL.md +59 -0
- package/agents/skills/codex/game-dev/SKILL.md +100 -0
- package/agents/skills/codex/post-processing/SKILL.md +44 -0
- package/agents/skills/codex/rock-ground-shaders/SKILL.md +134 -0
- package/agents/skills/codex/rockgen/SKILL.md +30 -0
- package/agents/skills/codex/scene-style-application/SKILL.md +74 -0
- package/agents/skills/codex/style-presets/SKILL.md +101 -0
- package/agents/skills/codex/toon-shading/SKILL.md +44 -0
- package/agents/skills/codex/vegetation-sky/SKILL.md +167 -0
- package/agents/skills/codex/visual-verification/SKILL.md +136 -0
- package/agents/skills/codex/water/SKILL.md +105 -0
- package/cli/toonlab.mjs +53 -0
- package/compose.yaml +20 -0
- package/database/apply-sql.mjs +141 -0
- package/database/client.mjs +39 -0
- package/database/creation-tags.mjs +23 -0
- package/database/env.mjs +16 -0
- package/database/fal-patina.mjs +131 -0
- package/database/generation-service.mjs +415 -0
- package/database/migrations/0001_local_workspace.sql +120 -0
- package/database/migrations/0002_file_ownership.sql +11 -0
- package/database/migrations/0003_remove_autosaves_from_library.sql +7 -0
- package/database/migrations/0004_catalog_asset_packs.sql +39 -0
- package/database/migrations/0005_creation_tag_discovery.sql +1 -0
- package/database/migrations/0006_normalize_creation_tags.sql +28 -0
- package/database/migrations/0007_creation_revisions.sql +80 -0
- package/database/migrations/0008_creation_revision_hardening.sql +98 -0
- package/database/migrations/0009_creation_revision_delete_integrity.sql +11 -0
- package/database/migrations/0010_external_catalog_assets.sql +28 -0
- package/database/providers.mjs +644 -0
- package/database/repository.mjs +1073 -0
- package/database/seeds/catalog/0001_empty_official_catalog.sql +4 -0
- package/database/seeds/catalog/0002_2026-08.sql +19454 -0
- package/database/seeds/catalog/0003_2026-08-open-assets.sql +159086 -0
- package/database/seeds/catalog/0004_2026-08-c7-rocks.sql +39405 -0
- package/mcp/lab-management.mjs +629 -0
- package/mcp/public-catalog.mjs +168 -0
- package/mcp/server.mjs +1044 -63
- package/mcp/vite-plugin.mjs +519 -26
- package/mcp/workspace.mjs +26 -1
- package/package.json +334 -46
- package/scripts/generate-catalog-seed.mjs +324 -0
- package/scripts/setup-local.mjs +128 -0
- package/src/agents/index.js +7 -0
- package/src/agents/sceneStyleOperations.js +169 -0
- package/src/asset-policy/catalogLicenses.js +102 -0
- package/src/asset-policy/index.js +315 -0
- package/src/assetlib/assetRef.js +1 -1
- package/src/assetlib/galleryMaterialFamily.js +165 -0
- package/src/assetlib/index.js +6 -2
- package/src/assetlib/loadImported.js +15 -1
- package/src/assetlib/plateau.js +188 -0
- package/src/assetlib/plateauLandmarks.js +144 -0
- package/src/assetlib/plateauViewer.js +348 -0
- package/src/assetlib/sources.js +16 -2
- package/src/catalog/manifest.js +11 -1
- package/src/catalog/officialCatalog.js +4 -0
- package/src/catalog/officialCatalogAssetRuntime.js +324 -0
- package/src/catalog/officialCatalogLod.js +183 -0
- package/src/catalog/officialCatalogPlacement.js +243 -0
- package/src/catalog/officialCatalogProvider.js +334 -0
- package/src/character/animationRetarget.js +1084 -0
- package/src/character/characterRig.js +6 -1
- package/src/character/characterRuntime.js +544 -0
- package/src/character/groundStabilizer.js +137 -0
- package/src/character/index.js +8 -0
- package/src/character/locomotionActions.js +74 -0
- package/src/character/locomotionStateMachine.js +241 -0
- package/src/character/mannequinAsset.js +15 -0
- package/src/character/mannequinAssetUrl.browser.js +4 -0
- package/src/character/mannequinAssetUrl.node.js +3 -0
- package/src/character/modelLoader.js +92 -12
- package/src/character/walkableCharacterRuntime.js +284 -0
- package/src/character/waterInteractionController.js +217 -0
- package/src/cloud/ambientSkyBaker.js +248 -0
- package/src/cloud/cloudLighting.js +1114 -0
- package/src/cloud/cloudParams.js +819 -0
- package/src/cloud/cloudReprojection.js +563 -0
- package/src/cloud/cloudStyle.js +780 -0
- package/src/cloud/cloudStyleSnapshots.js +387 -0
- package/src/cloud/cloudVolume.js +1903 -0
- package/src/cloud/heroCloudRecipe.js +404 -0
- package/src/cloud/index.js +42 -0
- package/src/cloud/noise/baseShapeVolume.js +331 -0
- package/src/cloud/noise/cirrusMap.js +131 -0
- package/src/cloud/noise/curlNoise.js +289 -0
- package/src/cloud/noise/erosionVolume.js +188 -0
- package/src/cloud/noise/noiseVolume.js +202 -0
- package/src/cloud/noise/periodicNoise3.js +295 -0
- package/src/cloud/noise/weatherMap.js +470 -0
- package/src/cloud/paramSchema.js +458 -0
- package/src/collisionMetadata.js +320 -0
- package/src/core/systemStylePolicy.js +32 -0
- package/src/environment/dayCurves.js +146 -0
- package/src/environment/environmentGroundFieldPass.js +354 -0
- package/src/environment/environmentMaterialAdapter.js +144 -6
- package/src/environment/environmentPresets.js +486 -90
- package/src/environment/environmentRigs.js +17 -2
- package/src/environment/environmentSettings.js +37 -1
- package/src/environment/environmentShaderMaterials.js +37 -6
- package/src/environment/environmentState.js +133 -0
- package/src/environment/environmentSunShadowPass.js +399 -6
- package/src/environment/environmentTextureResolver.js +7 -0
- package/src/environment/index.js +51 -0
- package/src/environment/manufacturedMaterialContract.js +837 -0
- package/src/environment/manufacturedReflectionProvider.js +114 -0
- package/src/environment/scanAssetStylize.js +4 -4
- package/src/environment/surfaceMaterialModes.js +71 -0
- package/src/environment/toonLabEnvironmentMaterials.js +353 -0
- package/src/environment/toonLabMaterialPassCoupling.js +347 -0
- package/src/environment/toonLabNormalIntegration.js +171 -0
- package/src/environment/toonLabRockMaterialResolver.js +592 -0
- package/src/environment/toonLabSourceDefaultLit.js +444 -0
- package/src/environment/toonLabSourceLibrary.js +762 -0
- package/src/environment/toonLabSourceMaterials.js +3718 -0
- package/src/environment/toonLabSourceSubsurfaceLighting.js +415 -0
- package/src/environment/toonLabSourceTemporal.js +717 -0
- package/src/environment/toonLabSurfaceLighting.js +462 -0
- package/src/environment/toonLabTextureReadiness.js +105 -0
- package/src/environment/toonLabTreeMaterials.js +450 -0
- package/src/environment/transparentMaterialProfile.js +87 -0
- package/src/environment/urbanPropMaterial.js +2976 -0
- package/src/ground-shader/groundPrintLayer.js +375 -0
- package/src/ground-shader/groundShaderMaterial.js +851 -0
- package/src/ground-shader/groundShaderSettings.js +468 -0
- package/src/ground-shader/index.js +3 -0
- package/src/index.js +52 -22
- package/src/lighting/callMeSenseiLightingContract.js +163 -0
- package/src/lighting/index.js +14 -7
- package/src/lighting/lightingCapabilities.js +2 -2
- package/src/lighting/lightingPresets.js +6 -3
- package/src/lighting/lightingStyle.js +41 -6
- package/src/lighting/lightingSystem.js +200 -6
- package/src/lighting/{unrealExport.js → toonLabExport.js} +40 -41
- package/src/post/postProcessing.js +71 -12
- package/src/react/index.js +66 -0
- package/src/renderer/index.js +16 -0
- package/src/renderer/rendererConfiguration.js +250 -0
- package/src/renderer/styleComparison.js +985 -0
- package/src/rock-shader/index.js +8 -0
- package/src/rock-shader/rockGeometryDetail.js +701 -0
- package/src/rock-shader/rockMaterial.js +2586 -0
- package/src/rock-shader/rockRegionRuntime.js +185 -0
- package/src/rock-shader/rockSemanticMaterialRuntime.js +337 -0
- package/src/rock-shader/rockShaderRuntime.js +781 -0
- package/src/rock-shader/rockShaderSettings.js +1099 -0
- package/src/rock-shader/rockTangentIntegrity.js +136 -0
- package/src/rock-shader/rockTextureIntegrity.js +115 -0
- package/src/rockgen/export/glbExport.js +104 -14
- package/src/rockgen/index.js +5 -3
- package/src/rockgen/lod/index.js +6 -0
- package/src/rockgen/lod/rockLodMetrics.js +168 -0
- package/src/rockgen/lod/rockLodPlanner.js +372 -0
- package/src/rockgen/lod/rockLodPolicy.js +235 -0
- package/src/rockgen/lod/rockLodRuntime.js +124 -0
- package/src/rockgen/lod/rockLodSimplifier.js +173 -0
- package/src/rockgen/lod/rockLodValidation.js +113 -0
- package/src/rockgen/mesh/meshAttributes.js +17 -0
- package/src/rockgen/mesh/meshDocument.js +6 -0
- package/src/rockgen/rockDocument.js +376 -9
- package/src/rockgen/rockgenPresets.js +114 -13
- package/src/rockgen/rockgenSettings.js +62 -11
- package/src/rockgen/surface/c7GeologySurface.js +372 -0
- package/src/runtime/index.js +14 -0
- package/src/runtime/sceneCollisionRuntime.js +317 -0
- package/src/runtime/sceneSurfaceRuntime.js +451 -0
- package/src/runtime/sceneUpdateScheduler.js +203 -0
- package/src/shaders-tsl/anime.js +9 -2
- package/src/shaders-tsl/chunks/environment-ground-field.js +174 -0
- package/src/shaders-tsl/chunks/environment-state.js +79 -0
- package/src/shaders-tsl/chunks/environment-sun-shadow.js +99 -32
- package/src/shaders-tsl/chunks/normalized-hue.js +41 -0
- package/src/shaders-tsl/chunks/pass-depth-color.js +4 -2
- package/src/shaders-tsl/chunks/scene-depth-color-pass.js +273 -0
- package/src/shaders-tsl/chunks/vegetation-style.js +116 -8
- package/src/shaders-tsl/chunks/water-color.js +19 -5
- package/src/shaders-tsl/chunks/water-shore-state.js +8 -1
- package/src/shaders-tsl/chunks/world-noise.js +30 -0
- package/src/shaders-tsl/environment.js +123 -17
- package/src/shaders-tsl/flower.js +51 -8
- package/src/shaders-tsl/grass.js +194 -29
- package/src/shaders-tsl/post-composite.js +83 -2
- package/src/shaders-tsl/tree-leaf.js +107 -16
- package/src/shaders-tsl/water-breaker.js +18 -4
- package/src/shaders-tsl/water.js +51 -3
- package/src/shaders-tsl/woody-surface.js +59 -7
- package/src/sky/atmosphereDome.js +193 -0
- package/src/sky/atmosphereParams.js +396 -0
- package/src/sky/atmosphereScattering.js +593 -0
- package/src/sky/atmosphereSky.js +371 -0
- package/src/sky/cloudShadow.js +379 -0
- package/src/sky/godRays.js +257 -0
- package/src/sky/index.js +55 -1
- package/src/sky/nightSky.js +1190 -0
- package/src/sky/renderLayers.js +120 -0
- package/src/sky/skyColor.js +582 -0
- package/src/sky/skyDayCycle.js +244 -0
- package/src/sky/skyParams.js +661 -0
- package/src/sky/skyPresets.js +772 -0
- package/src/sky/skyQualityTiers.js +383 -0
- package/src/sky/skyShaderSettings.js +699 -0
- package/src/sky/skyStyleSnapshots.js +211 -0
- package/src/sky/skySystem.js +1447 -0
- package/src/sky/skyTimeKeyframes.js +361 -0
- package/src/sky/stylizedSky.js +405 -116
- package/src/sky/sunDriver.js +499 -0
- package/src/sky/timeOfDay.js +335 -0
- package/src/styles/animeGameProfile.js +44 -0
- package/src/styles/index.js +157 -0
- package/src/styles/manufacturedStyleLabeling.js +204 -0
- package/src/styles/neutralStylePresets.js +712 -0
- package/src/styles/sceneLayerDocuments.js +185 -0
- package/src/styles/sceneLookComposition.js +156 -0
- package/src/styles/sceneQualityApplication.js +152 -0
- package/src/styles/sceneQualityProfiles.js +268 -0
- package/src/styles/sceneStyleAudit.js +512 -0
- package/src/styles/sceneStyleRuntime.js +923 -0
- package/src/styles/styleAdapters.js +177 -0
- package/src/styles/styleApplication.js +369 -0
- package/src/styles/styleBundle.js +445 -92
- package/src/styles/styleBundleProvider.js +83 -0
- package/src/styles/styleDomains.js +21 -0
- package/src/styles/styleInspector.js +262 -0
- package/src/styles/styleMaterialContract.js +375 -0
- package/src/styles/styleMetadata.js +73 -0
- package/src/styles/styleTargetDiscovery.js +162 -0
- package/src/styles/styleTargetLabels.js +206 -0
- package/src/styles/styleTargetMaterialCoverage.js +137 -0
- package/src/styles/styleTransaction.js +272 -0
- package/src/texgen/evaluateTexture.js +30 -3
- package/src/texgen/textureGenerators.js +83 -35
- package/src/texgen/textureSettings.js +25 -8
- package/src/toon/toonSettings.js +100 -2
- package/src/vegetation/branchTree.js +587 -0
- package/src/vegetation/callMeSenseiGrass.js +183 -0
- package/src/vegetation/compiledTree.js +506 -0
- package/src/vegetation/contactShadowField.js +107 -0
- package/src/vegetation/flowerSpecies.js +10 -2
- package/src/vegetation/grass.js +13 -0
- package/src/vegetation/grassClump.js +1179 -0
- package/src/vegetation/importedVegetationMaterial.js +183 -0
- package/src/vegetation/index.js +17 -1
- package/src/vegetation/legacyTreePresets.js +120 -0
- package/src/vegetation/scatter.js +688 -3
- package/src/vegetation/stylizedBush.js +14 -0
- package/src/vegetation/stylizedFlower.js +31 -0
- package/src/vegetation/stylizedFlowers.js +59 -7
- package/src/vegetation/stylizedForest.js +455 -147
- package/src/vegetation/stylizedGrass.js +188 -24
- package/src/vegetation/stylizedTree.js +676 -54
- package/src/vegetation/stylizedTreeFoliage.js +1111 -38
- package/src/vegetation/stylizedUnderstory.js +230 -0
- package/src/vegetation/treeExport.js +262 -47
- package/src/vegetation/treeSurfaceTextures.js +665 -0
- package/src/vegetation/vegetationShaders.js +617 -28
- package/src/version.js +3 -0
- package/src/water/water.js +2 -0
- package/src/water/waterBreakerSystem.js +2 -0
- package/src/water/waterCurrentField.js +1 -0
- package/src/water/waterMaterial.js +3 -0
- package/src/water/waterRain.js +2 -0
- package/src/water/waterScenePasses.js +170 -7
- package/src/water/waterSettings.js +250 -50
- package/src/water/waterShoreMaterial.js +16 -2
- package/src/water/waterShoreStateField.js +1 -1
- package/src/water/waterSplashSystem.js +2 -0
- package/src/water/waterStageSettings.js +60 -0
- package/src/water/waterSurface.js +144 -4
- package/src/water/waterUnderwaterAtmosphere.js +288 -0
- package/src/water/waterVegetation.js +20 -4
- package/src/worldCollision.js +64 -9
- package/types/agents/index.d.ts +1 -0
- package/types/agents/sceneStyleOperations.d.ts +484 -0
- package/types/asset-policy/catalogLicenses.d.ts +7 -0
- package/types/asset-policy/index.d.ts +20 -0
- package/types/assetlib/ambientcg.d.ts +81 -0
- package/types/assetlib/assetRef.d.ts +26 -0
- package/types/assetlib/galleryMaterialFamily.d.ts +13 -0
- package/types/assetlib/importedEntry.d.ts +18 -0
- package/types/assetlib/index.d.ts +16 -0
- package/types/assetlib/kaykit.d.ts +112 -0
- package/types/assetlib/kaykitStaticIndex.d.ts +38 -0
- package/types/assetlib/loadImported.d.ts +56 -0
- package/types/assetlib/opensource3d.d.ts +55 -0
- package/types/assetlib/plateau.d.ts +67 -0
- package/types/assetlib/plateauLandmarks.d.ts +238 -0
- package/types/assetlib/plateauViewer.d.ts +3 -0
- package/types/assetlib/polyhaven.d.ts +90 -0
- package/types/assetlib/polypizza.d.ts +10 -0
- package/types/assetlib/smithsonian.d.ts +9 -0
- package/types/assetlib/sources.d.ts +22 -0
- package/types/assetlib/zip.d.ts +8 -0
- package/types/catalog/manifest.d.ts +26 -0
- package/types/catalog/officialCatalog.d.ts +4 -0
- package/types/catalog/officialCatalogAssetRuntime.d.ts +2 -0
- package/types/catalog/officialCatalogLod.d.ts +63 -0
- package/types/catalog/officialCatalogPlacement.d.ts +49 -0
- package/types/catalog/officialCatalogProvider.d.ts +7 -0
- package/types/character/animationRetarget.d.ts +20 -0
- package/types/character/characterRig.d.ts +87 -0
- package/types/character/characterRuntime.d.ts +5 -0
- package/types/character/freestyleSwimClip.d.ts +5 -0
- package/types/character/groundStabilizer.d.ts +55 -0
- package/types/character/index.d.ts +10 -0
- package/types/character/locomotionActions.d.ts +4 -0
- package/types/character/locomotionStateMachine.d.ts +62 -0
- package/types/character/mannequinAsset.d.ts +12 -0
- package/types/character/mannequinAssetUrl.node.d.ts +1 -0
- package/types/character/modelLoader.d.ts +7 -0
- package/types/character/walkableCharacterRuntime.d.ts +343 -0
- package/types/character/waterInteractionController.d.ts +114 -0
- package/types/cloud/ambientSkyBaker.d.ts +18 -0
- package/types/cloud/cloudLighting.d.ts +38 -0
- package/types/cloud/cloudParams.d.ts +18 -0
- package/types/cloud/cloudReprojection.d.ts +15 -0
- package/types/cloud/cloudStyle.d.ts +1380 -0
- package/types/cloud/cloudStyleSnapshots.d.ts +10 -0
- package/types/cloud/cloudVolume.d.ts +43 -0
- package/types/cloud/heroCloudRecipe.d.ts +167 -0
- package/types/cloud/index.d.ts +12 -0
- package/types/cloud/noise/baseShapeVolume.d.ts +86 -0
- package/types/cloud/noise/cirrusMap.d.ts +24 -0
- package/types/cloud/noise/curlNoise.d.ts +106 -0
- package/types/cloud/noise/erosionVolume.d.ts +58 -0
- package/types/cloud/noise/noiseVolume.d.ts +63 -0
- package/types/cloud/noise/periodicNoise3.d.ts +75 -0
- package/types/cloud/noise/weatherMap.d.ts +227 -0
- package/types/cloud/paramSchema.d.ts +142 -0
- package/types/collisionMetadata.d.ts +11 -0
- package/types/core/generation.d.ts +95 -0
- package/types/core/materialRoles.d.ts +102 -0
- package/types/core/presetDocuments.d.ts +25 -0
- package/types/core/systemStylePolicy.d.ts +4 -0
- package/types/environment/dayCurves.d.ts +47 -0
- package/types/environment/environmentAmbientProbe.d.ts +2 -0
- package/types/environment/environmentGroundFieldPass.d.ts +2 -0
- package/types/environment/environmentMaterialAdapter.d.ts +44 -0
- package/types/environment/environmentMaterialClassifier.d.ts +26 -0
- package/types/environment/environmentPlanarReflection.d.ts +3 -0
- package/types/environment/environmentPresets.d.ts +171 -0
- package/types/environment/environmentRigs.d.ts +13 -0
- package/types/environment/environmentSettings.d.ts +161 -0
- package/types/environment/environmentShaderMaterials.d.ts +14 -0
- package/types/environment/environmentState.d.ts +6 -0
- package/types/environment/environmentSunShadowPass.d.ts +2 -0
- package/types/environment/environmentTextureResolver.d.ts +30 -0
- package/types/environment/environmentTimeOfDay.d.ts +25 -0
- package/types/environment/environmentVertexAo.d.ts +18 -0
- package/types/environment/index.d.ts +27 -0
- package/types/environment/manufacturedMaterialContract.d.ts +129 -0
- package/types/environment/manufacturedReflectionProvider.d.ts +3 -0
- package/types/environment/scanAssetStylize.d.ts +43 -0
- package/types/environment/surfaceMaterialModes.d.ts +14 -0
- package/types/environment/toonLabEnvironmentMaterials.d.ts +139 -0
- package/types/environment/toonLabMaterialPassCoupling.d.ts +7 -0
- package/types/environment/toonLabNormalIntegration.d.ts +29 -0
- package/types/environment/toonLabRockMaterialResolver.d.ts +270 -0
- package/types/environment/toonLabSourceDefaultLit.d.ts +136 -0
- package/types/environment/toonLabSourceLibrary.d.ts +19 -0
- package/types/environment/toonLabSourceMaterials.d.ts +14 -0
- package/types/environment/toonLabSourceSubsurfaceLighting.d.ts +85 -0
- package/types/environment/toonLabSourceTemporal.d.ts +20 -0
- package/types/environment/toonLabSurfaceLighting.d.ts +153 -0
- package/types/environment/toonLabTextureReadiness.d.ts +8 -0
- package/types/environment/toonLabTreeMaterials.d.ts +7 -0
- package/types/environment/transparentMaterialProfile.d.ts +76 -0
- package/types/environment/urbanPropMaterial.d.ts +25 -0
- package/types/ground-shader/groundPrintLayer.d.ts +4 -0
- package/types/ground-shader/groundShaderMaterial.d.ts +11 -0
- package/types/ground-shader/groundShaderSettings.d.ts +90 -0
- package/types/ground-shader/index.d.ts +3 -0
- package/types/index.d.ts +1591 -0
- package/types/lighting/callMeSenseiLightingContract.d.ts +14 -0
- package/types/lighting/colorIntensity.d.ts +44 -0
- package/types/lighting/index.d.ts +12 -0
- package/types/lighting/lightDescriptors.d.ts +803 -0
- package/types/lighting/lightingCapabilities.d.ts +85 -0
- package/types/lighting/lightingDocuments.d.ts +140 -0
- package/types/lighting/lightingFixtures.d.ts +171 -0
- package/types/lighting/lightingGenerator.d.ts +403 -0
- package/types/lighting/lightingPresets.d.ts +264 -0
- package/types/lighting/lightingRuntime.d.ts +401 -0
- package/types/lighting/lightingStyle.d.ts +156 -0
- package/types/lighting/lightingSystem.d.ts +440 -0
- package/types/lighting/toonLabExport.d.ts +46 -0
- package/types/lighting/utils.d.ts +18 -0
- package/types/loaders/index.d.ts +1 -0
- package/types/post/index.d.ts +2 -0
- package/types/post/postGenerator.d.ts +282 -0
- package/types/post/postProcessing.d.ts +16 -0
- package/types/react/index.d.ts +1785 -0
- package/types/renderer/index.d.ts +2 -0
- package/types/renderer/rendererConfiguration.d.ts +8 -0
- package/types/renderer/styleComparison.d.ts +409 -0
- package/types/rock-shader/index.d.ts +8 -0
- package/types/rock-shader/rockGeometryDetail.d.ts +300 -0
- package/types/rock-shader/rockMaterial.d.ts +21 -0
- package/types/rock-shader/rockRegionRuntime.d.ts +86 -0
- package/types/rock-shader/rockSemanticMaterialRuntime.d.ts +29 -0
- package/types/rock-shader/rockShaderRuntime.d.ts +10 -0
- package/types/rock-shader/rockShaderSettings.d.ts +184 -0
- package/types/rock-shader/rockTangentIntegrity.d.ts +51 -0
- package/types/rock-shader/rockTextureIntegrity.d.ts +61 -0
- package/types/rockgen/export/glbExport.d.ts +58 -0
- package/types/rockgen/heightfield/heightfieldErosion.d.ts +10 -0
- package/types/rockgen/heightfield/heightfieldPatch.d.ts +15 -0
- package/types/rockgen/heightfield/stylizedErosionSim.d.ts +41 -0
- package/types/rockgen/index.d.ts +12 -0
- package/types/rockgen/lod/index.d.ts +6 -0
- package/types/rockgen/lod/rockLodMetrics.d.ts +62 -0
- package/types/rockgen/lod/rockLodPlanner.d.ts +86 -0
- package/types/rockgen/lod/rockLodPolicy.d.ts +76 -0
- package/types/rockgen/lod/rockLodRuntime.d.ts +95 -0
- package/types/rockgen/lod/rockLodSimplifier.d.ts +15 -0
- package/types/rockgen/lod/rockLodValidation.d.ts +16 -0
- package/types/rockgen/mesh/meshAttributes.d.ts +32 -0
- package/types/rockgen/mesh/meshDocument.d.ts +40 -0
- package/types/rockgen/mesh/surfaceNets.d.ts +42 -0
- package/types/rockgen/noise/cellularNoise3.d.ts +24 -0
- package/types/rockgen/noise/prng.d.ts +16 -0
- package/types/rockgen/noise/simplexNoise3.d.ts +2 -0
- package/types/rockgen/noise/valueNoise3.d.ts +12 -0
- package/types/rockgen/rockDocument.d.ts +243 -0
- package/types/rockgen/rockHelpers.d.ts +2 -0
- package/types/rockgen/rockgenPresets.d.ts +10 -0
- package/types/rockgen/rockgenSettings.d.ts +531 -0
- package/types/rockgen/sdf/fieldCompiler.d.ts +20 -0
- package/types/rockgen/sdf/sculptEdits.d.ts +30 -0
- package/types/rockgen/sdf/sdfModifiers.d.ts +19 -0
- package/types/rockgen/sdf/sdfOps.d.ts +8 -0
- package/types/rockgen/sdf/sdfPrimitives.d.ts +22 -0
- package/types/rockgen/surface/c7GeologySurface.d.ts +8 -0
- package/types/runtime/index.d.ts +3 -0
- package/types/runtime/sceneCollisionRuntime.d.ts +94 -0
- package/types/runtime/sceneSurfaceRuntime.d.ts +90 -0
- package/types/runtime/sceneUpdateScheduler.d.ts +28 -0
- package/types/shaders-tsl/anime.d.ts +9 -0
- package/types/shaders-tsl/chunks/character-color.d.ts +6 -0
- package/types/shaders-tsl/chunks/character-highlights.d.ts +12 -0
- package/types/shaders-tsl/chunks/character-lighting.d.ts +20 -0
- package/types/shaders-tsl/chunks/character-material-maps.d.ts +18 -0
- package/types/shaders-tsl/chunks/character-roles.d.ts +21 -0
- package/types/shaders-tsl/chunks/character-scene-lights.d.ts +48 -0
- package/types/shaders-tsl/chunks/character-shadow-color.d.ts +5 -0
- package/types/shaders-tsl/chunks/character-skinning.d.ts +23 -0
- package/types/shaders-tsl/chunks/environment-color.d.ts +9 -0
- package/types/shaders-tsl/chunks/environment-debug.d.ts +24 -0
- package/types/shaders-tsl/chunks/environment-ground-field.d.ts +40 -0
- package/types/shaders-tsl/chunks/environment-lighting.d.ts +22 -0
- package/types/shaders-tsl/chunks/environment-state.d.ts +42 -0
- package/types/shaders-tsl/chunks/environment-sun-shadow.d.ts +19 -0
- package/types/shaders-tsl/chunks/foliage-fog.d.ts +21 -0
- package/types/shaders-tsl/chunks/normalized-hue.d.ts +1 -0
- package/types/shaders-tsl/chunks/pass-depth-color.d.ts +20 -0
- package/types/shaders-tsl/chunks/projected-water-caustics.d.ts +24 -0
- package/types/shaders-tsl/chunks/scene-depth-color-pass.d.ts +39 -0
- package/types/shaders-tsl/chunks/stylized-cloud-shadow.d.ts +9 -0
- package/types/shaders-tsl/chunks/vegetation-style.d.ts +60 -0
- package/types/shaders-tsl/chunks/water-color.d.ts +15 -0
- package/types/shaders-tsl/chunks/water-common.d.ts +9 -0
- package/types/shaders-tsl/chunks/water-foam.d.ts +8 -0
- package/types/shaders-tsl/chunks/water-lighting.d.ts +9 -0
- package/types/shaders-tsl/chunks/water-ripple.d.ts +8 -0
- package/types/shaders-tsl/chunks/water-shore-state.d.ts +7 -0
- package/types/shaders-tsl/chunks/water-waves.d.ts +18 -0
- package/types/shaders-tsl/chunks/world-noise.d.ts +4 -0
- package/types/shaders-tsl/environment-ao-overlay.d.ts +5 -0
- package/types/shaders-tsl/environment.d.ts +34 -0
- package/types/shaders-tsl/flower.d.ts +6 -0
- package/types/shaders-tsl/grass.d.ts +4 -0
- package/types/shaders-tsl/post-composite.d.ts +25 -0
- package/types/shaders-tsl/sky.d.ts +4 -0
- package/types/shaders-tsl/tree-leaf.d.ts +2 -0
- package/types/shaders-tsl/water-breaker.d.ts +15 -0
- package/types/shaders-tsl/water-kelp.d.ts +5 -0
- package/types/shaders-tsl/water-rain.d.ts +10 -0
- package/types/shaders-tsl/water-shore-state-simulation.d.ts +12 -0
- package/types/shaders-tsl/water-simulation.d.ts +5 -0
- package/types/shaders-tsl/water-splash.d.ts +12 -0
- package/types/shaders-tsl/water.d.ts +22 -0
- package/types/shaders-tsl/woody-surface.d.ts +3 -0
- package/types/sky/atmosphereDome.d.ts +2 -0
- package/types/sky/atmosphereParams.d.ts +185 -0
- package/types/sky/atmosphereScattering.d.ts +11 -0
- package/types/sky/atmosphereSky.d.ts +2 -0
- package/types/sky/cloudShadow.d.ts +69 -0
- package/types/sky/godRays.d.ts +5 -0
- package/types/sky/index.d.ts +18 -0
- package/types/sky/nightSky.d.ts +22 -0
- package/types/sky/renderLayers.d.ts +59 -0
- package/types/sky/sceneOverrideLayers.d.ts +10 -0
- package/types/sky/skyColor.d.ts +704 -0
- package/types/sky/skyDayCycle.d.ts +86 -0
- package/types/sky/skyParams.d.ts +3407 -0
- package/types/sky/skyPresets.d.ts +160 -0
- package/types/sky/skyQuality.d.ts +21 -0
- package/types/sky/skyQualityTiers.d.ts +223 -0
- package/types/sky/skyShaderSettings.d.ts +152 -0
- package/types/sky/skyStyleSnapshots.d.ts +11 -0
- package/types/sky/skySystem.d.ts +3 -0
- package/types/sky/skyTimeKeyframes.d.ts +230 -0
- package/types/sky/stylizedSky.d.ts +341 -0
- package/types/sky/sunDriver.d.ts +15 -0
- package/types/sky/timeOfDay.d.ts +145 -0
- package/types/styles/animeGameProfile.d.ts +27 -0
- package/types/styles/index.d.ts +18 -0
- package/types/styles/manufacturedStyleLabeling.d.ts +119 -0
- package/types/styles/neutralStylePresets.d.ts +944 -0
- package/types/styles/sceneLayerDocuments.d.ts +145 -0
- package/types/styles/sceneLookComposition.d.ts +53 -0
- package/types/styles/sceneQualityApplication.d.ts +34 -0
- package/types/styles/sceneQualityProfiles.d.ts +13 -0
- package/types/styles/sceneStyleAudit.d.ts +80 -0
- package/types/styles/sceneStyleRuntime.d.ts +1802 -0
- package/types/styles/styleAdapters.d.ts +14 -0
- package/types/styles/styleApplication.d.ts +47 -0
- package/types/styles/styleBundle.d.ts +19 -0
- package/types/styles/styleBundleProvider.d.ts +44 -0
- package/types/styles/styleDomains.d.ts +18 -0
- package/types/styles/styleInspector.d.ts +59 -0
- package/types/styles/styleMaterialContract.d.ts +46 -0
- package/types/styles/styleMetadata.d.ts +11 -0
- package/types/styles/styleTargetDiscovery.d.ts +48 -0
- package/types/styles/styleTargetLabels.d.ts +32 -0
- package/types/styles/styleTargetMaterialCoverage.d.ts +20 -0
- package/types/styles/styleTransaction.d.ts +10 -0
- package/types/styles/styleTypes.d.ts +66 -0
- package/types/texgen/evaluateTexture.d.ts +40 -0
- package/types/texgen/index.d.ts +7 -0
- package/types/texgen/noise2.d.ts +46 -0
- package/types/texgen/textureAi.d.ts +6 -0
- package/types/texgen/textureGenerators.d.ts +261 -0
- package/types/texgen/texturePresets.d.ts +45 -0
- package/types/texgen/textureSettings.d.ts +121 -0
- package/types/texgen/textureThree.d.ts +13 -0
- package/types/toon/characterRenderPasses.d.ts +3 -0
- package/types/toon/index.d.ts +3 -0
- package/types/toon/settings/alphaSettings.d.ts +125 -0
- package/types/toon/settings/averageShadowSettings.d.ts +39 -0
- package/types/toon/settings/baseTextureSettings.d.ts +39 -0
- package/types/toon/settings/celShadeSettings.d.ts +37 -0
- package/types/toon/settings/contactShadowSettings.d.ts +30 -0
- package/types/toon/settings/eyeHighlightSettings.d.ts +55 -0
- package/types/toon/settings/faceLightingSettings.d.ts +32 -0
- package/types/toon/settings/furSettings.d.ts +23 -0
- package/types/toon/settings/glitterSettings.d.ts +43 -0
- package/types/toon/settings/hairHighlightSettings.d.ts +89 -0
- package/types/toon/settings/indirectLightSettings.d.ts +43 -0
- package/types/toon/settings/localLightSettings.d.ts +46 -0
- package/types/toon/settings/materialMapSettings.d.ts +86 -0
- package/types/toon/settings/outlineSettings.d.ts +125 -0
- package/types/toon/settings/perspectiveRemovalSettings.d.ts +14 -0
- package/types/toon/settings/rimLightSettings.d.ts +79 -0
- package/types/toon/settings/sceneShadowSettings.d.ts +32 -0
- package/types/toon/settings/selfShadowSettings.d.ts +44 -0
- package/types/toon/settings/shadowColorSettings.d.ts +77 -0
- package/types/toon/settings/skinToneSettings.d.ts +30 -0
- package/types/toon/settings/specularSettings.d.ts +96 -0
- package/types/toon/settings/stickerSettings.d.ts +40 -0
- package/types/toon/toonMaterialAdapter.d.ts +883 -0
- package/types/toon/toonSettings.d.ts +1182 -0
- package/types/vegetation/branchTree.d.ts +14 -0
- package/types/vegetation/callMeSenseiGrass.d.ts +8 -0
- package/types/vegetation/compiledTree.d.ts +75 -0
- package/types/vegetation/contactShadowField.d.ts +26 -0
- package/types/vegetation/flowerSpecies.d.ts +107 -0
- package/types/vegetation/grass.d.ts +4 -0
- package/types/vegetation/grassClump.d.ts +7 -0
- package/types/vegetation/grassPalettes.d.ts +29 -0
- package/types/vegetation/importedVegetationMaterial.d.ts +13 -0
- package/types/vegetation/index.d.ts +225 -0
- package/types/vegetation/legacyTreePresets.d.ts +22 -0
- package/types/vegetation/scatter.d.ts +13 -0
- package/types/vegetation/stylizedBush.d.ts +2 -0
- package/types/vegetation/stylizedFlower.d.ts +2 -0
- package/types/vegetation/stylizedFlowers.d.ts +8 -0
- package/types/vegetation/stylizedForest.d.ts +3 -0
- package/types/vegetation/stylizedGrass.d.ts +21 -0
- package/types/vegetation/stylizedTree.d.ts +20 -0
- package/types/vegetation/stylizedTreeFoliage.d.ts +19 -0
- package/types/vegetation/stylizedUnderstory.d.ts +50 -0
- package/types/vegetation/treeExport.d.ts +6 -0
- package/types/vegetation/treeSurfaceTextures.d.ts +72 -0
- package/types/vegetation/vegetationShaders.d.ts +74 -0
- package/types/version.d.ts +1 -0
- package/types/water/index.d.ts +1 -0
- package/types/water/sceneOverrideLayers.d.ts +16 -0
- package/types/water/water.d.ts +17 -0
- package/types/water/waterBreakerSystem.d.ts +46 -0
- package/types/water/waterCurrentField.d.ts +115 -0
- package/types/water/waterInteraction.d.ts +21 -0
- package/types/water/waterMaterial.d.ts +60 -0
- package/types/water/waterNearshorePhase.d.ts +5 -0
- package/types/water/waterRain.d.ts +17 -0
- package/types/water/waterRippleSimulation.d.ts +52 -0
- package/types/water/waterScenePasses.d.ts +70 -0
- package/types/water/waterSettings.d.ts +648 -0
- package/types/water/waterShoreMaterial.d.ts +3 -0
- package/types/water/waterShoreStateField.d.ts +2 -0
- package/types/water/waterSplashSystem.d.ts +2 -0
- package/types/water/waterStageSettings.d.ts +9 -0
- package/types/water/waterSurface.d.ts +2 -0
- package/types/water/waterUnderwaterAtmosphere.d.ts +103 -0
- package/types/water/waterVegetation.d.ts +16 -0
- package/types/worldCollision.d.ts +22 -0
- package/AGENTS.md +0 -254
- package/ATTRIBUTION.md +0 -78
- package/docs/characters.md +0 -144
- package/docs/debug-panel.md +0 -126
- package/docs/docs.css +0 -589
- package/docs/environment.md +0 -186
- package/docs/getting-started.md +0 -180
- package/docs/index.html +0 -14
- package/docs/lab-architecture.md +0 -100
- package/docs/lighting.md +0 -825
- package/docs/main.jsx +0 -752
- package/docs/mcp.md +0 -97
- package/docs/post-processing.md +0 -98
- package/docs/settings-reference.md +0 -1878
- package/docs/shader-constants.md +0 -77
- package/docs/sky.md +0 -182
- package/docs/style-labs.md +0 -309
- package/docs/texture-lab.md +0 -135
- package/docs/toon-shading.md +0 -183
- package/docs/tsl-conventions.md +0 -167
- package/docs/vegetation-sky.md +0 -275
- package/docs/water.md +0 -430
- package/docs/weather.md +0 -200
- package/docs/world-scale.md +0 -111
- package/mcp/style-lab-tools.mjs +0 -371
- package/src/ambientfx/INTEGRATION.md +0 -164
- package/src/ambientfx/ambientFxPresets.js +0 -83
- package/src/ambientfx/ambientFxSettings.js +0 -368
- package/src/ambientfx/emitters.js +0 -169
- package/src/ambientfx/index.js +0 -5
- package/src/ambientfx/particleBackbone.js +0 -493
- package/src/ambientfx/stylizedAmbientFx.js +0 -450
- package/src/biome/biomeGenerator.js +0 -385
- package/src/biome/biomeRuntime.js +0 -299
- package/src/biome/index.js +0 -2
- package/src/buildinggen/buildingAsset.js +0 -44
- package/src/buildinggen/buildingGrammar.js +0 -311
- package/src/buildinggen/buildingMesh.js +0 -451
- package/src/buildinggen/buildingPresets.js +0 -46
- package/src/buildinggen/buildingRecipe.js +0 -100
- package/src/buildinggen/buildingSettings.js +0 -238
- package/src/buildinggen/index.js +0 -6
- package/src/camera/cameraDirector.js +0 -157
- package/src/camera/cameraGenerator.js +0 -367
- package/src/camera/cameraRig.js +0 -570
- package/src/camera/cameraSettings.js +0 -236
- package/src/camera/index.js +0 -7
- package/src/catalog/builtinEntries.js +0 -245
- package/src/catalog/catalog.js +0 -210
- package/src/catalog/index.js +0 -3
- package/src/core/shaderBackend.js +0 -24
- package/src/debrisgen/debrisFields.js +0 -544
- package/src/debrisgen/debrisGenerator.js +0 -1929
- package/src/debrisgen/debrisPalettes.js +0 -71
- package/src/debrisgen/debrisPhysics.js +0 -198
- package/src/debrisgen/debrisPresets.js +0 -162
- package/src/debrisgen/debrisSettings.js +0 -333
- package/src/debrisgen/debrisTextures.js +0 -380
- package/src/debrisgen/index.js +0 -5
- package/src/debug/fieldValues.js +0 -95
- package/src/debug/index.js +0 -7
- package/src/debug/settingsPanel.js +0 -170
- package/src/fauna/INTEGRATION.md +0 -174
- package/src/fauna/boids.js +0 -861
- package/src/fauna/faunaBodies.js +0 -492
- package/src/fauna/faunaPresets.js +0 -52
- package/src/fauna/faunaSettings.js +0 -525
- package/src/fauna/index.js +0 -5
- package/src/fauna/stylizedFauna.js +0 -395
- package/src/game-feel/gameFeelGenerator.js +0 -402
- package/src/game-feel/gameFeelRuntime.js +0 -549
- package/src/game-feel/index.js +0 -2
- package/src/motion/index.js +0 -5
- package/src/motion/motionClip.js +0 -441
- package/src/motion/motionController.js +0 -628
- package/src/motion/motionDocuments.js +0 -225
- package/src/motion/motionGraph.js +0 -307
- package/src/motion/motionSettings.js +0 -222
- package/src/pathgen/index.js +0 -7
- package/src/pathgen/pathBridge.js +0 -232
- package/src/pathgen/pathPresets.js +0 -35
- package/src/pathgen/pathRibbon.js +0 -410
- package/src/pathgen/pathRouter.js +0 -380
- package/src/pathgen/pathSettings.js +0 -335
- package/src/pathgen/pathTextures.js +0 -123
- package/src/pathgen/stylizedPaths.js +0 -453
- package/src/propgen/generatorsWave1.js +0 -379
- package/src/propgen/generatorsWave2.js +0 -462
- package/src/propgen/index.js +0 -5
- package/src/propgen/propAsset.js +0 -323
- package/src/propgen/propParts.js +0 -170
- package/src/propgen/propPlacement.js +0 -459
- package/src/propgen/propPresets.js +0 -82
- package/src/propgen/propSettings.js +0 -395
- package/src/soundscape/index.js +0 -4
- package/src/soundscape/soundscapeGenerator.js +0 -179
- package/src/soundscape/soundscapeRuntime.js +0 -806
- package/src/soundscape/soundscapeSettings.js +0 -292
- package/src/stylizedTerrain.js +0 -631
- package/src/stylizedWorld.js +0 -903
- package/src/vegetation/treeRecipe.js +0 -647
- package/src/vfxgen/INTEGRATION.md +0 -145
- package/src/vfxgen/core/burstBackbone.js +0 -380
- package/src/vfxgen/core/projectileCore.js +0 -92
- package/src/vfxgen/core/spriteShapes.js +0 -98
- package/src/vfxgen/core/trailRibbon.js +0 -272
- package/src/vfxgen/core/vfxRandom.js +0 -29
- package/src/vfxgen/effects/emitHelpers.js +0 -37
- package/src/vfxgen/effects/magicEffects.js +0 -162
- package/src/vfxgen/effects/movementEffects.js +0 -87
- package/src/vfxgen/effects/weaponEffects.js +0 -118
- package/src/vfxgen/index.js +0 -18
- package/src/vfxgen/moves/moveController.js +0 -146
- package/src/vfxgen/moves/moveLibrary.js +0 -335
- package/src/vfxgen/vfxPresets.js +0 -98
- package/src/vfxgen/vfxSettings.js +0 -384
- package/src/vfxgen/vfxSystem.js +0 -449
- package/src/vfxgen/weapons/stylizedWeapons.js +0 -137
- package/src/villagegen/index.js +0 -4
- package/src/villagegen/stylizedVillage.js +0 -490
- package/src/villagegen/villageArchetypes.js +0 -160
- package/src/villagegen/villageNames.js +0 -40
- package/src/villagegen/villageSites.js +0 -105
- package/src/weather/index.js +0 -6
- package/src/weather/weatherPrecipitation.js +0 -221
- package/src/weather/weatherPresets.js +0 -258
- package/src/weather/weatherSettings.js +0 -269
- package/src/weather/weatherSystem.js +0 -871
- package/src/worldMinimap.js +0 -214
- package/src/worldPresets.js +0 -125
|
@@ -0,0 +1,1114 @@
|
|
|
1
|
+
// Cloud scattering, as composable TSL nodes: Beer-Lambert extinction, Nubis'
|
|
2
|
+
// max-combined deep-light and silver-lining terms, the powder edge deficit,
|
|
3
|
+
// skylight and ground-bounce fill, the base darkening pair, and the moonlit rim.
|
|
4
|
+
//
|
|
5
|
+
// Sources, per docs/sky-cloud-parameters.md's runtime boundary — this is
|
|
6
|
+
// written from the published literature, not from the reference product:
|
|
7
|
+
//
|
|
8
|
+
// Schneider & Vos, "Nubis: Authoring Real-Time Volumetric Cloudscapes with
|
|
9
|
+
// the Decima Engine" (SIGGRAPH 2017), slides 80 and 85 — the two HG lobes
|
|
10
|
+
// and two Beer-Lambert curves are combined with max(), not summed.
|
|
11
|
+
// Schneider, "The Real-Time Volumetric Cloudscapes of Horizon Zero Dawn"
|
|
12
|
+
// (SIGGRAPH 2015) — the powder / dark-edge approximation and density field.
|
|
13
|
+
// Hillaire, "Physically Based and Unified Volumetric Rendering in Frostbite"
|
|
14
|
+
// (SIGGRAPH 2015) and "A Scalable and Production Ready Sky and Atmosphere
|
|
15
|
+
// Rendering Technique" (EGSR 2020) — the energy-conserving segment
|
|
16
|
+
// integration the marcher uses.
|
|
17
|
+
//
|
|
18
|
+
// Everything here is an expression builder: no `toVar`, no `Loop`, no stack
|
|
19
|
+
// requirement, so a headless test can build any single term on its own. The one
|
|
20
|
+
// exception is `createCloudLightingModel().inScatterNode`, which is a laid-out
|
|
21
|
+
// `Fn` so the 128 primary samples share one GPU function instead of inlining
|
|
22
|
+
// 128 copies of the whole model.
|
|
23
|
+
//
|
|
24
|
+
// Units. `extinction` is sigma_t in 1/m — `shape.density` times the normalized
|
|
25
|
+
// density field. Slab distances are metres. Optical depths are dimensionless.
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
clamp,
|
|
29
|
+
exp,
|
|
30
|
+
float,
|
|
31
|
+
Fn,
|
|
32
|
+
max,
|
|
33
|
+
mix,
|
|
34
|
+
saturate,
|
|
35
|
+
smoothstep,
|
|
36
|
+
sqrt,
|
|
37
|
+
vec3,
|
|
38
|
+
vec4,
|
|
39
|
+
} from 'three/tsl';
|
|
40
|
+
|
|
41
|
+
import { henyeyGreensteinPhaseNode } from '../sky/atmosphereScattering.js';
|
|
42
|
+
|
|
43
|
+
/** Dual-HG droplet phase constants. */
|
|
44
|
+
export const CLOUD_PHASE_FORWARD_G = 0.8;
|
|
45
|
+
export const CLOUD_PHASE_BACKWARD_G = -0.2;
|
|
46
|
+
export const CLOUD_PHASE_BLEND = 0.5;
|
|
47
|
+
|
|
48
|
+
/** Three-octave multiple-scattering falloffs. */
|
|
49
|
+
export const CLOUD_MULTISCATTER_EXTINCTION = 0.5;
|
|
50
|
+
export const CLOUD_MULTISCATTER_SCATTER = 0.5;
|
|
51
|
+
export const CLOUD_MULTISCATTER_ECCENTRICITY = 0.5;
|
|
52
|
+
|
|
53
|
+
// Backward-compatible diagnostics names. They now report the reference model,
|
|
54
|
+
// not the discarded max-combined Nubis approximation.
|
|
55
|
+
export const CLOUD_PHASE_BASE_G = CLOUD_PHASE_FORWARD_G;
|
|
56
|
+
export const CLOUD_PHASE_SILVER_INTENSITY = 0;
|
|
57
|
+
export const CLOUD_PHASE_SILVER_SPREAD = 0;
|
|
58
|
+
export const CLOUD_DEEP_EXTINCTION_SCALE = CLOUD_MULTISCATTER_EXTINCTION;
|
|
59
|
+
export const CLOUD_DEEP_CONTRIBUTION = CLOUD_MULTISCATTER_SCATTER;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Powder exponent. Nubis' approximation of the near-surface deficit of
|
|
63
|
+
* multiply-scattered light is `1 - exp(-2 d)`; this is that 2.
|
|
64
|
+
*/
|
|
65
|
+
export const CLOUD_POWDER_EXPONENT = 2;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Normalisation that turns `1 - exp(-2 x)` into a pure edge term.
|
|
69
|
+
*
|
|
70
|
+
* Nubis writes the expression against a density sample in its own units; over a
|
|
71
|
+
* field normalised to [0, 1] it tops out at `1 - exp(-2)` = 0.8647, so a solid
|
|
72
|
+
* core loses 13.5% of its direct light at `powderStrength` 1 and more above it.
|
|
73
|
+
* That is a global absorption, and `lighting.scatteringAlbedo` already owns
|
|
74
|
+
* global absorption — the parameter here documents only "darkens the thin outer
|
|
75
|
+
* edges". Dividing by the value at full occupancy keeps the shape of the fit and
|
|
76
|
+
* pins a fully surrounded sample to exactly 1 at every strength.
|
|
77
|
+
*/
|
|
78
|
+
/**
|
|
79
|
+
* Representative path, in metres, the moon rim term attenuates over.
|
|
80
|
+
*
|
|
81
|
+
* The rim is an edge term, so what it needs is "how thin is the cloud here",
|
|
82
|
+
* and the cheapest honest answer is Beer-Lambert over a fixed short path: a
|
|
83
|
+
* wisp at sigma 0.002 keeps three quarters of the moonlight, a core at 0.03
|
|
84
|
+
* keeps one percent. Marching toward the moon instead would double the light
|
|
85
|
+
* march for a term that is only ever a few percent of the image.
|
|
86
|
+
*/
|
|
87
|
+
export const CLOUD_MOON_RIM_PATH = 150;
|
|
88
|
+
|
|
89
|
+
/** Lambertian bounce normalisation, folded into the ground-bounce term. */
|
|
90
|
+
const INV_PI = 1 / Math.PI;
|
|
91
|
+
const CLOUD_LUMINANCE = vec3(0.2126, 0.7152, 0.0722);
|
|
92
|
+
|
|
93
|
+
// Laid-out function names have to be unique inside one shader, and a name is a
|
|
94
|
+
// GPU identifier rather than a label, so a second cloud material built in the
|
|
95
|
+
// same program cannot reuse this one's.
|
|
96
|
+
let lightingModelId = 0;
|
|
97
|
+
|
|
98
|
+
/** Beer-Lambert transmittance for an optical depth. */
|
|
99
|
+
export function beerLambertNode(opticalDepth) {
|
|
100
|
+
return exp(max(float(opticalDepth), 0).negate());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Nubis powder / dark-edge term: `1 - exp(-2 x)`, raised to `strength`.
|
|
105
|
+
*
|
|
106
|
+
* Beer-Lambert alone makes a thin sunlit edge the *brightest* part of a cloud,
|
|
107
|
+
* because little light was absorbed there — which is exactly backwards. In a
|
|
108
|
+
* real cloud a point near the surface has few neighbours to scatter light in
|
|
109
|
+
* from, so the multiply-scattered field is starved there and the edge reads
|
|
110
|
+
* darker than the body. This is the standard approximation of that deficit.
|
|
111
|
+
*
|
|
112
|
+
* `occupancy` is **how much cloud surrounds the sample**, in [0, 1]:
|
|
113
|
+
* `createCloudDensityField`'s third channel, which is the density field with the
|
|
114
|
+
* shell's own height envelope divided out. Both of the obvious alternatives are
|
|
115
|
+
* wrong in a way that shows up in the render:
|
|
116
|
+
*
|
|
117
|
+
* - The light-march optical depth makes a sunlit top black, because a top with
|
|
118
|
+
* clear sky above it has a light depth of zero and `1 - exp(0)` is zero.
|
|
119
|
+
* - The plain normalized density carries the cumulus taper
|
|
120
|
+
* (`saturate((1 - h) / 0.7)`) and the two edge fades, so it falls
|
|
121
|
+
* monotonically with height whatever the cloud is actually doing there. That
|
|
122
|
+
* turns the term into a sunlit-top darkening ramp — measured 0.865 at the
|
|
123
|
+
* h <= 0.3 plateau against 0.133 at h = 0.95, a 6.5x penalty on exactly the
|
|
124
|
+
* tops the spec wants brilliant — and it dims solid cores too, since the
|
|
125
|
+
* envelope caps the density they can reach.
|
|
126
|
+
*
|
|
127
|
+
* The envelope is the shell's vertical profile, not the local cloud boundary,
|
|
128
|
+
* so dividing it out leaves the quantity the term is defined on: the horizontal
|
|
129
|
+
* occupancy of the cloud field. A wisp is thin at any height and stays dark; a
|
|
130
|
+
* tower's crown is surrounded by cloud and is not darkened relative to its
|
|
131
|
+
* middle.
|
|
132
|
+
*
|
|
133
|
+
* `strength` is an exponent rather than a mix weight, so 0 removes the term
|
|
134
|
+
* exactly (`x^0 = 1`), 1 is the published expression, and larger values
|
|
135
|
+
* over-darken without ever leaving [0, 1] — and because the base is normalised to
|
|
136
|
+
* 1 at full occupancy, a solid core is untouched at *every* strength. A
|
|
137
|
+
* `mix(1, powder, strength)` would have clamped everything above 1 to the same
|
|
138
|
+
* image. The reference publishes a default of 1.0 and no range, so none is
|
|
139
|
+
* asserted here.
|
|
140
|
+
*/
|
|
141
|
+
export function powderNode(occupancy, strength) {
|
|
142
|
+
const local = saturate(float(occupancy));
|
|
143
|
+
const powder = exp(local.mul(-CLOUD_POWDER_EXPONENT)).oneMinus();
|
|
144
|
+
return mix(float(1), powder, max(float(strength), 0));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Nubis' max-combined phase function (2017 slide 80).
|
|
149
|
+
*
|
|
150
|
+
* The broad g=0.6 lobe keeps clouds readable away from the sun. A narrow
|
|
151
|
+
* g=(0.99 - spread) lobe supplies the silver lining, and max() prevents that
|
|
152
|
+
* highlight from adding energy over the whole body as a blend or sum would.
|
|
153
|
+
*/
|
|
154
|
+
export function cloudPhaseNode(cosTheta) {
|
|
155
|
+
return mix(
|
|
156
|
+
henyeyGreensteinPhaseNode(cosTheta, float(CLOUD_PHASE_FORWARD_G)),
|
|
157
|
+
henyeyGreensteinPhaseNode(cosTheta, float(CLOUD_PHASE_BACKWARD_G)),
|
|
158
|
+
float(CLOUD_PHASE_BLEND),
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Nubis' deep-light approximation (2017 slide 85), made view-dependent as the
|
|
164
|
+
* accompanying text specifies.
|
|
165
|
+
*
|
|
166
|
+
* Looking into the sun (cos=1) uses physical single scattering. Looking away
|
|
167
|
+
* (cos=-1) may use the dimmer, quarter-depth curve that stands in for light
|
|
168
|
+
* scattered deeper into the cloud. The linear angle ramp is the published code
|
|
169
|
+
* example also reproduced by the open Meteoros implementation.
|
|
170
|
+
*/
|
|
171
|
+
export function cloudDirectionalAttenuationNode({ cosTheta, lightOpticalDepth }) {
|
|
172
|
+
void cosTheta;
|
|
173
|
+
return beerLambertNode(max(float(lightOpticalDepth), 0));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Phase, deep-light visibility and powder deficit for the directional sun. */
|
|
177
|
+
export function cloudDirectionalScatteringNode({
|
|
178
|
+
cosTheta,
|
|
179
|
+
lightOpticalDepth,
|
|
180
|
+
occupancy,
|
|
181
|
+
powderStrength,
|
|
182
|
+
}) {
|
|
183
|
+
const depth = max(float(lightOpticalDepth), 0);
|
|
184
|
+
let eccentricity = 1;
|
|
185
|
+
let extinction = 1;
|
|
186
|
+
let scatter = 1;
|
|
187
|
+
let energy = float(0);
|
|
188
|
+
for (let octave = 0; octave < 3; octave += 1) {
|
|
189
|
+
const phase = mix(
|
|
190
|
+
henyeyGreensteinPhaseNode(
|
|
191
|
+
cosTheta,
|
|
192
|
+
float(CLOUD_PHASE_FORWARD_G * eccentricity),
|
|
193
|
+
),
|
|
194
|
+
henyeyGreensteinPhaseNode(
|
|
195
|
+
cosTheta,
|
|
196
|
+
float(CLOUD_PHASE_BACKWARD_G * eccentricity),
|
|
197
|
+
),
|
|
198
|
+
float(CLOUD_PHASE_BLEND),
|
|
199
|
+
);
|
|
200
|
+
energy = energy.add(
|
|
201
|
+
exp(depth.mul(-extinction)).mul(scatter).mul(phase),
|
|
202
|
+
);
|
|
203
|
+
eccentricity *= CLOUD_MULTISCATTER_ECCENTRICITY;
|
|
204
|
+
extinction *= CLOUD_MULTISCATTER_EXTINCTION;
|
|
205
|
+
scatter *= CLOUD_MULTISCATTER_SCATTER;
|
|
206
|
+
}
|
|
207
|
+
return energy.mul(powderNode(occupancy, powderStrength));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Direct-beam transmittance for cloud shadows.
|
|
212
|
+
*
|
|
213
|
+
* Multiple scattering is radiance arriving from other directions; it fills the
|
|
214
|
+
* cloud image but cannot make the unobstructed solar beam reappear on a ground
|
|
215
|
+
* receiver. Shadow transmission is therefore plain Beer-Lambert.
|
|
216
|
+
*/
|
|
217
|
+
export function cloudSunTransmittanceNode({ lightOpticalDepth }) {
|
|
218
|
+
return beerLambertNode(lightOpticalDepth);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Fraction of the sphere around a cloud sample that the ground fills.
|
|
223
|
+
*
|
|
224
|
+
* A sample at cloud altitude sees the ground below its own horizon, which is half
|
|
225
|
+
* the sphere to within the horizon dip (1.7 degrees at 2.8 km). The same one-half
|
|
226
|
+
* is implicit in the multiple-scattering bake, whose ground term fires on the
|
|
227
|
+
* directions that intersect the planet and is then averaged over the whole
|
|
228
|
+
* sphere. Using it here is what makes the two ground estimates cancel below,
|
|
229
|
+
* rather than a fitted number.
|
|
230
|
+
*/
|
|
231
|
+
export const CLOUD_GROUND_HEMISPHERE_FRACTION = 0.5;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Radiance arriving at a cloud sample from the lit ground, before the cloud
|
|
235
|
+
* below it attenuates anything: a Lambertian re-emission of the direct beam that
|
|
236
|
+
* reached the ground, over the half-sphere the ground fills.
|
|
237
|
+
*
|
|
238
|
+
* This is the same construction Hillaire's multiple-scattering bake uses for its
|
|
239
|
+
* own ground term, which is what lets it serve two purposes — estimating the
|
|
240
|
+
* ground light the table's tap already contains, and adding the cloud's own.
|
|
241
|
+
* Checked against a re-bake with `groundAlbedo` zeroed: the estimate lands within
|
|
242
|
+
* 5% of the measured ground share of the tap at every sun angle (1.049 / 1.008 /
|
|
243
|
+
* 0.937 of it in R/G/B at zenith, 1.045 / 0.997 / 0.908 at 10 degrees).
|
|
244
|
+
*
|
|
245
|
+
* `groundIrradiance` is the *direct* beam at the ground and nothing else. The
|
|
246
|
+
* sky-lit part of the ground is already inside the table's own
|
|
247
|
+
* multiple-scattering boost.
|
|
248
|
+
*/
|
|
249
|
+
export function cloudGroundRadianceNode({ groundIrradiance, albedo }) {
|
|
250
|
+
return albedo.mul(groundIrradiance).mul(INV_PI * CLOUD_GROUND_HEMISPHERE_FRACTION);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Skylight fill at a sample: the sky's share of the ambient bath, attenuated by
|
|
255
|
+
* the cloud above it, scaled by `lighting.ambientIntensity`.
|
|
256
|
+
*
|
|
257
|
+
* `skyRadiance` is the multiple-scattering table's tap, and that tap is a
|
|
258
|
+
* **sphere average** — the bake integrates every direction, including the ones
|
|
259
|
+
* that end on the lit ground, so it already carries a ground bounce taken at
|
|
260
|
+
* `atmosphere.groundAlbedo`. Measured at 2.8 km with the sun at zenith, 63% /
|
|
261
|
+
* 51% / 33% of the R/G/B of the tap *is* that ground bounce.
|
|
262
|
+
*
|
|
263
|
+
* `groundInTap` is that share, and subtracting it is what leaves this term the
|
|
264
|
+
* sky alone. Three things follow, all of which the reference's own docs require:
|
|
265
|
+
* `atmosphere.groundAlbedo` cancels out of the cloud — it is documented "sky dome
|
|
266
|
+
* only", and the fill now moves by at most 2.2% when it is driven from 0.18 to 0,
|
|
267
|
+
* against 14% before; `lighting.ambientIntensity`, documented as the *skylight*
|
|
268
|
+
* fill, no longer dials the ground; and at `ambientIntensity` 1 the two terms sum
|
|
269
|
+
* back to exactly the tap, so the bath is neither gained nor lost by the split.
|
|
270
|
+
*
|
|
271
|
+
* Checked against a ground-free re-bake of the same table: the remainder is
|
|
272
|
+
* 0.916 / 0.991 / 1.031 of it in R/G/B with the sun at zenith, and
|
|
273
|
+
* 0.990 / 1.000 / 1.004 at 10 degrees.
|
|
274
|
+
*/
|
|
275
|
+
export function cloudAmbientNode({
|
|
276
|
+
skyRadiance,
|
|
277
|
+
groundInTap,
|
|
278
|
+
ambientIntensity,
|
|
279
|
+
multipleScattering = 0,
|
|
280
|
+
extinction,
|
|
281
|
+
slabAbove,
|
|
282
|
+
}) {
|
|
283
|
+
// This is an already integrated hemispherical bath. Treating the local
|
|
284
|
+
// extinction as constant through the whole remaining shell double-counts
|
|
285
|
+
// self-shadow and makes a dense core black; the directional light march owns
|
|
286
|
+
// cloud self-shadow until there is a real ambient-direction march.
|
|
287
|
+
void extinction;
|
|
288
|
+
void slabAbove;
|
|
289
|
+
void multipleScattering;
|
|
290
|
+
const skyOnly = max(skyRadiance.sub(groundInTap), vec3(0));
|
|
291
|
+
return skyOnly.mul(max(float(ambientIntensity), 0));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Ground bounce on a cloud underside: the ground's radiance at
|
|
296
|
+
* `lighting.groundBounceAlbedo`, attenuated by the cloud below the sample.
|
|
297
|
+
*
|
|
298
|
+
* The ground now enters the fill exactly once — here. `cloudAmbientNode` has
|
|
299
|
+
* already taken the table's own copy back out, so `groundBounceAlbedo` is the
|
|
300
|
+
* only albedo that decides how much light a cloud underside receives, which is
|
|
301
|
+
* what the parameter documents. It also means the two halves of the bath are
|
|
302
|
+
* attenuated along the paths they actually travel: sky light through the slab
|
|
303
|
+
* above the sample, ground light through the slab below it. Before, the whole
|
|
304
|
+
* sphere average rode the slab above and a second full bounce rode the slab
|
|
305
|
+
* below, which counted the ground twice and — because ground light is the
|
|
306
|
+
* spectrally flat term — flattened the fill toward grey (B/R 0.84 against a
|
|
307
|
+
* measured 1.04 for the true bath; 1.21 after).
|
|
308
|
+
*/
|
|
309
|
+
export function cloudGroundBounceNode({
|
|
310
|
+
groundIrradiance,
|
|
311
|
+
groundBounceAlbedo,
|
|
312
|
+
extinction,
|
|
313
|
+
slabBelow,
|
|
314
|
+
}) {
|
|
315
|
+
// As above, a single local extinction value is not a valid integral through
|
|
316
|
+
// the heterogeneous volume. The sun light march supplies the directional
|
|
317
|
+
// depth; keep the low-frequency bounce as a fill rather than extinguishing it
|
|
318
|
+
// through a fictitious solid slab.
|
|
319
|
+
void extinction;
|
|
320
|
+
void slabBelow;
|
|
321
|
+
return cloudGroundRadianceNode({ albedo: groundBounceAlbedo, groundIrradiance });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* The base-darkening pair as a multiplier on the lit terms.
|
|
326
|
+
*
|
|
327
|
+
* 1 above `baseShadowHeight`, falling to `1 - baseShadowStrength` at the floor
|
|
328
|
+
* of the shell. `baseShadowStrength` 1 therefore shades the bottom to black,
|
|
329
|
+
* which is what "shades them all the way down to the floor of the shell" means,
|
|
330
|
+
* and a larger height spreads the same amount of shading over more of the cloud
|
|
331
|
+
* so it reads softer.
|
|
332
|
+
*/
|
|
333
|
+
export function cloudBaseShadowNode({ heightFraction, baseShadowStrength, baseShadowHeight }) {
|
|
334
|
+
const verticalProbability = mix(
|
|
335
|
+
float(0.15),
|
|
336
|
+
float(1),
|
|
337
|
+
smoothstep(
|
|
338
|
+
float(0),
|
|
339
|
+
max(float(baseShadowHeight), 1e-3),
|
|
340
|
+
saturate(float(heightFraction)),
|
|
341
|
+
),
|
|
342
|
+
);
|
|
343
|
+
return mix(float(1), verticalProbability, saturate(float(baseShadowStrength)));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** Height-varying sky and ground ambient fill. */
|
|
347
|
+
export function cloudSkyAmbientNode({
|
|
348
|
+
heightFraction,
|
|
349
|
+
zenithRadiance,
|
|
350
|
+
horizonRadiance,
|
|
351
|
+
groundBounceRadiance,
|
|
352
|
+
ambientIntensity,
|
|
353
|
+
}) {
|
|
354
|
+
const height = saturate(float(heightFraction));
|
|
355
|
+
const baseSky = mix(zenithRadiance, horizonRadiance, float(0.35));
|
|
356
|
+
const skyAmbient = mix(baseSky, zenithRadiance, sqrt(height));
|
|
357
|
+
const ground = groundBounceRadiance.mul(height.oneMinus());
|
|
358
|
+
return skyAmbient.add(ground).mul(max(float(ambientIntensity), 0));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Moonlight on cloud edges.
|
|
363
|
+
*
|
|
364
|
+
* Scaled by `lighting.moonGain`, by the moon's lit fraction
|
|
365
|
+
* (`moonPhaseIllumination` — a sliver must not light a sky like a full moon),
|
|
366
|
+
* and by how night it is, so the term is gone by day. `moonRadiance` carries
|
|
367
|
+
* `moonColor * moonIntensity`.
|
|
368
|
+
*/
|
|
369
|
+
export function cloudMoonRimNode({
|
|
370
|
+
cosTheta,
|
|
371
|
+
extinction,
|
|
372
|
+
moonRadiance,
|
|
373
|
+
moonGain,
|
|
374
|
+
moonIllumination,
|
|
375
|
+
nightFactor,
|
|
376
|
+
path = CLOUD_MOON_RIM_PATH,
|
|
377
|
+
}) {
|
|
378
|
+
const rim = beerLambertNode(max(float(extinction), 0).mul(path));
|
|
379
|
+
return moonRadiance
|
|
380
|
+
.mul(cloudPhaseNode(cosTheta))
|
|
381
|
+
.mul(rim)
|
|
382
|
+
.mul(max(float(moonGain), 0))
|
|
383
|
+
.mul(saturate(float(moonIllumination)))
|
|
384
|
+
.mul(saturate(float(nightFactor)));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Directional moonlight through the same cloud depth model as sunlight.
|
|
389
|
+
*
|
|
390
|
+
* The old night path only supplied a local edge rim, so the moon could outline
|
|
391
|
+
* a cloud but could not reveal lobes or self-shadowing inside it. This term is
|
|
392
|
+
* fed by a moon-facing light march and is multiplied by `nightFactor`; the
|
|
393
|
+
* caller skips that second light march entirely while the sky is not dark.
|
|
394
|
+
*/
|
|
395
|
+
export function cloudMoonDirectionalNode({
|
|
396
|
+
cosTheta,
|
|
397
|
+
lightOpticalDepth,
|
|
398
|
+
moonRadiance,
|
|
399
|
+
moonGain,
|
|
400
|
+
moonIllumination,
|
|
401
|
+
nightFactor,
|
|
402
|
+
occupancy,
|
|
403
|
+
powderStrength,
|
|
404
|
+
}) {
|
|
405
|
+
return moonRadiance
|
|
406
|
+
.mul(cloudDirectionalScatteringNode({
|
|
407
|
+
cosTheta,
|
|
408
|
+
lightOpticalDepth,
|
|
409
|
+
occupancy,
|
|
410
|
+
powderStrength,
|
|
411
|
+
}))
|
|
412
|
+
.mul(max(float(moonGain), 0))
|
|
413
|
+
.mul(saturate(float(moonIllumination)))
|
|
414
|
+
.mul(saturate(float(nightFactor)));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Optional three-tone remap over physically calculated cloud radiance.
|
|
419
|
+
*
|
|
420
|
+
* The physical luminance selects the authored shadow, body and highlight
|
|
421
|
+
* colours. It is compressed into 0..1 only for choosing a band; output remains
|
|
422
|
+
* linear HDR radiance, so atmosphere, exposure and bloom still operate in the
|
|
423
|
+
* same space as V1. Both switches are uniforms. A disabled master or module
|
|
424
|
+
* blends by exactly zero and leaves the physical value unchanged.
|
|
425
|
+
*/
|
|
426
|
+
export function applyCloudToneNode(radiance, style = null) {
|
|
427
|
+
const physical = max(vec3(radiance), vec3(0));
|
|
428
|
+
if (!style?.enabled || !style?.tone?.enabled) return physical;
|
|
429
|
+
|
|
430
|
+
const tone = style.tone;
|
|
431
|
+
const luminance = max(physical.dot(CLOUD_LUMINANCE), 0).toVar();
|
|
432
|
+
const signal = luminance.div(luminance.add(1)).toVar();
|
|
433
|
+
const width = max(tone.softness, 1e-4).toVar();
|
|
434
|
+
const shadowToMid = smoothstep(
|
|
435
|
+
tone.shadowPoint.sub(width),
|
|
436
|
+
tone.shadowPoint.add(width),
|
|
437
|
+
signal,
|
|
438
|
+
).toVar();
|
|
439
|
+
const midToLight = smoothstep(
|
|
440
|
+
tone.lightPoint.sub(width),
|
|
441
|
+
tone.lightPoint.add(width),
|
|
442
|
+
signal,
|
|
443
|
+
).toVar();
|
|
444
|
+
const palette = mix(
|
|
445
|
+
mix(tone.shadowColor, tone.midColor, shadowToMid),
|
|
446
|
+
tone.lightColor,
|
|
447
|
+
midToLight,
|
|
448
|
+
).toVar();
|
|
449
|
+
const paletteLuminance = max(palette.dot(CLOUD_LUMINANCE), 1e-4).toVar();
|
|
450
|
+
const compressed = max(luminance, tone.shadowLift)
|
|
451
|
+
.div(float(1).add(luminance.mul(max(tone.highlightCompression, 0))))
|
|
452
|
+
.mul(max(tone.brightness, 0))
|
|
453
|
+
.toVar();
|
|
454
|
+
const styled = palette.mul(compressed.div(paletteLuminance)).toVar();
|
|
455
|
+
const amount = saturate(style.enabled.mul(tone.enabled).mul(style.amount));
|
|
456
|
+
return mix(physical, styled, amount);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Adds the cool skylight art direction to shadowed cloud regions only.
|
|
461
|
+
*
|
|
462
|
+
* The physical pre-tone luminance selects the shadow mask. The authored colour
|
|
463
|
+
* is normalized back to the already-styled luminance before blending, so this
|
|
464
|
+
* module changes hue without flattening the light march or lifting the back of
|
|
465
|
+
* every cloud to a constant colour.
|
|
466
|
+
*/
|
|
467
|
+
export function applyCloudBlueShadowNode(radiance, physicalRadiance, style = null) {
|
|
468
|
+
const styled = max(vec3(radiance), vec3(0));
|
|
469
|
+
if (!style?.enabled || !style?.blueShadow?.enabled) return styled;
|
|
470
|
+
|
|
471
|
+
const source = max(vec3(physicalRadiance), vec3(0));
|
|
472
|
+
const blueShadow = style.blueShadow;
|
|
473
|
+
const sourceLuminance = max(source.dot(CLOUD_LUMINANCE), 0).toVar();
|
|
474
|
+
const signal = sourceLuminance.div(sourceLuminance.add(1)).toVar();
|
|
475
|
+
const width = max(blueShadow.softness, 1e-4).toVar();
|
|
476
|
+
const shadowMask = smoothstep(
|
|
477
|
+
blueShadow.range.sub(width),
|
|
478
|
+
blueShadow.range.add(width),
|
|
479
|
+
signal,
|
|
480
|
+
).oneMinus().toVar();
|
|
481
|
+
const styledLuminance = max(styled.dot(CLOUD_LUMINANCE), 0).toVar();
|
|
482
|
+
const blueLuminance = max(blueShadow.color.dot(CLOUD_LUMINANCE), 1e-4).toVar();
|
|
483
|
+
const tinted = blueShadow.color.mul(styledLuminance.div(blueLuminance)).toVar();
|
|
484
|
+
const amount = saturate(
|
|
485
|
+
style.enabled
|
|
486
|
+
.mul(blueShadow.enabled)
|
|
487
|
+
.mul(style.amount)
|
|
488
|
+
.mul(blueShadow.amount)
|
|
489
|
+
.mul(shadowMask),
|
|
490
|
+
);
|
|
491
|
+
return mix(styled, tinted, amount);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Applies the per-sample daytime style modules in a stable, documented order.
|
|
496
|
+
*
|
|
497
|
+
* `daylightAmount` lets the shared clock remove the daytime remap before the
|
|
498
|
+
* night palette is applied. Full night then starts from the physically
|
|
499
|
+
* moonlit, self-shadowed volume instead of a blue daytime tone ramp. A value of
|
|
500
|
+
* one preserves the established V2.6 result exactly.
|
|
501
|
+
*/
|
|
502
|
+
export function applyCloudStyleNode(radiance, style = null, daylightAmount = 1) {
|
|
503
|
+
const physical = max(vec3(radiance), vec3(0));
|
|
504
|
+
const toned = applyCloudToneNode(physical, style);
|
|
505
|
+
const styled = applyCloudBlueShadowNode(toned, physical, style);
|
|
506
|
+
return mix(physical, styled, saturate(float(daylightAmount)));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Turns the accumulated cloud underside into a pale painted wash.
|
|
511
|
+
*
|
|
512
|
+
* This runs once after the volume march rather than once per density sample.
|
|
513
|
+
* Working on the resolved cloud colour lets `detail` compress small lighting
|
|
514
|
+
* variations across the whole visible body. The blue-shadow range selects the
|
|
515
|
+
* same underside region, but this module has its own switch and still works
|
|
516
|
+
* when the blue hue module is bypassed.
|
|
517
|
+
*/
|
|
518
|
+
export function applyCloudShadowWashNode(premultipliedRadiance, alpha, style = null) {
|
|
519
|
+
const original = max(vec3(premultipliedRadiance), vec3(0));
|
|
520
|
+
if (!style?.enabled || !style?.shadowWash?.enabled) return original;
|
|
521
|
+
|
|
522
|
+
const wash = style.shadowWash;
|
|
523
|
+
const safeAlpha = max(float(alpha), 1e-4).toVar();
|
|
524
|
+
const body = original.div(safeAlpha).toVar();
|
|
525
|
+
const luminance = max(body.dot(CLOUD_LUMINANCE), 0).toVar();
|
|
526
|
+
const signal = luminance.div(luminance.add(1)).toVar();
|
|
527
|
+
const range = style?.blueShadow?.range ?? float(0.38);
|
|
528
|
+
const width = max(wash.blend, 1e-4).toVar();
|
|
529
|
+
const shadowMask = smoothstep(
|
|
530
|
+
range.sub(width),
|
|
531
|
+
range.add(width),
|
|
532
|
+
signal,
|
|
533
|
+
).oneMinus().toVar();
|
|
534
|
+
const washedLuminance = mix(
|
|
535
|
+
max(wash.lift, 0),
|
|
536
|
+
luminance,
|
|
537
|
+
saturate(wash.detail),
|
|
538
|
+
).toVar();
|
|
539
|
+
const washedBody = body
|
|
540
|
+
.mul(washedLuminance.div(max(luminance, 1e-4)))
|
|
541
|
+
.toVar();
|
|
542
|
+
const amount = saturate(
|
|
543
|
+
style.enabled
|
|
544
|
+
.mul(wash.enabled)
|
|
545
|
+
.mul(style.amount)
|
|
546
|
+
.mul(shadowMask),
|
|
547
|
+
);
|
|
548
|
+
return mix(original, washedBody.mul(safeAlpha), amount);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Keeps the physical cloud edge while allowing a painted interior.
|
|
553
|
+
*
|
|
554
|
+
* Cloud opacity remains entirely owned by the volume march. This function only
|
|
555
|
+
* blends between the resolved physical colour and the shadow-washed colour,
|
|
556
|
+
* using resolved opacity as an interior mask. Thin wisps and the outer contour
|
|
557
|
+
* therefore retain their physical shading and silhouette.
|
|
558
|
+
*/
|
|
559
|
+
export function applyCloudInnerPaintNode(
|
|
560
|
+
premultipliedRadiance,
|
|
561
|
+
alpha,
|
|
562
|
+
style = null,
|
|
563
|
+
daylightAmount = 1,
|
|
564
|
+
) {
|
|
565
|
+
const original = max(vec3(premultipliedRadiance), vec3(0));
|
|
566
|
+
const painted = applyCloudShadowWashNode(original, alpha, style);
|
|
567
|
+
if (!style?.enabled || !style?.innerPaint?.enabled) return painted;
|
|
568
|
+
|
|
569
|
+
const inner = style.innerPaint;
|
|
570
|
+
const edgeStart = saturate(inner.edgeKeep).toVar();
|
|
571
|
+
const edgeWidth = max(inner.edgeBlend, 1e-4).toVar();
|
|
572
|
+
const interiorMask = smoothstep(
|
|
573
|
+
edgeStart,
|
|
574
|
+
edgeStart.add(edgeWidth),
|
|
575
|
+
saturate(float(alpha)),
|
|
576
|
+
).toVar();
|
|
577
|
+
const amount = saturate(
|
|
578
|
+
style.enabled
|
|
579
|
+
.mul(inner.enabled)
|
|
580
|
+
.mul(inner.amount)
|
|
581
|
+
.mul(saturate(float(daylightAmount)))
|
|
582
|
+
.mul(interiorMask),
|
|
583
|
+
);
|
|
584
|
+
return mix(original, painted, amount);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Broadens the clean white region across sun-reachable upper cloud samples.
|
|
589
|
+
*
|
|
590
|
+
* `sunlitHeight` is accumulated by the physical volume march from the existing
|
|
591
|
+
* height fraction and light optical depth. It changes no density or opacity.
|
|
592
|
+
* The final blend reuses the V2.4 resolved-opacity boundary so the physical
|
|
593
|
+
* contour and thin wisps remain untouched while only the opaque interior is
|
|
594
|
+
* simplified toward the authored warm white.
|
|
595
|
+
*/
|
|
596
|
+
export function applyCloudWhiteTopNode(
|
|
597
|
+
premultipliedRadiance,
|
|
598
|
+
alpha,
|
|
599
|
+
sunlitHeight,
|
|
600
|
+
style = null,
|
|
601
|
+
) {
|
|
602
|
+
const original = max(vec3(premultipliedRadiance), vec3(0));
|
|
603
|
+
if (!style?.enabled || !style?.whiteTop?.enabled) return original;
|
|
604
|
+
|
|
605
|
+
const white = style.whiteTop;
|
|
606
|
+
const safeAlpha = max(float(alpha), 1e-4).toVar();
|
|
607
|
+
const body = original.div(safeAlpha).toVar();
|
|
608
|
+
const start = saturate(white.area).oneMinus().toVar();
|
|
609
|
+
const width = max(white.softness, 1e-4).toVar();
|
|
610
|
+
const topMask = smoothstep(
|
|
611
|
+
start.sub(width),
|
|
612
|
+
start.add(width),
|
|
613
|
+
saturate(float(sunlitHeight)),
|
|
614
|
+
).toVar();
|
|
615
|
+
|
|
616
|
+
const edgeStart = saturate(style.innerPaint.edgeKeep).toVar();
|
|
617
|
+
const edgeWidth = max(style.innerPaint.edgeBlend, 1e-4).toVar();
|
|
618
|
+
const interiorMask = smoothstep(
|
|
619
|
+
edgeStart,
|
|
620
|
+
edgeStart.add(edgeWidth),
|
|
621
|
+
saturate(float(alpha)),
|
|
622
|
+
).toVar();
|
|
623
|
+
|
|
624
|
+
const bodyLuminance = max(body.dot(CLOUD_LUMINANCE), 0).toVar();
|
|
625
|
+
const whiteLuminance = max(white.color.dot(CLOUD_LUMINANCE), 1e-4).toVar();
|
|
626
|
+
const paintedLuminance = mix(
|
|
627
|
+
whiteLuminance,
|
|
628
|
+
bodyLuminance,
|
|
629
|
+
saturate(white.detail),
|
|
630
|
+
).toVar();
|
|
631
|
+
const paintedBody = white.color
|
|
632
|
+
.mul(paintedLuminance.div(whiteLuminance))
|
|
633
|
+
.toVar();
|
|
634
|
+
const amount = saturate(
|
|
635
|
+
style.enabled
|
|
636
|
+
.mul(white.enabled)
|
|
637
|
+
.mul(style.amount)
|
|
638
|
+
.mul(white.amount)
|
|
639
|
+
.mul(topMask)
|
|
640
|
+
.mul(interiorMask),
|
|
641
|
+
);
|
|
642
|
+
return mix(original, paintedBody.mul(safeAlpha), amount);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Restores directional sunlight value variation after the white-top paint.
|
|
647
|
+
*
|
|
648
|
+
* White Top deliberately simplifies the upper cloud into one authored hue.
|
|
649
|
+
* This stage uses both the physical in-scatter luminance and Beer-Lambert sun
|
|
650
|
+
* reach from the existing light march to shade that hue. An aerial view then
|
|
651
|
+
* recovers sun-facing crowns and shadowed folds without changing density,
|
|
652
|
+
* opacity, or the approved top colour. The fixed value range keeps the
|
|
653
|
+
* authored white top bright while retaining the light march's local variation.
|
|
654
|
+
*/
|
|
655
|
+
export function applyCloudTopLightNode(
|
|
656
|
+
premultipliedRadiance,
|
|
657
|
+
physicalLight,
|
|
658
|
+
sunlight,
|
|
659
|
+
alpha,
|
|
660
|
+
sunlitHeight,
|
|
661
|
+
daylight,
|
|
662
|
+
surfaceLightAmount = 0,
|
|
663
|
+
surfaceSunFacing = 0.5,
|
|
664
|
+
style = null,
|
|
665
|
+
) {
|
|
666
|
+
const original = max(vec3(premultipliedRadiance), vec3(0));
|
|
667
|
+
if (!style?.enabled || !style?.topLight?.enabled) return original;
|
|
668
|
+
|
|
669
|
+
const safeAlpha = max(float(alpha), 1e-4).toVar();
|
|
670
|
+
const body = original.div(safeAlpha).toVar();
|
|
671
|
+
const physicalSignal = smoothstep(
|
|
672
|
+
0.08,
|
|
673
|
+
0.9,
|
|
674
|
+
max(float(physicalLight), 0),
|
|
675
|
+
).toVar();
|
|
676
|
+
const lightSignal = sqrt(saturate(float(sunlight))).mul(mix(
|
|
677
|
+
0.4,
|
|
678
|
+
1,
|
|
679
|
+
physicalSignal,
|
|
680
|
+
)).toVar();
|
|
681
|
+
const exteriorAmount = saturate(float(surfaceLightAmount)).toVar();
|
|
682
|
+
const exteriorSignal = smoothstep(
|
|
683
|
+
0.15,
|
|
684
|
+
0.9,
|
|
685
|
+
saturate(float(surfaceSunFacing)),
|
|
686
|
+
).mul(mix(0.7, 1, sqrt(saturate(float(sunlight))))).toVar();
|
|
687
|
+
const resolvedSignal = mix(lightSignal, exteriorSignal, exteriorAmount).toVar();
|
|
688
|
+
const shadowValue = mix(0.65, 0.55, exteriorAmount).toVar();
|
|
689
|
+
// Exterior shaping is carried by the shaded turn, not clipped pin-light
|
|
690
|
+
// highlights on distant wisps. Keep the bright end close to the V2.8 white.
|
|
691
|
+
const highlightValue = mix(1.25, 1.3, exteriorAmount).toVar();
|
|
692
|
+
const lightRatio = mix(shadowValue, highlightValue, resolvedSignal).toVar();
|
|
693
|
+
const relitBody = body.mul(lightRatio).toVar();
|
|
694
|
+
|
|
695
|
+
const white = style.whiteTop;
|
|
696
|
+
const start = saturate(white.area).oneMinus().toVar();
|
|
697
|
+
const width = max(white.softness, 1e-4).toVar();
|
|
698
|
+
const topMask = smoothstep(
|
|
699
|
+
start.sub(width),
|
|
700
|
+
start.add(width),
|
|
701
|
+
saturate(float(sunlitHeight)),
|
|
702
|
+
).toVar();
|
|
703
|
+
const edgeStart = saturate(style.innerPaint.edgeKeep).toVar();
|
|
704
|
+
const edgeWidth = max(style.innerPaint.edgeBlend, 1e-4).toVar();
|
|
705
|
+
const interiorMask = smoothstep(
|
|
706
|
+
edgeStart,
|
|
707
|
+
edgeStart.add(edgeWidth),
|
|
708
|
+
saturate(float(alpha)),
|
|
709
|
+
).toVar();
|
|
710
|
+
const amount = saturate(
|
|
711
|
+
style.enabled
|
|
712
|
+
.mul(style.topLight.enabled)
|
|
713
|
+
.mul(style.amount)
|
|
714
|
+
.mul(style.topLight.amount)
|
|
715
|
+
.mul(saturate(float(daylight)))
|
|
716
|
+
.mul(topMask)
|
|
717
|
+
.mul(interiorMask),
|
|
718
|
+
);
|
|
719
|
+
return mix(original, relitBody.mul(safeAlpha), amount);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Adds a restrained cool tint to the shadow and middle regions while leaving
|
|
724
|
+
* the existing warm white top intact.
|
|
725
|
+
*
|
|
726
|
+
* The physical march still owns opacity and the outer contour. This final
|
|
727
|
+
* colour stage reuses the same sunlit-height descriptor, White Top boundary,
|
|
728
|
+
* and V2.4 interior mask. It therefore changes only the readable lower colour
|
|
729
|
+
* regions inside opaque daytime clouds.
|
|
730
|
+
*/
|
|
731
|
+
export function applyCloudLightBlendNode(
|
|
732
|
+
premultipliedRadiance,
|
|
733
|
+
alpha,
|
|
734
|
+
sunlitHeight,
|
|
735
|
+
daylight,
|
|
736
|
+
style = null,
|
|
737
|
+
) {
|
|
738
|
+
const original = max(vec3(premultipliedRadiance), vec3(0));
|
|
739
|
+
if (!style?.enabled || !style?.lightBlend?.enabled) return original;
|
|
740
|
+
|
|
741
|
+
const blend = style.lightBlend;
|
|
742
|
+
const safeAlpha = max(float(alpha), 1e-4).toVar();
|
|
743
|
+
const body = original.div(safeAlpha).toVar();
|
|
744
|
+
const daylightAmount = saturate(float(daylight)).toVar();
|
|
745
|
+
const signal = saturate(float(sunlitHeight).mul(daylightAmount)).toVar();
|
|
746
|
+
const center = saturate(blend.balance).toVar();
|
|
747
|
+
const width = max(blend.softness, 1e-4).toVar();
|
|
748
|
+
const bottomToMiddle = smoothstep(
|
|
749
|
+
center.sub(width),
|
|
750
|
+
center.add(width),
|
|
751
|
+
signal,
|
|
752
|
+
).toVar();
|
|
753
|
+
|
|
754
|
+
// Match the V2.5 selector exactly. Its bright upper region passes through
|
|
755
|
+
// this stage instead of being repainted by a second palette.
|
|
756
|
+
const topStart = saturate(style.whiteTop.area).oneMinus().toVar();
|
|
757
|
+
const topWidth = max(style.whiteTop.softness, 1e-4).toVar();
|
|
758
|
+
const keepWhiteTop = smoothstep(
|
|
759
|
+
topStart.sub(topWidth),
|
|
760
|
+
topStart.add(topWidth),
|
|
761
|
+
signal,
|
|
762
|
+
).toVar();
|
|
763
|
+
const palette = mix(
|
|
764
|
+
blend.bottomColor,
|
|
765
|
+
blend.middleColor,
|
|
766
|
+
bottomToMiddle,
|
|
767
|
+
).toVar();
|
|
768
|
+
|
|
769
|
+
const edgeStart = saturate(style.innerPaint.edgeKeep).toVar();
|
|
770
|
+
const edgeWidth = max(style.innerPaint.edgeBlend, 1e-4).toVar();
|
|
771
|
+
const interiorMask = smoothstep(
|
|
772
|
+
edgeStart,
|
|
773
|
+
edgeStart.add(edgeWidth),
|
|
774
|
+
saturate(float(alpha)),
|
|
775
|
+
).toVar();
|
|
776
|
+
|
|
777
|
+
const bodyLuminance = max(body.dot(CLOUD_LUMINANCE), 0).toVar();
|
|
778
|
+
const paletteLuminance = max(palette.dot(CLOUD_LUMINANCE), 1e-4).toVar();
|
|
779
|
+
const tintedBody = palette.mul(bodyLuminance.div(paletteLuminance)).toVar();
|
|
780
|
+
const paintedBody = mix(
|
|
781
|
+
tintedBody,
|
|
782
|
+
body,
|
|
783
|
+
saturate(blend.detail),
|
|
784
|
+
).toVar();
|
|
785
|
+
const amount = saturate(
|
|
786
|
+
style.enabled
|
|
787
|
+
.mul(blend.enabled)
|
|
788
|
+
.mul(style.amount)
|
|
789
|
+
.mul(blend.amount)
|
|
790
|
+
.mul(daylightAmount)
|
|
791
|
+
.mul(interiorMask)
|
|
792
|
+
.mul(keepWhiteTop.oneMinus()),
|
|
793
|
+
);
|
|
794
|
+
return mix(original, paintedBody.mul(safeAlpha), amount);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Tints the resolved cloud interior in the morning, evening, and night from the
|
|
799
|
+
* shared sky clock. Afternoon receives an exact zero blend, so V2.6 remains
|
|
800
|
+
* the day result rather than becoming another palette input.
|
|
801
|
+
*
|
|
802
|
+
* The physical march still owns opacity and the outer contour. Density and
|
|
803
|
+
* alpha remain untouched. The V2.4 interior mask only softens the
|
|
804
|
+
* colour grade slightly at the contour; time colour must still reach wisps so
|
|
805
|
+
* they cannot remain daytime cyan or white after the clock changes.
|
|
806
|
+
*/
|
|
807
|
+
export function applyCloudTimePaletteNode(
|
|
808
|
+
premultipliedRadiance,
|
|
809
|
+
alpha,
|
|
810
|
+
sunlitHeight,
|
|
811
|
+
morningLight,
|
|
812
|
+
eveningLight,
|
|
813
|
+
nightness,
|
|
814
|
+
style = null,
|
|
815
|
+
) {
|
|
816
|
+
const original = max(vec3(premultipliedRadiance), vec3(0));
|
|
817
|
+
if (!style?.enabled || !style?.timePalette?.enabled) return original;
|
|
818
|
+
|
|
819
|
+
const timePalette = style.timePalette;
|
|
820
|
+
const safeAlpha = max(float(alpha), 1e-4).toVar();
|
|
821
|
+
const body = original.div(safeAlpha).toVar();
|
|
822
|
+
const night = saturate(float(nightness)).toVar();
|
|
823
|
+
const morning = saturate(float(morningLight)).toVar();
|
|
824
|
+
const evening = saturate(float(eveningLight)).toVar();
|
|
825
|
+
const eveningSide = evening.div(max(morning.add(evening), 1e-4)).toVar();
|
|
826
|
+
|
|
827
|
+
const bodyLuminance = max(body.dot(CLOUD_LUMINANCE), 0).toVar();
|
|
828
|
+
const bodySignal = bodyLuminance.div(bodyLuminance.add(1)).toVar();
|
|
829
|
+
const compressedNightSignal = bodySignal.mul(bodySignal).toVar();
|
|
830
|
+
const nightSignal = mix(
|
|
831
|
+
bodySignal,
|
|
832
|
+
compressedNightSignal,
|
|
833
|
+
saturate(timePalette.nightContrast),
|
|
834
|
+
).toVar();
|
|
835
|
+
// The marcher exposes a sunlight-weighted height boosted for the daytime
|
|
836
|
+
// white-top module. Bring it back to a normalized shell height here, then
|
|
837
|
+
// combine it with resolved opacity. This creates broad top/body/edge value
|
|
838
|
+
// regions at night even when the physical moon contribution is too uniform
|
|
839
|
+
// to describe the cloud on its own. It never changes density or alpha.
|
|
840
|
+
const heightSignal = saturate(float(sunlitHeight).mul(0.2)).toVar();
|
|
841
|
+
const topSignal = smoothstep(0.12, 0.78, heightSignal).toVar();
|
|
842
|
+
const depthSignal = smoothstep(0.08, 0.85, saturate(float(alpha))).toVar();
|
|
843
|
+
const edgeSignal = depthSignal.oneMinus().toVar();
|
|
844
|
+
const nightValue = nightSignal
|
|
845
|
+
.mul(1.2)
|
|
846
|
+
.add(topSignal.mul(0.012))
|
|
847
|
+
.add(edgeSignal.mul(0.006))
|
|
848
|
+
.add(0.008)
|
|
849
|
+
.toVar();
|
|
850
|
+
const warmBand = smoothstep(
|
|
851
|
+
0.01,
|
|
852
|
+
0.18,
|
|
853
|
+
max(bodySignal, heightSignal.mul(0.18)),
|
|
854
|
+
).toVar();
|
|
855
|
+
const nightBand = smoothstep(0.015, 0.08, nightValue).toVar();
|
|
856
|
+
const morningColor = mix(
|
|
857
|
+
timePalette.morningBottom,
|
|
858
|
+
timePalette.morningTop,
|
|
859
|
+
warmBand,
|
|
860
|
+
).toVar();
|
|
861
|
+
const eveningColor = mix(
|
|
862
|
+
timePalette.eveningBottom,
|
|
863
|
+
timePalette.eveningTop,
|
|
864
|
+
warmBand,
|
|
865
|
+
).toVar();
|
|
866
|
+
const nightColor = mix(
|
|
867
|
+
timePalette.nightBottom,
|
|
868
|
+
timePalette.nightTop,
|
|
869
|
+
nightBand,
|
|
870
|
+
).toVar();
|
|
871
|
+
const warmColor = mix(morningColor, eveningColor, eveningSide).toVar();
|
|
872
|
+
const palette = mix(warmColor, nightColor, night).toVar();
|
|
873
|
+
|
|
874
|
+
const edgeStart = saturate(style.innerPaint.edgeKeep).toVar();
|
|
875
|
+
const edgeWidth = max(style.innerPaint.edgeBlend, 1e-4).toVar();
|
|
876
|
+
const interiorMask = smoothstep(
|
|
877
|
+
edgeStart,
|
|
878
|
+
edgeStart.add(edgeWidth),
|
|
879
|
+
saturate(float(alpha)),
|
|
880
|
+
).toVar();
|
|
881
|
+
const contourColor = mix(float(0.9), float(1), interiorMask).toVar();
|
|
882
|
+
|
|
883
|
+
const warmBrightness = mix(
|
|
884
|
+
timePalette.morningBrightness,
|
|
885
|
+
timePalette.eveningBrightness,
|
|
886
|
+
eveningSide,
|
|
887
|
+
).toVar();
|
|
888
|
+
const brightness = mix(
|
|
889
|
+
warmBrightness,
|
|
890
|
+
timePalette.nightBrightness,
|
|
891
|
+
night,
|
|
892
|
+
).toVar();
|
|
893
|
+
const shapedLuminance = mix(bodySignal, nightValue, night).toVar();
|
|
894
|
+
const styledLuminance = shapedLuminance.mul(max(brightness, 0)).toVar();
|
|
895
|
+
const paletteLuminance = max(palette.dot(CLOUD_LUMINANCE), 1e-4).toVar();
|
|
896
|
+
const tintedBody = palette.mul(styledLuminance.div(paletteLuminance)).toVar();
|
|
897
|
+
const warmDetail = mix(
|
|
898
|
+
timePalette.morningDetail,
|
|
899
|
+
timePalette.eveningDetail,
|
|
900
|
+
eveningSide,
|
|
901
|
+
).toVar();
|
|
902
|
+
const retainedDetail = saturate(
|
|
903
|
+
mix(warmDetail, timePalette.nightDetail, night),
|
|
904
|
+
).toVar();
|
|
905
|
+
// Detail means the original volumetric lighting, including its value
|
|
906
|
+
// structure. The earlier draft normalized this branch back to the painted
|
|
907
|
+
// luminance, so it restored hue but erased the self-shadowing developers
|
|
908
|
+
// expected the control to preserve.
|
|
909
|
+
const paintedBody = mix(
|
|
910
|
+
tintedBody,
|
|
911
|
+
body,
|
|
912
|
+
retainedDetail,
|
|
913
|
+
).toVar();
|
|
914
|
+
const timeAmount = max(
|
|
915
|
+
max(
|
|
916
|
+
morning.mul(timePalette.morningEnabled).mul(timePalette.morningAmount),
|
|
917
|
+
evening.mul(timePalette.eveningEnabled).mul(timePalette.eveningAmount),
|
|
918
|
+
),
|
|
919
|
+
night.mul(timePalette.nightEnabled).mul(timePalette.nightAmount),
|
|
920
|
+
).toVar();
|
|
921
|
+
const amount = saturate(
|
|
922
|
+
style.enabled
|
|
923
|
+
.mul(timePalette.enabled)
|
|
924
|
+
.mul(timeAmount)
|
|
925
|
+
.mul(contourColor),
|
|
926
|
+
).toVar();
|
|
927
|
+
// Keep afternoon on the exact V2.6 result. A conditional return is stronger
|
|
928
|
+
// than evaluating the time colour and trusting a zero mix to cancel it;
|
|
929
|
+
// inactive time calculations cannot perturb or poison the day value.
|
|
930
|
+
return timeAmount.greaterThan(1e-5).select(
|
|
931
|
+
mix(original, paintedBody.mul(safeAlpha), amount),
|
|
932
|
+
original,
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Binds the scattering model to one set of live parameter groups.
|
|
938
|
+
*
|
|
939
|
+
* `lighting` is a `CloudLighting` group, `atmosphere` an atmosphere param group
|
|
940
|
+
* (`multipleScattering` and `groundAlbedo` are read — the latter only to cancel
|
|
941
|
+
* it out of the fill, see `cloudAmbientNode`), and `timeOfDay` is optional —
|
|
942
|
+
* without it the moon terms resolve to zero rather than to a full moon.
|
|
943
|
+
*
|
|
944
|
+
* Returns `inScatterNode`, a laid-out TSL function. RGB is the styled radiance
|
|
945
|
+
* scattered toward the eye from one cubic metre of cloud, with
|
|
946
|
+
* `scatteringAlbedo` already applied. Alpha preserves the physical luminance
|
|
947
|
+
* from before any style module, so final-pixel paint can restore lighting value
|
|
948
|
+
* without a second light march. The marcher's job is then the segment integral:
|
|
949
|
+
* `radiance += transmittance * (1 - exp(-sigma dt)) * inScatter`, which is the
|
|
950
|
+
* analytically integrated form for a constant medium over the segment.
|
|
951
|
+
*/
|
|
952
|
+
export function createCloudLightingModel({
|
|
953
|
+
lighting,
|
|
954
|
+
atmosphere,
|
|
955
|
+
style = null,
|
|
956
|
+
timeOfDay = null,
|
|
957
|
+
name = 'cloudInScatter',
|
|
958
|
+
} = {}) {
|
|
959
|
+
if (!lighting?.scatteringAlbedo || !lighting?.powderStrength) {
|
|
960
|
+
throw new TypeError('createCloudLightingModel needs a cloud lighting param group.');
|
|
961
|
+
}
|
|
962
|
+
// `groundAlbedo` as well as `multipleScattering`: the fill has to know which
|
|
963
|
+
// albedo the skylight table was baked with to avoid counting the ground twice.
|
|
964
|
+
if (!atmosphere?.multipleScattering || !atmosphere?.groundAlbedo) {
|
|
965
|
+
throw new TypeError('createCloudLightingModel needs an atmosphere param group.');
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// Absent clock: no moon, rather than a full one. `float(0)` is a constant node
|
|
969
|
+
// the compiler folds away, so the moon terms cost nothing when unused.
|
|
970
|
+
const moonIllumination = timeOfDay?.moonPhaseIllumination ?? float(0);
|
|
971
|
+
const nightFactor = timeOfDay?.skyDarkness ?? float(0);
|
|
972
|
+
|
|
973
|
+
lightingModelId += 1;
|
|
974
|
+
const functionName = `${name}${lightingModelId}`;
|
|
975
|
+
|
|
976
|
+
const inScatterNode = Fn(([
|
|
977
|
+
heightFraction,
|
|
978
|
+
occupancy,
|
|
979
|
+
extinction,
|
|
980
|
+
lightOpticalDepth,
|
|
981
|
+
moonLightOpticalDepth,
|
|
982
|
+
slabAbove,
|
|
983
|
+
slabBelow,
|
|
984
|
+
cosSun,
|
|
985
|
+
cosMoon,
|
|
986
|
+
sunRadiance,
|
|
987
|
+
zenithRadiance,
|
|
988
|
+
horizonRadiance,
|
|
989
|
+
groundBounceRadiance,
|
|
990
|
+
moonRadiance,
|
|
991
|
+
]) => {
|
|
992
|
+
const baseShadow = cloudBaseShadowNode({
|
|
993
|
+
baseShadowHeight: lighting.baseShadowHeight,
|
|
994
|
+
baseShadowStrength: lighting.baseShadowStrength,
|
|
995
|
+
heightFraction,
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
const direct = sunRadiance.mul(cloudDirectionalScatteringNode({
|
|
999
|
+
cosTheta: cosSun,
|
|
1000
|
+
lightOpticalDepth,
|
|
1001
|
+
occupancy,
|
|
1002
|
+
powderStrength: lighting.powderStrength,
|
|
1003
|
+
}));
|
|
1004
|
+
|
|
1005
|
+
const ambient = cloudSkyAmbientNode({
|
|
1006
|
+
ambientIntensity: lighting.ambientIntensity,
|
|
1007
|
+
groundBounceRadiance,
|
|
1008
|
+
heightFraction,
|
|
1009
|
+
horizonRadiance,
|
|
1010
|
+
zenithRadiance,
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
const moonDirectional = cloudMoonDirectionalNode({
|
|
1014
|
+
cosTheta: cosMoon,
|
|
1015
|
+
lightOpticalDepth: moonLightOpticalDepth,
|
|
1016
|
+
moonGain: lighting.moonGain,
|
|
1017
|
+
moonIllumination,
|
|
1018
|
+
moonRadiance,
|
|
1019
|
+
nightFactor,
|
|
1020
|
+
occupancy,
|
|
1021
|
+
powderStrength: lighting.powderStrength,
|
|
1022
|
+
});
|
|
1023
|
+
const moonRim = cloudMoonRimNode({
|
|
1024
|
+
cosTheta: cosMoon,
|
|
1025
|
+
extinction,
|
|
1026
|
+
moonGain: lighting.moonGain,
|
|
1027
|
+
moonIllumination,
|
|
1028
|
+
moonRadiance,
|
|
1029
|
+
nightFactor,
|
|
1030
|
+
});
|
|
1031
|
+
const moon = moonDirectional.add(moonRim.mul(0.35));
|
|
1032
|
+
|
|
1033
|
+
void slabAbove;
|
|
1034
|
+
void slabBelow;
|
|
1035
|
+
const lit = direct.add(moon).mul(baseShadow).add(ambient);
|
|
1036
|
+
const physical = max(lit.mul(saturate(lighting.scatteringAlbedo)), vec3(0));
|
|
1037
|
+
const styled = applyCloudStyleNode(physical, style, nightFactor.oneMinus());
|
|
1038
|
+
return vec4(styled, max(physical.dot(CLOUD_LUMINANCE), 0));
|
|
1039
|
+
}).setLayout({
|
|
1040
|
+
name: functionName,
|
|
1041
|
+
type: 'vec4',
|
|
1042
|
+
inputs: [
|
|
1043
|
+
{ name: 'heightFraction', type: 'float' },
|
|
1044
|
+
{ name: 'occupancy', type: 'float' },
|
|
1045
|
+
{ name: 'extinction', type: 'float' },
|
|
1046
|
+
{ name: 'lightOpticalDepth', type: 'float' },
|
|
1047
|
+
{ name: 'moonLightOpticalDepth', type: 'float' },
|
|
1048
|
+
{ name: 'slabAbove', type: 'float' },
|
|
1049
|
+
{ name: 'slabBelow', type: 'float' },
|
|
1050
|
+
{ name: 'cosSun', type: 'float' },
|
|
1051
|
+
{ name: 'cosMoon', type: 'float' },
|
|
1052
|
+
{ name: 'sunRadiance', type: 'vec3' },
|
|
1053
|
+
{ name: 'zenithRadiance', type: 'vec3' },
|
|
1054
|
+
{ name: 'horizonRadiance', type: 'vec3' },
|
|
1055
|
+
{ name: 'groundBounceRadiance', type: 'vec3' },
|
|
1056
|
+
{ name: 'moonRadiance', type: 'vec3' },
|
|
1057
|
+
],
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
return {
|
|
1061
|
+
functionName,
|
|
1062
|
+
inScatterNode,
|
|
1063
|
+
lighting,
|
|
1064
|
+
atmosphere,
|
|
1065
|
+
style,
|
|
1066
|
+
timeOfDay,
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Sun transmittance through the cloud, 0..1 — what the shadow bake needs.
|
|
1070
|
+
*
|
|
1071
|
+
* View-independent and normalised, per `cloudSunTransmittanceNode`. The
|
|
1072
|
+
* bake's own job is still to return 1 outside its footprint.
|
|
1073
|
+
*/
|
|
1074
|
+
sunTransmittanceNode({ lightOpticalDepth }) {
|
|
1075
|
+
return cloudSunTransmittanceNode({ lightOpticalDepth });
|
|
1076
|
+
},
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* The phase-weighted, powdered Nubis directional term — the key light's
|
|
1080
|
+
* contribution to radiance scattered toward `cosTheta`, which is what the
|
|
1081
|
+
* env-map bake needs and what the marcher multiplies `sunRadiance` by.
|
|
1082
|
+
*
|
|
1083
|
+
* Not a transmittance and not in [0, 1]: it carries the max-combined broad
|
|
1084
|
+
* and silver-lining phase. Use `sunTransmittanceNode` for anything that has
|
|
1085
|
+
* to shadow.
|
|
1086
|
+
*/
|
|
1087
|
+
phaseVisibilityNode({ cosTheta, occupancy, lightOpticalDepth }) {
|
|
1088
|
+
return cloudDirectionalScatteringNode({
|
|
1089
|
+
cosTheta,
|
|
1090
|
+
lightOpticalDepth,
|
|
1091
|
+
occupancy,
|
|
1092
|
+
powderStrength: lighting.powderStrength,
|
|
1093
|
+
});
|
|
1094
|
+
},
|
|
1095
|
+
|
|
1096
|
+
/** Fixed phase terms exposed for diagnostics and verification. */
|
|
1097
|
+
phaseConstants: Object.freeze({
|
|
1098
|
+
backwardG: CLOUD_PHASE_BACKWARD_G,
|
|
1099
|
+
phaseBlend: CLOUD_PHASE_BLEND,
|
|
1100
|
+
forwardG: CLOUD_PHASE_FORWARD_G,
|
|
1101
|
+
msEccentricity: CLOUD_MULTISCATTER_ECCENTRICITY,
|
|
1102
|
+
msExtinction: CLOUD_MULTISCATTER_EXTINCTION,
|
|
1103
|
+
msScatter: CLOUD_MULTISCATTER_SCATTER,
|
|
1104
|
+
}),
|
|
1105
|
+
nubis: Object.freeze({
|
|
1106
|
+
backwardG: CLOUD_PHASE_BACKWARD_G,
|
|
1107
|
+
phaseBlend: CLOUD_PHASE_BLEND,
|
|
1108
|
+
forwardG: CLOUD_PHASE_FORWARD_G,
|
|
1109
|
+
msEccentricity: CLOUD_MULTISCATTER_ECCENTRICITY,
|
|
1110
|
+
msExtinction: CLOUD_MULTISCATTER_EXTINCTION,
|
|
1111
|
+
msScatter: CLOUD_MULTISCATTER_SCATTER,
|
|
1112
|
+
}),
|
|
1113
|
+
};
|
|
1114
|
+
}
|