@3driseai/3drise-viewer 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Clouds.d.ts.map +1 -1
- package/dist/components/Clouds.js +0 -1
- package/dist/components/CloudsGenerator.d.ts +32 -0
- package/dist/components/CloudsGenerator.d.ts.map +1 -0
- package/dist/components/CloudsGenerator.js +58 -0
- package/dist/components/EnvironmentManager.d.ts +47 -5
- package/dist/components/EnvironmentManager.d.ts.map +1 -1
- package/dist/components/EnvironmentManager.js +35 -66
- package/dist/components/HdrEnvironment.d.ts +20 -0
- package/dist/components/HdrEnvironment.d.ts.map +1 -0
- package/dist/components/HdrEnvironment.js +44 -0
- package/dist/components/LandscapeBuilder.d.ts +23 -0
- package/dist/components/LandscapeBuilder.d.ts.map +1 -0
- package/dist/components/LandscapeBuilder.js +25 -0
- package/dist/components/LandscapeObject.d.ts +24 -0
- package/dist/components/LandscapeObject.d.ts.map +1 -0
- package/dist/components/LandscapeObject.js +27 -0
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +10 -0
- package/dist/components/OceanBuilder.d.ts +35 -0
- package/dist/components/OceanBuilder.d.ts.map +1 -0
- package/dist/components/OceanBuilder.js +53 -0
- package/dist/components/SceneBuilder.d.ts.map +1 -1
- package/dist/components/SkySystem.d.ts +27 -6
- package/dist/components/SkySystem.d.ts.map +1 -1
- package/dist/components/SkySystem.js +6 -5
- package/dist/components/SunLight.d.ts.map +1 -1
- package/dist/components/SunLight.js +4 -1
- package/dist/components/TerrainBuilder.d.ts +30 -0
- package/dist/components/TerrainBuilder.d.ts.map +1 -0
- package/dist/components/TerrainBuilder.js +44 -0
- package/dist/components/index.d.ts +17 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +10 -0
- package/dist/components/landscape/LandscapeMesh.d.ts +18 -0
- package/dist/components/landscape/LandscapeMesh.d.ts.map +1 -0
- package/dist/components/landscape/LandscapeMesh.js +278 -0
- package/dist/components/landscape/index.d.ts +3 -0
- package/dist/components/landscape/index.d.ts.map +1 -0
- package/dist/components/landscape/index.js +1 -0
- package/dist/components/landscape/types.d.ts +13 -0
- package/dist/components/landscape/types.d.ts.map +1 -0
- package/dist/components/landscape/types.js +1 -0
- package/dist/components/terrain/Canyon.d.ts +23 -0
- package/dist/components/terrain/Canyon.d.ts.map +1 -0
- package/dist/components/terrain/Canyon.js +29 -0
- package/dist/components/terrain/Dunes.d.ts +22 -0
- package/dist/components/terrain/Dunes.d.ts.map +1 -0
- package/dist/components/terrain/Dunes.js +25 -0
- package/dist/components/terrain/Hills.d.ts +17 -0
- package/dist/components/terrain/Hills.d.ts.map +1 -0
- package/dist/components/terrain/Hills.js +20 -0
- package/dist/components/terrain/Mountains.d.ts +19 -0
- package/dist/components/terrain/Mountains.d.ts.map +1 -0
- package/dist/components/terrain/Mountains.js +22 -0
- package/dist/components/terrain/TerrainSurface.d.ts +22 -0
- package/dist/components/terrain/TerrainSurface.d.ts.map +1 -0
- package/dist/components/terrain/TerrainSurface.js +94 -0
- package/dist/components/terrain/index.d.ts +7 -0
- package/dist/components/terrain/index.d.ts.map +1 -0
- package/dist/components/terrain/index.js +7 -0
- package/dist/components/terrain/types.d.ts +17 -0
- package/dist/components/terrain/types.d.ts.map +1 -0
- package/dist/components/terrain/types.js +1 -0
- package/dist/components/volumetricClouds/Cirrus.d.ts +14 -0
- package/dist/components/volumetricClouds/Cirrus.d.ts.map +1 -0
- package/dist/components/volumetricClouds/Cirrus.js +13 -0
- package/dist/components/volumetricClouds/Cumulonimbus.d.ts +17 -0
- package/dist/components/volumetricClouds/Cumulonimbus.d.ts.map +1 -0
- package/dist/components/volumetricClouds/Cumulonimbus.js +20 -0
- package/dist/components/volumetricClouds/Cumulus.d.ts +14 -0
- package/dist/components/volumetricClouds/Cumulus.d.ts.map +1 -0
- package/dist/components/volumetricClouds/Cumulus.js +17 -0
- package/dist/components/volumetricClouds/Stratocumulus.d.ts +12 -0
- package/dist/components/volumetricClouds/Stratocumulus.d.ts.map +1 -0
- package/dist/components/volumetricClouds/Stratocumulus.js +16 -0
- package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts +42 -0
- package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts.map +1 -0
- package/dist/components/volumetricClouds/VolumetricCloudsPass.js +298 -0
- package/dist/components/volumetricClouds/index.d.ts +8 -0
- package/dist/components/volumetricClouds/index.d.ts.map +1 -0
- package/dist/components/volumetricClouds/index.js +7 -0
- package/dist/components/volumetricClouds/types.d.ts +17 -0
- package/dist/components/volumetricClouds/types.d.ts.map +1 -0
- package/dist/components/volumetricClouds/types.js +1 -0
- package/dist/components/water/Host.d.ts +12 -0
- package/dist/components/water/Host.d.ts.map +1 -0
- package/dist/components/water/Host.js +12 -0
- package/dist/components/water/Lake.d.ts +18 -0
- package/dist/components/water/Lake.d.ts.map +1 -0
- package/dist/components/water/Lake.js +21 -0
- package/dist/components/water/OceanSurface.d.ts +27 -0
- package/dist/components/water/OceanSurface.d.ts.map +1 -0
- package/dist/components/water/OceanSurface.js +60 -0
- package/dist/components/water/Sea.d.ts +18 -0
- package/dist/components/water/Sea.d.ts.map +1 -0
- package/dist/components/water/Sea.js +17 -0
- package/dist/components/water/Shore.d.ts +16 -0
- package/dist/components/water/Shore.d.ts.map +1 -0
- package/dist/components/water/Shore.js +15 -0
- package/dist/components/water/Storm.d.ts +14 -0
- package/dist/components/water/Storm.d.ts.map +1 -0
- package/dist/components/water/Storm.js +13 -0
- package/dist/components/water/WaterBody.d.ts +31 -0
- package/dist/components/water/WaterBody.d.ts.map +1 -0
- package/dist/components/water/WaterBody.js +76 -0
- package/dist/components/water/index.d.ts +9 -0
- package/dist/components/water/index.d.ts.map +1 -0
- package/dist/components/water/index.js +11 -0
- package/dist/components/water/types.d.ts +20 -0
- package/dist/components/water/types.d.ts.map +1 -0
- package/dist/components/water/types.js +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +9 -0
- package/dist/hooks/index.d.ts +24 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +12 -0
- package/dist/hooks/useClipmap.d.ts +71 -0
- package/dist/hooks/useClipmap.d.ts.map +1 -0
- package/dist/hooks/useClipmap.js +81 -0
- package/dist/hooks/useCloudLive.d.ts +29 -0
- package/dist/hooks/useCloudLive.d.ts.map +1 -0
- package/dist/hooks/useCloudLive.js +43 -0
- package/dist/hooks/useCloudNoise.d.ts +28 -0
- package/dist/hooks/useCloudNoise.d.ts.map +1 -0
- package/dist/hooks/useCloudNoise.js +128 -0
- package/dist/hooks/useCloudSun.d.ts +49 -0
- package/dist/hooks/useCloudSun.d.ts.map +1 -0
- package/dist/hooks/useCloudSun.js +84 -0
- package/dist/hooks/useCubeCamera.d.ts +27 -9
- package/dist/hooks/useCubeCamera.d.ts.map +1 -1
- package/dist/hooks/useCubeCamera.js +28 -70
- package/dist/hooks/useEnvironmentProbe.d.ts +32 -0
- package/dist/hooks/useEnvironmentProbe.d.ts.map +1 -0
- package/dist/hooks/useEnvironmentProbe.js +35 -0
- package/dist/hooks/useFrameRenderer.d.ts +15 -0
- package/dist/hooks/useFrameRenderer.d.ts.map +1 -0
- package/dist/hooks/useFrameRenderer.js +23 -0
- package/dist/hooks/useLandscapeLive.d.ts +29 -0
- package/dist/hooks/useLandscapeLive.d.ts.map +1 -0
- package/dist/hooks/useLandscapeLive.js +41 -0
- package/dist/hooks/useLandscapeMaps.d.ts +32 -0
- package/dist/hooks/useLandscapeMaps.d.ts.map +1 -0
- package/dist/hooks/useLandscapeMaps.js +138 -0
- package/dist/hooks/useOceanLive.d.ts +29 -0
- package/dist/hooks/useOceanLive.d.ts.map +1 -0
- package/dist/hooks/useOceanLive.js +38 -0
- package/dist/hooks/useOceanMaterial.d.ts +39 -0
- package/dist/hooks/useOceanMaterial.d.ts.map +1 -0
- package/dist/hooks/useOceanMaterial.js +65 -0
- package/dist/hooks/useTerrainClipmap.d.ts +12 -0
- package/dist/hooks/useTerrainClipmap.d.ts.map +1 -0
- package/dist/hooks/useTerrainClipmap.js +10 -0
- package/dist/hooks/useTerrainLive.d.ts +34 -0
- package/dist/hooks/useTerrainLive.d.ts.map +1 -0
- package/dist/hooks/useTerrainLive.js +45 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/presets/landscapePresets.d.ts +60 -0
- package/dist/presets/landscapePresets.d.ts.map +1 -0
- package/dist/presets/landscapePresets.js +194 -0
- package/dist/presets/landscapeTypes.d.ts +195 -0
- package/dist/presets/landscapeTypes.d.ts.map +1 -0
- package/dist/presets/landscapeTypes.js +315 -0
- package/dist/presets/oceanSurfacePresets.d.ts +33 -0
- package/dist/presets/oceanSurfacePresets.d.ts.map +1 -0
- package/dist/presets/oceanSurfacePresets.js +245 -0
- package/dist/presets/terrainPresets.d.ts +28 -0
- package/dist/presets/terrainPresets.d.ts.map +1 -0
- package/dist/presets/terrainPresets.js +293 -0
- package/dist/presets/volumetricCloudPresets.d.ts +38 -0
- package/dist/presets/volumetricCloudPresets.d.ts.map +1 -0
- package/dist/presets/volumetricCloudPresets.js +480 -0
- package/dist/shaders/clouds/CirrusMaterial.d.ts +84 -0
- package/dist/shaders/clouds/CirrusMaterial.d.ts.map +1 -0
- package/dist/shaders/clouds/CirrusMaterial.js +167 -0
- package/dist/shaders/clouds/CloudCompositeMaterial.d.ts +20 -0
- package/dist/shaders/clouds/CloudCompositeMaterial.d.ts.map +1 -0
- package/dist/shaders/clouds/CloudCompositeMaterial.js +82 -0
- package/dist/shaders/clouds/CloudResolveMaterial.d.ts +22 -0
- package/dist/shaders/clouds/CloudResolveMaterial.d.ts.map +1 -0
- package/dist/shaders/clouds/CloudResolveMaterial.js +77 -0
- package/dist/shaders/clouds/CloudVolumeMaterial.d.ts +143 -0
- package/dist/shaders/clouds/CloudVolumeMaterial.d.ts.map +1 -0
- package/dist/shaders/clouds/CloudVolumeMaterial.js +249 -0
- package/dist/shaders/clouds/SkyBackgroundMaterial.d.ts +21 -0
- package/dist/shaders/clouds/SkyBackgroundMaterial.d.ts.map +1 -0
- package/dist/shaders/clouds/SkyBackgroundMaterial.js +42 -0
- package/dist/shaders/glsl/cloudCore.glsl.d.ts +24 -0
- package/dist/shaders/glsl/cloudCore.glsl.d.ts.map +1 -0
- package/dist/shaders/glsl/cloudCore.glsl.js +348 -0
- package/dist/shaders/glsl/landscapeCore.glsl.d.ts +49 -0
- package/dist/shaders/glsl/landscapeCore.glsl.d.ts.map +1 -0
- package/dist/shaders/glsl/landscapeCore.glsl.js +518 -0
- package/dist/shaders/glsl/noiseBake.glsl.d.ts +30 -0
- package/dist/shaders/glsl/noiseBake.glsl.d.ts.map +1 -0
- package/dist/shaders/glsl/noiseBake.glsl.js +164 -0
- package/dist/shaders/glsl/oceanCore.glsl.d.ts +52 -0
- package/dist/shaders/glsl/oceanCore.glsl.d.ts.map +1 -0
- package/dist/shaders/glsl/oceanCore.glsl.js +421 -0
- package/dist/shaders/glsl/terrainCore.glsl.d.ts +78 -0
- package/dist/shaders/glsl/terrainCore.glsl.d.ts.map +1 -0
- package/dist/shaders/glsl/terrainCore.glsl.js +625 -0
- package/dist/shaders/landscape/LandscapeMaterial.d.ts +73 -0
- package/dist/shaders/landscape/LandscapeMaterial.d.ts.map +1 -0
- package/dist/shaders/landscape/LandscapeMaterial.js +136 -0
- package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts +57 -0
- package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts.map +1 -0
- package/dist/shaders/ocean/OceanSurfaceMaterial.js +268 -0
- package/dist/shaders/terrain/TerrainMaterial.d.ts +80 -0
- package/dist/shaders/terrain/TerrainMaterial.d.ts.map +1 -0
- package/dist/shaders/terrain/TerrainMaterial.js +291 -0
- package/dist/types/cloudConfigs.d.ts +244 -0
- package/dist/types/cloudConfigs.d.ts.map +1 -0
- package/dist/types/cloudConfigs.js +282 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +4 -1
- package/dist/types/landscapeConfigs.d.ts +218 -0
- package/dist/types/landscapeConfigs.d.ts.map +1 -0
- package/dist/types/landscapeConfigs.js +256 -0
- package/dist/types/oceanConfigs.d.ts +290 -0
- package/dist/types/oceanConfigs.d.ts.map +1 -0
- package/dist/types/oceanConfigs.js +380 -0
- package/dist/types/terrainConfigs.d.ts +251 -0
- package/dist/types/terrainConfigs.d.ts.map +1 -0
- package/dist/types/terrainConfigs.js +292 -0
- package/dist/utils/cloudUniforms.d.ts +14 -0
- package/dist/utils/cloudUniforms.d.ts.map +1 -0
- package/dist/utils/cloudUniforms.js +122 -0
- package/dist/utils/frameRenderer.d.ts +33 -0
- package/dist/utils/frameRenderer.d.ts.map +1 -0
- package/dist/utils/frameRenderer.js +24 -0
- package/dist/utils/handleCloudsAnimations.d.ts +16 -0
- package/dist/utils/handleCloudsAnimations.d.ts.map +1 -0
- package/dist/utils/handleCloudsAnimations.js +64 -0
- package/dist/utils/landscapeGeometry.d.ts +61 -0
- package/dist/utils/landscapeGeometry.d.ts.map +1 -0
- package/dist/utils/landscapeGeometry.js +126 -0
- package/dist/utils/landscapeHeight.d.ts +44 -0
- package/dist/utils/landscapeHeight.d.ts.map +1 -0
- package/dist/utils/landscapeHeight.js +65 -0
- package/dist/utils/landscapeTextures.d.ts +94 -0
- package/dist/utils/landscapeTextures.d.ts.map +1 -0
- package/dist/utils/landscapeTextures.js +290 -0
- package/dist/utils/landscapeUniforms.d.ts +20 -0
- package/dist/utils/landscapeUniforms.d.ts.map +1 -0
- package/dist/utils/landscapeUniforms.js +61 -0
- package/dist/utils/oceanUniforms.d.ts +47 -0
- package/dist/utils/oceanUniforms.d.ts.map +1 -0
- package/dist/utils/oceanUniforms.js +216 -0
- package/dist/utils/rtin.d.ts +63 -0
- package/dist/utils/rtin.d.ts.map +1 -0
- package/dist/utils/rtin.js +202 -0
- package/dist/utils/terrainUniforms.d.ts +14 -0
- package/dist/utils/terrainUniforms.d.ts.map +1 -0
- package/dist/utils/terrainUniforms.js +196 -0
- package/package.json +2 -2
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared cloud core. Imported by CloudVolumeMaterial and by any surface material that
|
|
3
|
+
* wants cloud shadows. Nothing here is per-type: the four cloud models differ by the
|
|
4
|
+
* height-gradient blend (u_cloudType), the shear/anvil/precipitation terms, and their
|
|
5
|
+
* configs — not by separate shader code.
|
|
6
|
+
*
|
|
7
|
+
* Fragment shaders including this MUST be compiled with glslVersion: THREE.GLSL3.
|
|
8
|
+
* GLSL ES 3.00 declares no default precision for sampler3D, so the precision lines
|
|
9
|
+
* below are required, not decoration.
|
|
10
|
+
*/
|
|
11
|
+
export const cloudCommon = /* glsl */ `
|
|
12
|
+
#define PI 3.14159265359
|
|
13
|
+
#define saturate(x) clamp(x, 0.0, 1.0)
|
|
14
|
+
float remap(float v, float a, float b, float c, float d){
|
|
15
|
+
return c + (v - a) * (d - c) / max(b - a, 1e-6);
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
/** Analytic single-scattering sky. Also supplies the clouds' ambient and aerial perspective. */
|
|
19
|
+
export const skyLib = /* glsl */ `
|
|
20
|
+
uniform vec3 u_sunDir;
|
|
21
|
+
uniform vec3 u_sunColor;
|
|
22
|
+
uniform float u_sunIntensity;
|
|
23
|
+
|
|
24
|
+
// The four Preetham parameters, taken straight from SkySettings so the clouds are
|
|
25
|
+
// lit by the same atmosphere SkyController draws. Hardcoding them here is exactly
|
|
26
|
+
// what makes cloud lighting drift away from the sky behind it.
|
|
27
|
+
uniform float u_turbidity;
|
|
28
|
+
uniform float u_rayleigh;
|
|
29
|
+
uniform float u_mieCoefficient;
|
|
30
|
+
uniform float u_mieG;
|
|
31
|
+
|
|
32
|
+
/** Sun radiance in the same units skyRadiance() returns. Phase functions integrate to 1
|
|
33
|
+
* over the sphere, so they return ~1/4pi; without a real radiance the clouds render black. */
|
|
34
|
+
const float SUN_RADIANCE = 22.0;
|
|
35
|
+
|
|
36
|
+
float rayleighPhase(float mu){ return (3.0 / (16.0 * PI)) * (1.0 + mu * mu); }
|
|
37
|
+
float miePhaseHG(float mu, float g){
|
|
38
|
+
float g2 = g * g;
|
|
39
|
+
return (1.0 - g2) / (4.0 * PI * pow(max(1.0 + g2 - 2.0 * g * mu, 1e-4), 1.5));
|
|
40
|
+
}
|
|
41
|
+
/** Kasten-Young relative air mass. */
|
|
42
|
+
float airMass(float cy){
|
|
43
|
+
float z = degrees(acos(clamp(cy, -1.0, 1.0)));
|
|
44
|
+
return min(1.0 / (max(cy, 0.0) + 0.15 * pow(max(93.885 - z, 0.5), -1.253)), 40.0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
vec3 skyRadiance(vec3 rd){
|
|
48
|
+
float mu = dot(rd, u_sunDir);
|
|
49
|
+
float up = rd.y;
|
|
50
|
+
float am = airMass(max(up, -0.02));
|
|
51
|
+
|
|
52
|
+
vec3 bR = vec3(0.0487, 0.1130, 0.2780) * u_turbidity * u_rayleigh;
|
|
53
|
+
float bM = u_mieCoefficient * u_turbidity * 4.25;
|
|
54
|
+
vec3 ext = bR * am + vec3(bM * am * 1.2);
|
|
55
|
+
|
|
56
|
+
// A single-scattering model that applies the full reddened beam everywhere turns the
|
|
57
|
+
// zenith teal at low sun. Most zenith light is in reality multiply scattered, so the
|
|
58
|
+
// Rayleigh term sees a partly whitened sun; only the Mie glow keeps the full depth.
|
|
59
|
+
vec3 rayCol = mix(u_sunColor, vec3(1.0), 0.60);
|
|
60
|
+
vec3 insR = bR * am * rayleighPhase(mu) * rayCol;
|
|
61
|
+
vec3 insM = vec3(bM * am * miePhaseHG(mu, u_mieG) * 1.3) * u_sunColor;
|
|
62
|
+
|
|
63
|
+
vec3 col = ((insR + insM) / max(ext, vec3(1e-4))) * (1.0 - exp(-ext)) * 26.0;
|
|
64
|
+
|
|
65
|
+
float below = saturate(-up * 14.0);
|
|
66
|
+
vec3 groundCol = vec3(0.055, 0.052, 0.048) * u_sunColor * (0.25 + 0.75 * saturate(u_sunDir.y));
|
|
67
|
+
return mix(col, groundCol, below);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
vec3 sunDisc(vec3 rd){
|
|
71
|
+
float mu = dot(rd, u_sunDir);
|
|
72
|
+
float d = smoothstep(0.99976, 0.99992, mu);
|
|
73
|
+
float limb = 1.0 - 0.42 * saturate((0.99992 - mu) / 0.00016);
|
|
74
|
+
return u_sunColor * u_sunIntensity * d * limb * 42.0;
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
/** Every uniform the density field reads. Include before cloudDensity. */
|
|
78
|
+
export const cloudUniforms = /* glsl */ `
|
|
79
|
+
precision highp float;
|
|
80
|
+
precision highp sampler3D;
|
|
81
|
+
precision highp sampler2D;
|
|
82
|
+
|
|
83
|
+
uniform sampler3D u_base;
|
|
84
|
+
uniform sampler3D u_detail;
|
|
85
|
+
uniform sampler2D u_weather;
|
|
86
|
+
uniform sampler2D u_curl;
|
|
87
|
+
uniform float u_time;
|
|
88
|
+
|
|
89
|
+
uniform float u_coverage, u_density, u_cloudType;
|
|
90
|
+
uniform float u_baseScale, u_detailScale, u_detailStrength, u_curlStrength;
|
|
91
|
+
uniform float u_layerBottom, u_layerTop, u_shear, u_anvil, u_precip;
|
|
92
|
+
uniform float u_weatherScale, u_evolution;
|
|
93
|
+
uniform vec2 u_windDir;
|
|
94
|
+
uniform float u_windSpeed, u_rise, u_churn;
|
|
95
|
+
uniform float u_host, u_radius, u_curvature;
|
|
96
|
+
uniform vec3 u_center;
|
|
97
|
+
`;
|
|
98
|
+
export const cloudDensity = /* glsl */ `
|
|
99
|
+
/**
|
|
100
|
+
* Height fraction inside the cloud body.
|
|
101
|
+
* In layer mode the ray is bent by planetary curvature (t^2 / 2R) so the deck meets a
|
|
102
|
+
* real horizon instead of running to infinity as a flat slab would. Bending the ray is
|
|
103
|
+
* equivalent to raising the sample's altitude and keeps coordinates small, which a
|
|
104
|
+
* literal 6371 km sphere does not.
|
|
105
|
+
*/
|
|
106
|
+
float heightAt(vec3 p, float t){
|
|
107
|
+
if (u_host < 0.5){
|
|
108
|
+
return saturate((p.y - (u_center.y - u_radius)) / max(2.0 * u_radius, 1.0));
|
|
109
|
+
}
|
|
110
|
+
float y = p.y + t * t * u_curvature;
|
|
111
|
+
return saturate((y - u_layerBottom) / max(u_layerTop - u_layerBottom, 1.0));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Two scales scrolling at different rates: coverage evolves rather than slides past. */
|
|
115
|
+
vec4 sampleWeather(vec3 p){
|
|
116
|
+
vec2 b = p.xz * u_weatherScale;
|
|
117
|
+
vec2 drift = u_windDir * (u_time * u_windSpeed * 0.16) * u_weatherScale;
|
|
118
|
+
vec4 a = texture(u_weather, b + drift);
|
|
119
|
+
vec4 c = texture(u_weather, b * 2.17 - drift * 1.63 + vec2(0.31, 0.73));
|
|
120
|
+
return mix(a, c, 0.36);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Three archetypal vertical profiles, blended by type. This is what makes the four
|
|
125
|
+
* models genuinely different shapes rather than one shape with different noise.
|
|
126
|
+
* 0.0 stratus thin, bottom-weighted, flat top
|
|
127
|
+
* 0.5 cumulus flat base, bulge in the upper middle, eroded cauliflower top
|
|
128
|
+
* 1.0 cumulonimbus holds density to the tropopause, then spreads
|
|
129
|
+
*/
|
|
130
|
+
float heightGradient(float h, float ty){
|
|
131
|
+
float stratus = saturate(remap(h, 0.00, 0.06, 0.0, 1.0)) * saturate(remap(h, 0.20, 0.36, 1.0, 0.0));
|
|
132
|
+
float cumulus = saturate(remap(h, 0.02, 0.20, 0.0, 1.0)) * saturate(remap(h, 0.56, 0.96, 1.0, 0.0));
|
|
133
|
+
float cumulon = saturate(remap(h, 0.01, 0.09, 0.0, 1.0)) * saturate(remap(h, 0.82, 1.00, 1.0, 0.0));
|
|
134
|
+
return mix(mix(stratus, cumulus, saturate(ty * 2.0)), cumulon, saturate((ty - 0.5) * 2.0));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param detail 0..1 — fades high-frequency erosion out with distance. Without it the
|
|
139
|
+
* finest worley octave beats against the step size and the far field speckles.
|
|
140
|
+
*/
|
|
141
|
+
float cloudDensity(vec3 p, float h, float detail){
|
|
142
|
+
if (h <= 0.001 || h >= 0.999) return 0.0;
|
|
143
|
+
|
|
144
|
+
vec4 w = sampleWeather(p);
|
|
145
|
+
float ty = saturate(u_cloudType + (w.b - 0.5) * 0.30);
|
|
146
|
+
|
|
147
|
+
float cov = mix(w.r, w.g, 0.40);
|
|
148
|
+
// Slow formation and decay: the coverage threshold itself breathes.
|
|
149
|
+
cov += sin(u_time * 0.041 + w.b * 12.6) * 0.5 * u_evolution * 0.16;
|
|
150
|
+
|
|
151
|
+
// The window slides AND narrows with coverage. Remapping to a fixed upper bound of 1.0
|
|
152
|
+
// caps effective coverage near the weather map's median (~0.5), so overcast is
|
|
153
|
+
// unreachable no matter how high the slider goes.
|
|
154
|
+
cov = saturate(remap(cov, 1.0 - u_coverage, 1.0 - u_coverage * 0.35, 0.0, 1.0));
|
|
155
|
+
if (cov <= 0.0) return 0.0;
|
|
156
|
+
|
|
157
|
+
// Anvil: above the equilibrium level a storm spreads laterally. Gated on type so a
|
|
158
|
+
// fair-weather cumulus never grows one.
|
|
159
|
+
float anvil = saturate(remap(h, 0.70, 1.0, 0.0, 1.0)) * u_anvil * saturate((ty - 0.5) * 2.0);
|
|
160
|
+
cov = saturate(mix(cov, cov * 1.9, anvil));
|
|
161
|
+
|
|
162
|
+
// Object host: fade coverage toward the bounding sphere so the volume is not sliced
|
|
163
|
+
// flat by its own container. Through coverage, not final density, so the detail
|
|
164
|
+
// erosion still bites into the boundary and the silhouette stays ragged.
|
|
165
|
+
if (u_host < 0.5){
|
|
166
|
+
float rad = length(p - u_center) / max(u_radius, 1.0);
|
|
167
|
+
cov *= saturate(remap(rad, 0.50, 0.97, 1.0, 0.0));
|
|
168
|
+
if (cov <= 0.0) return 0.0;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Wind shear tilts the column with altitude; then advection; then convective rise.
|
|
172
|
+
// Three decoupled timescales is what reads as delicate rather than scrolling.
|
|
173
|
+
vec3 sp = p;
|
|
174
|
+
float thick = (u_host < 0.5) ? (2.0 * u_radius) : max(u_layerTop - u_layerBottom, 1.0);
|
|
175
|
+
sp.xz += u_windDir * (h * u_shear * thick);
|
|
176
|
+
sp.xz += u_windDir * (u_time * u_windSpeed);
|
|
177
|
+
sp.y -= u_time * u_rise;
|
|
178
|
+
|
|
179
|
+
vec4 b = texture(u_base, sp * u_baseScale);
|
|
180
|
+
float lowFbm = b.g * 0.625 + b.b * 0.25 + b.a * 0.125;
|
|
181
|
+
float shape = saturate(remap(b.r, lowFbm - 1.0, 1.0, 0.0, 1.0));
|
|
182
|
+
shape *= heightGradient(h, ty);
|
|
183
|
+
|
|
184
|
+
float d = saturate(remap(shape, 1.0 - cov, 1.0, 0.0, 1.0)) * cov;
|
|
185
|
+
if (d <= 0.0) return 0.0;
|
|
186
|
+
|
|
187
|
+
if (detail > 0.01){
|
|
188
|
+
vec2 curl = texture(u_curl, sp.xz * u_baseScale * 2.4).rg * 2.0 - 1.0;
|
|
189
|
+
vec3 dp = sp;
|
|
190
|
+
// Curl warp is strongest at the base, where a cloud shears out into wisps.
|
|
191
|
+
dp.xz += curl * u_curlStrength * (1.0 - h) * 260.0;
|
|
192
|
+
dp.y -= u_time * u_churn;
|
|
193
|
+
vec3 dn = texture(u_detail, dp * u_detailScale).rgb;
|
|
194
|
+
float dFbm = dn.r * 0.625 + dn.g * 0.25 + dn.b * 0.125;
|
|
195
|
+
// Wispy erosion low down, billowy erosion up top.
|
|
196
|
+
float m = mix(1.0 - dFbm, dFbm, saturate(h * 3.2));
|
|
197
|
+
d = saturate(remap(d, m * u_detailStrength * detail, 1.0, 0.0, 1.0));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return d * u_density;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Scattering albedo. Rain-bearing cloud base holds large drops, which absorb far more
|
|
205
|
+
* than the small droplets higher up — this is why a storm base is charcoal while its
|
|
206
|
+
* top is brilliant white, and it is not something extinction alone reproduces.
|
|
207
|
+
*/
|
|
208
|
+
float cloudAlbedo(float h){
|
|
209
|
+
return mix(1.0, 0.22, u_precip * saturate(remap(h, 0.30, 0.02, 0.0, 1.0)));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
float cloudDensityCheap(vec3 p, float h){ return cloudDensity(p, h, 0.0); }
|
|
213
|
+
`;
|
|
214
|
+
/** Ray/layer and ray/sphere intersection. */
|
|
215
|
+
export const cloudGeometry = /* glsl */ `
|
|
216
|
+
/**
|
|
217
|
+
* Roots of a*t^2 + rd.y*t + (ro.y - h) = 0 with a = 1/(2R).
|
|
218
|
+
* The naive quadratic formula cancels catastrophically here: a is ~1e-7, so
|
|
219
|
+
* (-b + sqrt(b*b - 4ac)) / 2a returns noise for steep rays and the deck vanishes
|
|
220
|
+
* directly overhead. The stable form below is not optional.
|
|
221
|
+
*/
|
|
222
|
+
int layerRoots(vec3 ro, vec3 rd, float h, out float r1, out float r2){
|
|
223
|
+
float a = u_curvature, b = rd.y, c = ro.y - h;
|
|
224
|
+
r1 = 1e20; r2 = 1e20;
|
|
225
|
+
if (a < 1e-12){
|
|
226
|
+
if (abs(b) < 1e-7) return 0;
|
|
227
|
+
float t = -c / b;
|
|
228
|
+
if (t > 0.0){ r1 = t; return 1; }
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
float disc = b * b - 4.0 * a * c;
|
|
232
|
+
if (disc < 0.0) return 0;
|
|
233
|
+
float sq = sqrt(disc);
|
|
234
|
+
float q = -0.5 * (b + (b >= 0.0 ? sq : -sq));
|
|
235
|
+
if (abs(q) < 1e-20) return 0;
|
|
236
|
+
float ta = q / a;
|
|
237
|
+
float tb = c / q;
|
|
238
|
+
float lo = min(ta, tb), hi = max(ta, tb);
|
|
239
|
+
int n = 0;
|
|
240
|
+
if (lo > 0.0){ r1 = lo; n = 1; }
|
|
241
|
+
if (hi > 0.0){ if (n == 0){ r1 = hi; n = 1; } else { r2 = hi; n = 2; } }
|
|
242
|
+
return n;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
bool layerRange(vec3 ro, vec3 rd, float hb, float ht, out float t0, out float t1){
|
|
246
|
+
float b1, b2, c1, c2;
|
|
247
|
+
int nb = layerRoots(ro, rd, hb, b1, b2);
|
|
248
|
+
int nt = layerRoots(ro, rd, ht, c1, c2);
|
|
249
|
+
float ts[4]; int n = 0;
|
|
250
|
+
if (nb > 0){ ts[n] = b1; n++; }
|
|
251
|
+
if (nb > 1){ ts[n] = b2; n++; }
|
|
252
|
+
if (nt > 0){ ts[n] = c1; n++; }
|
|
253
|
+
if (nt > 1){ ts[n] = c2; n++; }
|
|
254
|
+
if (n == 0){
|
|
255
|
+
if (ro.y > hb && ro.y < ht){ t0 = 0.0; t1 = 120000.0; return true; }
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
for (int i = 0; i < 4; i++)
|
|
259
|
+
for (int j = i + 1; j < 4; j++){
|
|
260
|
+
if (j < n && ts[j] < ts[i]){ float tmp = ts[i]; ts[i] = ts[j]; ts[j] = tmp; }
|
|
261
|
+
}
|
|
262
|
+
bool inside = (ro.y > hb && ro.y < ht);
|
|
263
|
+
if (inside){ t0 = 0.0; t1 = ts[0]; }
|
|
264
|
+
else { t0 = ts[0]; t1 = (n > 1) ? ts[n - 1] : ts[0] + 6000.0; }
|
|
265
|
+
return t1 > t0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
bool sphereRange(vec3 ro, vec3 rd, vec3 c, float r, out float t0, out float t1){
|
|
269
|
+
vec3 oc = ro - c;
|
|
270
|
+
float b = dot(oc, rd);
|
|
271
|
+
float cc = dot(oc, oc) - r * r;
|
|
272
|
+
float disc = b * b - cc;
|
|
273
|
+
if (disc < 0.0) return false;
|
|
274
|
+
float sq = sqrt(disc);
|
|
275
|
+
t0 = max(-b - sq, 0.0);
|
|
276
|
+
t1 = -b + sq;
|
|
277
|
+
return t1 > t0;
|
|
278
|
+
}
|
|
279
|
+
`;
|
|
280
|
+
export const cloudLighting = /* glsl */ `
|
|
281
|
+
uniform float u_sigmaT, u_g1, u_g2;
|
|
282
|
+
uniform float u_msExtinction, u_msEnergy, u_msPhase;
|
|
283
|
+
uniform float u_powder, u_silver, u_ambient;
|
|
284
|
+
uniform vec3 u_skyAmbient, u_groundAmbient;
|
|
285
|
+
uniform int u_lightSteps;
|
|
286
|
+
uniform float u_lightStep;
|
|
287
|
+
|
|
288
|
+
float hg(float mu, float g){
|
|
289
|
+
float g2 = g * g;
|
|
290
|
+
return (1.0 - g2) / (4.0 * PI * pow(max(1.0 + g2 - 2.0 * g * mu, 1e-4), 1.5));
|
|
291
|
+
}
|
|
292
|
+
/** Forward lobe carries the silver lining; the small backward lobe is the glow you get
|
|
293
|
+
* when the sun is behind you. A single lobe cannot do both. */
|
|
294
|
+
float dualLobe(float mu, float g1, float g2){
|
|
295
|
+
return mix(hg(mu, g1), hg(mu, -abs(g2)), 0.42);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const vec3 kCone[6] = vec3[6](
|
|
299
|
+
vec3( 0.38, 0.35, 0.14), vec3(-0.20, 0.60,-0.30), vec3(-0.50,-0.20, 0.40),
|
|
300
|
+
vec3( 0.10,-0.55,-0.35), vec3( 0.55, 0.10,-0.55), vec3(-0.15, 0.25, 0.62)
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Six exponentially growing samples toward the sun, spread inside a cone so the
|
|
305
|
+
* self-shadow is soft instead of banded, plus one long-range probe for distant occluders.
|
|
306
|
+
* The step is a FRACTION of cloud depth, not metres: the same absolute value is wrong
|
|
307
|
+
* for an 800 m deck and an 8 km tower.
|
|
308
|
+
*/
|
|
309
|
+
float lightMarch(vec3 p, float rayT){
|
|
310
|
+
float thick = (u_host < 0.5) ? (2.0 * u_radius) : max(u_layerTop - u_layerBottom, 1.0);
|
|
311
|
+
float st = u_lightStep * thick;
|
|
312
|
+
float acc = 0.0;
|
|
313
|
+
float t = 0.0;
|
|
314
|
+
for (int i = 0; i < 6; i++){
|
|
315
|
+
if (i >= u_lightSteps) break;
|
|
316
|
+
vec3 sp = p + u_sunDir * (t + st * 0.5) + kCone[i] * (t * 0.38);
|
|
317
|
+
acc += cloudDensityCheap(sp, heightAt(sp, rayT)) * st;
|
|
318
|
+
t += st;
|
|
319
|
+
st *= 1.62;
|
|
320
|
+
}
|
|
321
|
+
vec3 fp = p + u_sunDir * (t * 2.6);
|
|
322
|
+
acc += cloudDensityCheap(fp, heightAt(fp, rayT)) * st * 2.0;
|
|
323
|
+
return acc;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Wrenninge / Hillaire multiple-scattering octaves: each octave scales extinction,
|
|
328
|
+
* energy and phase anisotropy down. A real cumulus has optical depth 5-40 and stays
|
|
329
|
+
* white only because photons scatter hundreds of times; first-order exp(-tau) alone
|
|
330
|
+
* renders it black. This approximation is the single biggest difference between a
|
|
331
|
+
* physically-grounded cloud and a generic one.
|
|
332
|
+
*/
|
|
333
|
+
vec3 scatterEnergy(float lightD, float mu){
|
|
334
|
+
vec3 L = vec3(0.0);
|
|
335
|
+
float a = 1.0, b = 1.0, c = 1.0;
|
|
336
|
+
for (int n = 0; n < 3; n++){
|
|
337
|
+
float ph = dualLobe(mu, u_g1 * c, u_g2 * c);
|
|
338
|
+
L += vec3(b * ph * exp(-lightD * u_sigmaT * a));
|
|
339
|
+
a *= u_msExtinction; b *= u_msEnergy; c *= u_msPhase;
|
|
340
|
+
}
|
|
341
|
+
L += vec3(u_silver * pow(saturate(mu), 8.0) * exp(-lightD * u_sigmaT * 0.35));
|
|
342
|
+
return L;
|
|
343
|
+
}
|
|
344
|
+
`;
|
|
345
|
+
/** Everything a fragment shader needs to evaluate the density field. */
|
|
346
|
+
export const cloudField = [cloudCommon, skyLib, cloudUniforms, cloudDensity, cloudGeometry].join('\n');
|
|
347
|
+
/** The full model, including scattering. */
|
|
348
|
+
export const cloudFull = [cloudField, cloudLighting].join('\n');
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landscape GLSL — heightmap displacement, splat resolve, and the surface
|
|
3
|
+
* sampling that turns eight tiling materials into ground.
|
|
4
|
+
*
|
|
5
|
+
* Injected into a stock `MeshStandardMaterial` through `onBeforeCompile`, so
|
|
6
|
+
* every one of these snippets has to coexist with three's own chunks. Two
|
|
7
|
+
* consequences worth knowing before editing:
|
|
8
|
+
*
|
|
9
|
+
* - **three r163+ compiles every material as `#version 300 es`** and prepends
|
|
10
|
+
* `#define varying in/out`, `#define texture2D texture` and a `gl_FragColor`
|
|
11
|
+
* alias. So `sampler2DArray` and `textureGrad` are available with no
|
|
12
|
+
* `glslVersion` override — and setting `glslVersion: GLSL3` would REMOVE the
|
|
13
|
+
* `gl_FragColor` alias and break the stock chunks. Write GLSL1-flavoured code
|
|
14
|
+
* and let the prefix do the conversion.
|
|
15
|
+
* - Anything declared here shares a namespace with `<common>`. Everything is
|
|
16
|
+
* `ls`-prefixed or `u_ls`-prefixed for that reason.
|
|
17
|
+
*
|
|
18
|
+
* A backtick inside one of these template literals terminates the string. It has
|
|
19
|
+
* cost this codebase a debug cycle twice.
|
|
20
|
+
*/
|
|
21
|
+
/** Uniforms and helpers the vertex stage needs. Shared with the depth material,
|
|
22
|
+
* which must displace identically or the landscape's shadow will not match it. */
|
|
23
|
+
export declare const landscapeVertexPars = "\nuniform sampler2D u_lsHeight;\nuniform vec2 u_lsHeightSize;\nuniform float u_lsSmoothness;\nuniform float u_lsElevation;\nuniform float u_lsElevationBias;\nuniform float u_lsSize;\n\nvarying vec3 vLsWorld;\nvarying vec3 vLsNormalW;\nvarying float vLsHeight01;\n\n// B-spline bicubic through four bilinear taps. What it is for is 8-bit\n// heightmaps: 256 discrete levels over a kilometre of relief is a staircase, and\n// a bilinear tap keeps every step. Widening the footprint by u_lsSmoothness\n// turns the steps into slopes.\nvec4 lsCubic(float v) {\n vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;\n vec4 s = n * n * n;\n float x = s.x;\n float y = s.y - 4.0 * s.x;\n float z = s.z - 4.0 * s.y + 6.0 * s.x;\n float w = 6.0 - x - y - z;\n return vec4(x, y, z, w) * (1.0 / 6.0);\n}\n\nfloat lsHeightBicubic(vec2 uv, vec2 texSize) {\n vec2 invSize = 1.0 / texSize;\n vec2 coord = uv * texSize - 0.5;\n vec2 f = fract(coord);\n coord -= f;\n\n vec4 xc = lsCubic(f.x);\n vec4 yc = lsCubic(f.y);\n vec4 c = coord.xxyy + vec2(-0.5, 1.5).xyxy;\n vec4 s = vec4(xc.xz + xc.yw, yc.xz + yc.yw);\n vec4 o = (c + vec4(xc.yw, yc.yw) / s) * invSize.xxyy;\n\n float s0 = texture2D(u_lsHeight, o.xz).r;\n float s1 = texture2D(u_lsHeight, o.yz).r;\n float s2 = texture2D(u_lsHeight, o.xw).r;\n float s3 = texture2D(u_lsHeight, o.yw).r;\n float sx = s.x / (s.x + s.y);\n float sy = s.z / (s.z + s.w);\n return mix(mix(s3, s2, sx), mix(s1, s0, sx), sy);\n}\n\n// Normalised height at a UV. NOT wrapped in abs() \u2014 the reference does that and\n// it folds every below-zero basin upward, which matters the moment there is\n// water in the scene.\n// Single exit, and the result is initialised at its declaration. Every function\n// in this file is written that way deliberately: ANGLE translates GLSL to HLSL on\n// Windows and rewrites an early return into an assignment plus a jump, which\n// D3D's compiler then reports as X4000 \"use of potentially uninitialized\n// variable\" naming the function's own result. It is a warning rather than an\n// error and the shader links either way, but the pattern is worth avoiding \u2014 the\n// analysis that produces the warning is the same one that decides what to\n// initialise.\nfloat lsHeightAt(vec2 uv) {\n vec2 c = clamp(uv, 0.0, 1.0);\n float h = 0.0;\n if (u_lsSmoothness > 0.001) {\n h = lsHeightBicubic(c, u_lsHeightSize / max(u_lsSmoothness, 0.001));\n } else {\n h = texture2D(u_lsHeight, c).r;\n }\n return h;\n}\n\nfloat lsWorldHeight(vec2 uv) {\n return lsHeightAt(uv) * u_lsElevation + u_lsElevationBias;\n}\n\n// Surface normal by central difference at the heightmap's real texel size and\n// the real elevation scale. The reference hardcodes both (1.0/1024.0 and 120.0),\n// so its normals are only correct for one map at one scale, and its smoothed\n// variant returns abs(cross(...)) \u2014 a normal with the sign stripped, which is\n// why that API needs per-surface normal-flipping flags to paper over it.\nvec3 lsSurfaceNormal(vec2 uv) {\n vec2 texel = 1.0 / u_lsHeightSize;\n float hL = lsWorldHeight(uv - vec2(texel.x, 0.0));\n float hR = lsWorldHeight(uv + vec2(texel.x, 0.0));\n float hD = lsWorldHeight(uv - vec2(0.0, texel.y));\n float hU = lsWorldHeight(uv + vec2(0.0, texel.y));\n // World distance spanned by the two taps on each axis.\n float dx = 2.0 * texel.x * u_lsSize;\n float dz = 2.0 * texel.y * u_lsSize;\n return normalize(vec3(-(hR - hL) * dz, dx * dz, -(hU - hD) * dx));\n}\n";
|
|
24
|
+
/** Replaces `<begin_vertex>`. Displaces along local +Y and fills the varyings. */
|
|
25
|
+
export declare const landscapeVertexBody = "\nvec3 transformed = vec3(position);\nfloat lsH01 = lsHeightAt(uv);\ntransformed.y += lsH01 * u_lsElevation + u_lsElevationBias;\nvLsHeight01 = lsH01;\nvLsWorld = (modelMatrix * vec4(transformed, 1.0)).xyz;\nvLsNormalW = normalize(mat3(modelMatrix) * lsSurfaceNormal(uv));\n";
|
|
26
|
+
/** Replaces `<beginnormal_vertex>`. The geometry carries a flat up-normal — the
|
|
27
|
+
* shape is entirely in the heightmap — so the real normal is evaluated here and
|
|
28
|
+
* three's lighting sees a correctly shaded surface with no extra chunk. */
|
|
29
|
+
export declare const landscapeNormalBody = "\nvec3 objectNormal = lsSurfaceNormal(uv);\n";
|
|
30
|
+
/**
|
|
31
|
+
* Fragment uniforms, samplers and the surface sampling machinery.
|
|
32
|
+
*
|
|
33
|
+
* Compile-time counts rather than runtime loops: LS_COUNT is the actual surface
|
|
34
|
+
* count and LS_SAMPLES the blend width, so every loop here has constant bounds
|
|
35
|
+
* and unrolls. The reference runs an eight-element insertion sort in a dynamic
|
|
36
|
+
* while loop on every pixel of the screen.
|
|
37
|
+
*/
|
|
38
|
+
export declare const landscapeFragmentPars = "\nuniform sampler2DArray u_lsDiffuse;\nuniform sampler2DArray u_lsNormal; // rgb tangent-space normal, a surface height\nuniform sampler2D u_lsSplat0;\nuniform sampler2D u_lsSplat1;\nuniform sampler2D u_lsAo;\n// The height field as the fragment stage sees it: RG the one-texel gradient,\n// biased by u_lsGradScale, B the height. One fetch replaces two interpolated\n// varyings that were quantising both the splat and the shading to the\n// triangulation. See buildFieldTexture.\nuniform sampler2D u_lsField;\nuniform float u_lsGradScale;\nuniform vec2 u_lsFieldTexel;\n\n// repeat, normalStrength, roughness, heightBlend\nuniform vec4 u_lsSurfA[LS_COUNT];\n// tint.rgb, saturation\nuniform vec4 u_lsSurfB[LS_COUNT];\n// triplanar, flipNormalY, metalness, aperiodic\nuniform vec4 u_lsSurfC[LS_COUNT];\n// mean colour per diffuse layer, for the variance-preserving stochastic blend\nuniform vec3 u_lsSurfMean[LS_COUNT];\n\nuniform vec4 u_lsAuto; // bandWidth, slopeStart, slopeEnd, noise\nuniform float u_lsAutoBands[LS_COUNT];\nuniform float u_lsNoiseScale; // 1 / noiseSize, world units\nuniform float u_lsMacro;\nuniform float u_lsMacroScale;\nuniform float u_lsAoIntensity;\nuniform float u_lsSize;\nuniform float u_lsElevation;\n\nvarying vec3 vLsWorld;\nvarying vec3 vLsNormalW;\nvarying float vLsHeight01;\n\n// Arithmetic hash. Never fract(sin(dot(...))): four hashes an octave with two\n// sines each is most of a frame on its own, and this codebase has paid for that\n// lesson once already in the terrain.\nvec2 lsHash2(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973));\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfloat lsHash1(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * 0.1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfloat lsValueNoise(vec2 p) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n vec2 u = f * f * (3.0 - 2.0 * f);\n return mix(\n mix(lsHash1(i), lsHash1(i + vec2(1.0, 0.0)), u.x),\n mix(lsHash1(i + vec2(0.0, 1.0)), lsHash1(i + vec2(1.0, 1.0)), u.x),\n u.y\n );\n}\n\n#ifdef LS_STOCHASTIC\n// Triangle-grid stochastic tiling. Three taps at hashed offsets, weighted by the\n// barycentric coordinate of the point in its cell, so the repeat grid stops\n// existing. The blend is variance-preserving: a straight weighted sum of three\n// samples pulls every pixel toward the texture's mean and visibly washes out\n// contrast, which is what the reference's two-tap smoothstep blend does. Dividing\n// the deviation by sqrt(sum(w^2)) restores it. The mean comes from the CPU, where\n// the pixels were already in hand when the array was packed.\n// Cell size, in texture tiles. The point of stochastic tiling is to randomise\n// PATCHES of texture, so a cell wants to be a tile or two across: smaller and it\n// scrambles within a tile, which shows as noise rather than as break-up, and\n// goes sub-pixel almost immediately.\n//\n// The first version used the bare 3.4641 grid on already-tiled UVs, so a cell was\n// a THIRD of a tile \u2014 a fraction of a metre on the ground. It went sub-pixel at\n// any distance, the fade dropped it back to plain sampling, and the repeat grid\n// came straight back at exactly the range it exists to hide.\n#define LS_CELL_TILES 1.5\n\nvoid lsTriGrid(vec2 uv, out vec3 w, out vec2 v1, out vec2 v2, out vec2 v3) {\n w = vec3(0.0);\n v1 = vec2(0.0);\n v2 = vec2(0.0);\n v3 = vec2(0.0);\n vec2 skewed = mat2(1.0, 0.0, -0.57735027, 1.15470054) * (uv * (3.4641016 / LS_CELL_TILES));\n vec2 base = floor(skewed);\n vec3 t = vec3(fract(skewed), 0.0);\n t.z = 1.0 - t.x - t.y;\n if (t.z > 0.0) {\n w = vec3(t.z, t.y, t.x);\n v1 = base;\n v2 = base + vec2(0.0, 1.0);\n v3 = base + vec2(1.0, 0.0);\n } else {\n w = vec3(-t.z, 1.0 - t.y, 1.0 - t.x);\n v1 = base + vec2(1.0, 1.0);\n v2 = base + vec2(1.0, 0.0);\n v3 = base + vec2(0.0, 1.0);\n }\n}\n\nvec4 lsSampleStochastic(sampler2DArray tex, vec2 uv, float layer, vec3 mean) {\n vec2 dx = dFdx(uv);\n vec2 dy = dFdy(uv);\n vec4 result = vec4(0.0);\n\n // Stochastic cells per pixel. THIS FADE IS NOT OPTIONAL. A surface tiled 220\n // times across a two-kilometre landscape has cells a metre or two wide, which\n // is roughly a pixel by the time the ground is a few hundred metres away: the\n // three taps then land in unrelated parts of the texture, the variance\n // correction amplifies the difference, and the far field turns into a moire of\n // black speckle. Rendered, that looked exactly like shadow acne and was not \u2014\n // turning shadows off changed nothing and turning stochastic sampling off\n // fixed it. Past the fade this is a plain sample, which is what the mip chain\n // is for. Gradients are explicit throughout, so the branch is safe.\n float footprint = max(length(dx), length(dy)) * (3.4641016 / LS_CELL_TILES);\n float fade = smoothstep(0.45, 1.10, footprint);\n\n if (fade > 0.995) {\n result = textureGrad(tex, vec3(uv, layer), dx, dy);\n } else {\n vec3 w;\n vec2 v1, v2, v3;\n lsTriGrid(uv, w, v1, v2, v3);\n vec4 c1 = textureGrad(tex, vec3(uv + lsHash2(v1), layer), dx, dy);\n vec4 c2 = textureGrad(tex, vec3(uv + lsHash2(v2), layer), dx, dy);\n vec4 c3 = textureGrad(tex, vec3(uv + lsHash2(v3), layer), dx, dy);\n vec4 m = vec4(mean, 0.5);\n vec4 d = w.x * (c1 - m) + w.y * (c2 - m) + w.z * (c3 - m);\n // Clamped: the correction multiplies the deviation by up to 1.73, which\n // takes a dark texel below zero and a bright one past one.\n result = clamp(m + d * inversesqrt(max(dot(w, w), 1e-4)), 0.0, 1.0);\n if (fade >= 0.005) {\n result = mix(result, textureGrad(tex, vec3(uv, layer), dx, dy), fade);\n }\n }\n return result;\n}\n#endif\n\n// Per surface, not per material: stochastic tiling is three fetches instead of\n// one, and only the surfaces a camera gets close to need it. The whole path\n// compiles out when no surface asks for it.\nvec4 lsSampleArray(sampler2DArray tex, vec2 uv, float layer, vec3 mean, float stochastic) {\n vec4 result = vec4(0.0);\n#ifdef LS_STOCHASTIC\n if (stochastic > 0.5) {\n result = lsSampleStochastic(tex, uv, layer, mean);\n } else {\n result = texture(tex, vec3(uv, layer));\n }\n#else\n result = texture(tex, vec3(uv, layer));\n#endif\n return result;\n}\n\n#ifdef LS_TRIPLANAR\n// Biplanar: the two strongest axes, not three. The third contributes a fraction\n// of a percent on any surface that is not an exact 45 degree corner, and it costs\n// a third of the fetches. The reference ships biplanar samplers and then calls\n// the triplanar path anyway.\nvec4 lsSampleWorld(sampler2DArray tex, vec3 wp, vec3 n, float scale, float layer, vec3 mean, float stochastic) {\n vec3 an = abs(n);\n an = an * an * an;\n an /= max(an.x + an.y + an.z, 1e-4);\n float lo = min(an.x, min(an.y, an.z));\n vec3 w = max(an - lo, 0.0);\n w /= max(w.x + w.y + w.z, 1e-4);\n vec4 c = vec4(0.0);\n if (w.x > 0.002) c += w.x * lsSampleArray(tex, wp.zy * scale, layer, mean, stochastic);\n if (w.y > 0.002) c += w.y * lsSampleArray(tex, wp.xz * scale, layer, mean, stochastic);\n if (w.z > 0.002) c += w.z * lsSampleArray(tex, wp.xy * scale, layer, mean, stochastic);\n return c;\n}\n#endif\n\n// Saturation as a luma lerp. The reference runs a full RGB->HSV->RGB round trip\n// per sampled surface per pixel for this.\nvec3 lsSaturate(vec3 c, float s) {\n return mix(vec3(dot(c, vec3(0.2126, 0.7152, 0.0722))), c, s);\n}\n";
|
|
39
|
+
/**
|
|
40
|
+
* The resolve, called from `<map_fragment>`. Fills `lsAlbedo`, `lsNormalW`,
|
|
41
|
+
* `lsRoughness` and `lsMetalness`, which the roughness, metalness and normal
|
|
42
|
+
* chunks then read.
|
|
43
|
+
*
|
|
44
|
+
* Order matters: the surface heights come out of the normal array's alpha
|
|
45
|
+
* channel, so the height blend is computed before a single diffuse texel is
|
|
46
|
+
* fetched, and the diffuse pass already knows its final weights.
|
|
47
|
+
*/
|
|
48
|
+
export declare const landscapeFragmentBody = "\nvec2 lsUv = vUv;\n\n// PER FRAGMENT, not per vertex. The first version read an interpolated height\n// and an interpolated normal out of varyings, and both are linear across a\n// triangle: every splat threshold came out as a straight iso-line kinking at\n// triangle edges \u2014 big polygonal patches of flat colour \u2014 and the shading normal\n// was faceted to match. One fetch of the field texture fixes both, and makes the\n// mesh resolution invisible to the shading: the ground reads smooth however\n// coarse the triangles under it are.\nvec4 lsField = texture2D(u_lsField, lsUv);\nfloat lsHeight01 = lsField.b;\n\n// Gradient back to a world-space normal. dx and dz are the world distance one\n// field texel spans, so the normal is correct at any size and elevation rather\n// than only at the one it was authored for.\nvec2 lsGrad = (lsField.rg * 2.0 - 1.0) * u_lsGradScale;\nfloat lsDx = u_lsFieldTexel.x * u_lsSize;\nfloat lsDz = u_lsFieldTexel.y * u_lsSize;\nvec3 lsN = normalize(vec3(\n -lsGrad.x * u_lsElevation * lsDz,\n lsDx * lsDz,\n -lsGrad.y * u_lsElevation * lsDx\n));\n\n// Slope as a fraction of vertical rather than 1 - n.y: the naive form is badly\n// compressed at the angles that matter, so every threshold crowds into the top\n// of its range and rock never appears where it should. This way 0.5 IS 45\n// degrees. Same reasoning as the terrain material.\nfloat lsSlope = acos(clamp(lsN.y, -1.0, 1.0)) * 0.63661977;\n\n// ---- surface weights ------------------------------------------------------\nfloat lsW[LS_COUNT];\n\n#ifdef LS_AUTO\n// Generated from the heightmap's own height and slope, so a bare heightmap with\n// no authored splat map still renders as ground. Same rule the procedural\n// terrain shades by: everything by height, rock by slope, rock applied last.\nfloat lsHJit = lsHeight01 + (lsValueNoise(vLsWorld.xz * u_lsNoiseScale) - 0.5) * u_lsAuto.w;\nfloat lsRock = smoothstep(u_lsAuto.y, u_lsAuto.z, lsSlope);\nfor (int i = 0; i < LS_COUNT; i++) {\n float d = abs(lsHJit - u_lsAutoBands[i]);\n lsW[i] = max(1.0 - d / max(u_lsAuto.x, 1e-3), 0.0);\n}\n// The last surface is the rock layer.\nfor (int i = 0; i < LS_COUNT - 1; i++) lsW[i] *= (1.0 - lsRock);\nlsW[LS_COUNT - 1] = max(lsW[LS_COUNT - 1], lsRock);\n#else\nvec4 lsS0 = texture2D(u_lsSplat0, lsUv);\nlsW[0] = lsS0.r;\n#if LS_COUNT > 1\nlsW[1] = lsS0.g;\n#endif\n#if LS_COUNT > 2\nlsW[2] = lsS0.b;\n#endif\n#if LS_COUNT > 3\nlsW[3] = lsS0.a;\n#endif\n#if LS_COUNT > 4\nvec4 lsS1 = texture2D(u_lsSplat1, lsUv);\nlsW[4] = lsS1.r;\n#endif\n#if LS_COUNT > 5\nlsW[5] = lsS1.g;\n#endif\n#if LS_COUNT > 6\nlsW[6] = lsS1.b;\n#endif\n#if LS_COUNT > 7\nlsW[7] = lsS1.a;\n#endif\n#endif\n\n// ---- pick the heaviest LS_SAMPLES ----------------------------------------\n// Constant bounds both ways, so this unrolls. No sort, no swaps, no dynamic\n// loop: LS_SAMPLES passes each taking the largest remaining weight.\nint lsIdx[LS_SAMPLES];\nfloat lsWt[LS_SAMPLES];\nfor (int s = 0; s < LS_SAMPLES; s++) {\n int best = 0;\n float bw = -1.0;\n for (int i = 0; i < LS_COUNT; i++) {\n if (lsW[i] > bw) { bw = lsW[i]; best = i; }\n }\n lsIdx[s] = best;\n lsWt[s] = max(bw, 0.0);\n lsW[best] = -1.0;\n}\n\n// ---- normals and heights, then the height blend ---------------------------\nvec4 lsNrm[LS_SAMPLES];\nfloat lsBlendRange = 1.0;\nfloat lsPeak = -1e9;\nfloat lsV[LS_SAMPLES];\n\nfor (int s = 0; s < LS_SAMPLES; s++) {\n int i = lsIdx[s];\n float repeat = u_lsSurfA[i].x;\n vec2 uv = lsUv * repeat;\n vec3 mean = vec3(0.5, 0.5, 1.0);\n float stoch = u_lsSurfC[i].w;\n vec4 n;\n #ifdef LS_TRIPLANAR\n if (u_lsSurfC[i].x > 0.5) {\n n = lsSampleWorld(u_lsNormal, vLsWorld, lsN, repeat / max(u_lsSize, 1e-3), float(i), mean, stoch);\n } else {\n n = lsSampleArray(u_lsNormal, uv, float(i), mean, stoch);\n }\n #else\n n = lsSampleArray(u_lsNormal, uv, float(i), mean, stoch);\n #endif\n lsNrm[s] = n;\n\n // Height blending. Two surfaces meeting no longer cross-fade: the one whose\n // own height map is higher at this texel wins, so gravel comes through grass\n // along the gravel's grain. The reference loads these height maps and never\n // samples them.\n float hb = u_lsSurfA[i].w;\n lsBlendRange = min(lsBlendRange, mix(1.0, 0.04, hb));\n lsV[s] = lsWt[s] + n.a * hb;\n lsPeak = max(lsPeak, lsV[s]);\n}\n\nfloat lsSum = 0.0;\nfor (int s = 0; s < LS_SAMPLES; s++) {\n lsWt[s] = max(lsV[s] - (lsPeak - lsBlendRange), 0.0);\n lsSum += lsWt[s];\n}\nlsSum = max(lsSum, 1e-4);\nfor (int s = 0; s < LS_SAMPLES; s++) lsWt[s] /= lsSum;\n\n// ---- albedo, normal, roughness -------------------------------------------\nvec3 lsAlbedo = vec3(0.0);\nvec3 lsNsum = vec3(0.0);\nfloat lsRoughness = 0.0;\nfloat lsMetalness = 0.0;\n\nfor (int s = 0; s < LS_SAMPLES; s++) {\n int i = lsIdx[s];\n float w = lsWt[s];\n float repeat = u_lsSurfA[i].x;\n vec2 uv = lsUv * repeat;\n\n float stoch = u_lsSurfC[i].w;\n vec4 d;\n #ifdef LS_TRIPLANAR\n if (u_lsSurfC[i].x > 0.5) {\n d = lsSampleWorld(u_lsDiffuse, vLsWorld, lsN, repeat / max(u_lsSize, 1e-3), float(i), u_lsSurfMean[i], stoch);\n } else {\n d = lsSampleArray(u_lsDiffuse, uv, float(i), u_lsSurfMean[i], stoch);\n }\n #else\n d = lsSampleArray(u_lsDiffuse, uv, float(i), u_lsSurfMean[i], stoch);\n #endif\n\n vec3 col = lsSaturate(d.rgb, u_lsSurfB[i].w) * u_lsSurfB[i].rgb;\n lsAlbedo += col * w;\n\n // Whiteout-style blend: sum the tangent-space XY, keep Z positive, normalise\n // once at the end. The reference slerps every surface against a flat normal\n // and then RNM-blends the result \u2014 an acos and three sines per sampled\n // surface, per pixel.\n vec3 m = lsNrm[s].rgb * 2.0 - 1.0;\n m.y *= u_lsSurfC[i].y;\n m.xy *= u_lsSurfA[i].y;\n lsNsum += m * w;\n\n lsRoughness += u_lsSurfA[i].z * w;\n lsMetalness += u_lsSurfC[i].z * w;\n}\n\n// Large-scale albedo break-up, generated rather than sampled. Without it a splat\n// layer is one flat colour across hundreds of metres and reads as untextured;\n// the reference ships a 2.7 MB texture to do this.\nif (u_lsMacro > 0.001) {\n float macro = lsValueNoise(vLsWorld.xz * u_lsMacroScale) * 0.6\n + lsValueNoise(vLsWorld.xz * u_lsMacroScale * 2.7) * 0.4;\n lsAlbedo *= mix(1.0, 0.6 + macro * 0.8, u_lsMacro);\n}\n\nvec3 lsMapN = normalize(vec3(lsNsum.xy, max(1.0 - length(lsNsum.xy) * 0.5, 0.05)));\n// Tangent frame for a heightfield with axis-aligned UVs: world X, orthogonalised\n// against the surface normal. No tangent attribute and no derivative-based TBN\n// needed, because the mapping is known.\nvec3 lsT = normalize(vec3(1.0, 0.0, 0.0) - lsN * lsN.x);\nvec3 lsB = cross(lsN, lsT);\nvec3 lsNormalW = normalize(lsT * lsMapN.x + lsB * lsMapN.y + lsN * lsMapN.z);\n";
|
|
49
|
+
//# sourceMappingURL=landscapeCore.glsl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"landscapeCore.glsl.d.ts","sourceRoot":"","sources":["../../../src/shaders/glsl/landscapeCore.glsl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;mFACmF;AACnF,eAAO,MAAM,mBAAmB,u6GAyF/B,CAAA;AAED,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,uRAO/B,CAAA;AAED;;4EAE4E;AAC5E,eAAO,MAAM,mBAAmB,iDAE/B,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,23OAwLjC,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,mnNA4LjC,CAAA"}
|