@babylonjs-toolkit/next 9.34.1 → 9.34.100
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/lib/project.d.ts +1 -732
- package/lib/project.d.ts.map +1 -1
- package/lib/project.js +2 -3212
- package/lib/scenemanager.d.ts +453 -6
- package/lib/scenemanager.d.ts.map +1 -1
- package/lib/scenemanager.js +2881 -339
- package/package.json +1 -1
package/lib/scenemanager.js
CHANGED
|
@@ -143,7 +143,7 @@ import { UnityScrollBar } from './unityscrollbar';
|
|
|
143
143
|
import { UnityDropdownMenu } from './unitydropdownmenu';
|
|
144
144
|
import { RectAreaLight } from '@babylonjs/core/Lights/rectAreaLight';
|
|
145
145
|
export class SceneManager {
|
|
146
|
-
static get Version() { return "9.34.
|
|
146
|
+
static get Version() { return "9.34.100 - R1"; }
|
|
147
147
|
static get Copyright() { return "All rights reserved (c) 2024 Mackey Kinard"; }
|
|
148
148
|
static IsPrecompilingShaders() {
|
|
149
149
|
return SceneManager.PrecompileShaders !== false;
|
|
@@ -338,6 +338,11 @@ export class SceneManager {
|
|
|
338
338
|
await assetsManager.loadAsync();
|
|
339
339
|
}
|
|
340
340
|
static ShowLoadingScreen(engine, hideLoadingUIWithEngine = true, defaultLoadingUIMarginTop = "150px") {
|
|
341
|
+
const pageCover = (LoadingScreenStages != null) ? LoadingScreenStages.FindPageCover() : null;
|
|
342
|
+
if (pageCover != null) {
|
|
343
|
+
LoadingScreenStages.AttachScreen(pageCover, defaultLoadingUIMarginTop);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
341
346
|
if (engine.loadingScreen != null) {
|
|
342
347
|
SceneManager._HideLoadingScreen = engine.loadingScreen.hideLoadingUI.bind(engine.loadingScreen);
|
|
343
348
|
if (hideLoadingUIWithEngine === false)
|
|
@@ -348,9 +353,20 @@ export class SceneManager {
|
|
|
348
353
|
if (anyLoadingScreen._loadingTextDiv != null && anyLoadingScreen._loadingTextDiv.style != null) {
|
|
349
354
|
anyLoadingScreen._loadingTextDiv.style.marginTop = defaultLoadingUIMarginTop;
|
|
350
355
|
}
|
|
356
|
+
if (LoadingScreenStages != null)
|
|
357
|
+
LoadingScreenStages.Attach(anyLoadingScreen, defaultLoadingUIMarginTop);
|
|
351
358
|
}
|
|
352
359
|
}
|
|
353
360
|
static HideLoadingScreen(engine, fade = true) {
|
|
361
|
+
if (LoadingScreenStages != null) {
|
|
362
|
+
const unrevealed = LoadingScreenStages.SceneToReveal(engine);
|
|
363
|
+
if (unrevealed != null) {
|
|
364
|
+
SceneManager._FadeLoadingScreen = fade;
|
|
365
|
+
SceneManager.HideSplashScreen(unrevealed);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
LoadingScreenStages.Detach();
|
|
369
|
+
}
|
|
354
370
|
if (engine.loadingScreen != null) {
|
|
355
371
|
if (fade === false) {
|
|
356
372
|
const anyLoadingScreen = engine.loadingScreen;
|
|
@@ -470,9 +486,11 @@ export class SceneManager {
|
|
|
470
486
|
});
|
|
471
487
|
}
|
|
472
488
|
static RevealSplashScreen(scene) {
|
|
489
|
+
const fade = SceneManager._FadeLoadingScreen;
|
|
490
|
+
SceneManager._FadeLoadingScreen = true;
|
|
473
491
|
if (scene != null && !scene.isDisposed) {
|
|
474
492
|
try {
|
|
475
|
-
SceneManager.HideLoadingScreen(scene.getEngine());
|
|
493
|
+
SceneManager.HideLoadingScreen(scene.getEngine(), fade);
|
|
476
494
|
}
|
|
477
495
|
catch (e) {
|
|
478
496
|
console.warn(e);
|
|
@@ -490,6 +508,16 @@ export class SceneManager {
|
|
|
490
508
|
console.warn(e);
|
|
491
509
|
}
|
|
492
510
|
}
|
|
511
|
+
else if (LoadingScreenStages != null && LoadingScreenStages.IsShowing()) {
|
|
512
|
+
const engine = (scene != null) ? scene.getEngine() : EngineStore.LastCreatedEngine;
|
|
513
|
+
try {
|
|
514
|
+
if (engine != null)
|
|
515
|
+
SceneManager.HideLoadingScreen(engine, fade);
|
|
516
|
+
}
|
|
517
|
+
catch (e) {
|
|
518
|
+
console.warn(e);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
493
521
|
if (typeof document === "undefined")
|
|
494
522
|
return;
|
|
495
523
|
try {
|
|
@@ -630,7 +658,7 @@ export class SceneManager {
|
|
|
630
658
|
if (splash == null)
|
|
631
659
|
splash = document.getElementById("xbabylonjsLoadingDiv");
|
|
632
660
|
if (splash == null)
|
|
633
|
-
return
|
|
661
|
+
return (LoadingScreenStages != null && LoadingScreenStages.IsShowing());
|
|
634
662
|
const style = window.getComputedStyle(splash);
|
|
635
663
|
return style.display !== "none" && style.visibility !== "hidden" && splash.style.opacity !== "0";
|
|
636
664
|
}
|
|
@@ -5511,6 +5539,7 @@ SceneManager.AllLayerMask = 0xFFFFFFFF;
|
|
|
5511
5539
|
SceneManager.DefaultLayerMask = 0x0FFFFFFF;
|
|
5512
5540
|
SceneManager.HiddenLayerMask = 0x10000000;
|
|
5513
5541
|
SceneManager._HideLoadingScreen = null;
|
|
5542
|
+
SceneManager._FadeLoadingScreen = true;
|
|
5514
5543
|
SceneManager.CVTOOLS_NAME = "CVTOOLS_unity_metadata";
|
|
5515
5544
|
SceneManager.CVTOOLS_MESH = "CVTOOLS_babylon_mesh";
|
|
5516
5545
|
SceneManager.CVTOOLS_HAND = "CVTOOLS_left_handed";
|
|
@@ -9169,7 +9198,6 @@ export class CVTOOLS_unity_metadata {
|
|
|
9169
9198
|
return;
|
|
9170
9199
|
const levels = [];
|
|
9171
9200
|
const rendererCounts = [];
|
|
9172
|
-
let multiRenderer = false;
|
|
9173
9201
|
let totalMeshes = 0;
|
|
9174
9202
|
for (let index = 0; index < levelCount; index++) {
|
|
9175
9203
|
const entry = lodentries[index];
|
|
@@ -9183,8 +9211,6 @@ export class CVTOOLS_unity_metadata {
|
|
|
9183
9211
|
meshes.push(lodnode);
|
|
9184
9212
|
}
|
|
9185
9213
|
});
|
|
9186
|
-
if (meshes.length > 1)
|
|
9187
|
-
multiRenderer = true;
|
|
9188
9214
|
totalMeshes += meshes.length;
|
|
9189
9215
|
rendererCounts.push(meshes.length);
|
|
9190
9216
|
levels.push({ distance: thresholds[index], meshes: meshes });
|
|
@@ -9192,14 +9218,15 @@ export class CVTOOLS_unity_metadata {
|
|
|
9192
9218
|
if (totalMeshes === 0)
|
|
9193
9219
|
return;
|
|
9194
9220
|
const fadeMode = UnityLodGroups.ParseFadeMode(metadata.fademode);
|
|
9195
|
-
const
|
|
9221
|
+
const requiresSwitcher = UnityLodGroups.RequiresSwitcher(levels.map((level) => level.meshes), fadeMode);
|
|
9222
|
+
const nativeCandidate = (useDistances === false && requiresSwitcher === false
|
|
9196
9223
|
&& levels[0].meshes.length === 1 && levels[0].meshes[0] instanceof Mesh && !(levels[0].meshes[0] instanceof InstancedMesh));
|
|
9197
9224
|
if (nativeCandidate === true && this._setupNativeCoverageLevelOfDetail(scene, transform, levels, lodBias, metadata) === true) {
|
|
9198
9225
|
}
|
|
9199
9226
|
else if (useDistances === false) {
|
|
9200
9227
|
this._setupLevelOfDetailSwitcher(scene, transform, levels, rendererCounts, UnityLodGroups.MODE_COVERAGE, lodBias, metadata);
|
|
9201
9228
|
}
|
|
9202
|
-
else if (
|
|
9229
|
+
else if (requiresSwitcher === true) {
|
|
9203
9230
|
this._setupLevelOfDetailSwitcher(scene, transform, levels, rendererCounts, UnityLodGroups.MODE_DISTANCE, lodBias, metadata);
|
|
9204
9231
|
}
|
|
9205
9232
|
else {
|
|
@@ -9706,10 +9733,14 @@ export class CVTOOLS_unity_metadata {
|
|
|
9706
9733
|
return { mesh: skyboxMesh, texture: skyboxTexture };
|
|
9707
9734
|
};
|
|
9708
9735
|
const pbSkyClass = HdrpPhysicallyBasedSky;
|
|
9709
|
-
const
|
|
9736
|
+
const physicalSky = PhysicalSkyBlock.SkyOf(metadata);
|
|
9737
|
+
const hdrpSkyType = (physicalSky != null && typeof physicalSky.type === "string") ? physicalSky.type.toLowerCase() : null;
|
|
9710
9738
|
const liveSky = (pbSkyClass != null && hdrpSkyType === "physicallybased")
|
|
9711
9739
|
? pbSkyClass.TryCreate(babylonScene, metadata, root, { rendergroup: rendergroup, bakedSky: hasBakedSkyFaces ? (() => buildBakedSky().mesh) : null })
|
|
9712
9740
|
: null;
|
|
9741
|
+
const volumetricClouds = VolumetricClouds;
|
|
9742
|
+
if (volumetricClouds != null)
|
|
9743
|
+
volumetricClouds.TryCreate(babylonScene, metadata, root);
|
|
9713
9744
|
const graphSky = (liveSky == null && hdrpSkyType !== "none" && typeof skybox.customMaterial === "string" && skybox.bags != null && ShaderGraphSky != null)
|
|
9714
9745
|
? ShaderGraphSky.CreateGraphSkybox(babylonScene, skybox.bags, { size: skysize, rendergroup: rendergroup, skyfog: skyfog, tags: skytags, environment: (skybox.environment == null && babylonScene.environmentTexture == null) })
|
|
9715
9746
|
: null;
|
|
@@ -9816,8 +9847,10 @@ export class CVTOOLS_unity_metadata {
|
|
|
9816
9847
|
const hasDiffuseIbl = Array.isArray(environment.sh) && environment.sh.length === 27;
|
|
9817
9848
|
const useUnitySH = (mode === 0 && hasDiffuseIbl === true);
|
|
9818
9849
|
const environmentLevel = (useUnitySH ? (specIbl * scale * SPECULAR_SCALE) : specIbl * SPECULAR_SCALE) * HdrpRendering.Indirect(babylonScene).reflection;
|
|
9850
|
+
const hasEnvironmentFile = (envinfo != null && envinfo !== "");
|
|
9851
|
+
const ambientScale = LightingConversions.EnvironmentAmbientScale(diffuseIbl, hasEnvironmentFile ? environmentLevel : null, units, HdrpRendering.IsParity(babylonScene));
|
|
9819
9852
|
if (useUnitySH)
|
|
9820
|
-
this.generateSphericalHarmonics(babylonScene, environment.sh,
|
|
9853
|
+
this.generateSphericalHarmonics(babylonScene, environment.sh, ambientScale, hasEnvironmentFile ? environmentLevel : null);
|
|
9821
9854
|
if (envinfo != null && envinfo !== "") {
|
|
9822
9855
|
try {
|
|
9823
9856
|
const root = (this._rootUrl != null) ? this._rootUrl : "/";
|
|
@@ -9925,6 +9958,8 @@ export class CVTOOLS_unity_metadata {
|
|
|
9925
9958
|
}
|
|
9926
9959
|
if (metadata.shaderglobals != null)
|
|
9927
9960
|
ShaderGlobals.LoadSceneGlobals(babylonScene, metadata.shaderglobals);
|
|
9961
|
+
ShaderGlobals.LoadSceneDrivers(babylonScene, metadata.shaderglobaldrivers);
|
|
9962
|
+
ShaderGraphRuntime.RecordExportReport(babylonScene, metadata.exportreport);
|
|
9928
9963
|
if (metadata.lights2d != null && Light2DTexture != null)
|
|
9929
9964
|
Light2DTexture.Load(babylonScene, metadata.lights2d);
|
|
9930
9965
|
if (metadata.renderfeatures != null && ShaderGraphPass != null)
|
|
@@ -15402,6 +15437,8 @@ export class LightingConversions {
|
|
|
15402
15437
|
static UnityLinearLightColor(light) {
|
|
15403
15438
|
if (light == null || light.diffuse == null)
|
|
15404
15439
|
return new Color3(0.0, 0.0, 0.0);
|
|
15440
|
+
if (light.metadata != null && light.metadata.toolkit != null && light.metadata.toolkit.linearDiffuse === true)
|
|
15441
|
+
return light.diffuse.clone();
|
|
15405
15442
|
const rider = (light.metadata != null && light.metadata.toolkit != null) ? light.metadata.toolkit.colorTemperature : null;
|
|
15406
15443
|
const d = light.diffuse;
|
|
15407
15444
|
const channel = (value, factor) => {
|
|
@@ -15414,6 +15451,18 @@ export class LightingConversions {
|
|
|
15414
15451
|
const tb = (rider != null) ? rider.b : 1.0;
|
|
15415
15452
|
return new Color3(channel(d.r, tr), channel(d.g, tg), channel(d.b, tb));
|
|
15416
15453
|
}
|
|
15454
|
+
static LinearizeLightColor(light, scene) {
|
|
15455
|
+
const sceneMetadata = (scene != null && scene.metadata != null) ? scene.metadata.toolkit : null;
|
|
15456
|
+
const toolkit = (light != null && light.metadata != null) ? light.metadata.toolkit : null;
|
|
15457
|
+
if (sceneMetadata == null || toolkit == null || toolkit.linearDiffuse === true)
|
|
15458
|
+
return false;
|
|
15459
|
+
const isLinearProject = sceneMetadata.colorspace != null && ("" + sceneMetadata.colorspace).toLowerCase() === "linear";
|
|
15460
|
+
if (!isLinearProject || HdrpRendering.IsParityExport(sceneMetadata) === true)
|
|
15461
|
+
return false;
|
|
15462
|
+
light.diffuse = LightingConversions.UnityLinearLightColor(light);
|
|
15463
|
+
toolkit.linearDiffuse = true;
|
|
15464
|
+
return true;
|
|
15465
|
+
}
|
|
15417
15466
|
static SetShadowFade(scene, fade) {
|
|
15418
15467
|
if (scene == null || !Array.isArray(fade) || fade.length < 2)
|
|
15419
15468
|
return;
|
|
@@ -15869,6 +15918,10 @@ export class LightingConversions {
|
|
|
15869
15918
|
return Math.max(0.0, reflection);
|
|
15870
15919
|
return Math.min(levelMax, Math.max(0.0, reflection));
|
|
15871
15920
|
}
|
|
15921
|
+
static EnvironmentAmbientScale(ambientIntensity, environmentLevel, units, isParity) {
|
|
15922
|
+
const isNormalised = units === LightingConversions.PhysicalUnits && environmentLevel != null && environmentLevel > 0;
|
|
15923
|
+
return (isNormalised && isParity !== true) ? ambientIntensity / environmentLevel : ambientIntensity;
|
|
15924
|
+
}
|
|
15872
15925
|
static WarnBakedSkyWithoutEnvironment(skybox) {
|
|
15873
15926
|
if (skybox == null || skybox.bakedsky !== true || skybox.environment != null)
|
|
15874
15927
|
return false;
|
|
@@ -16290,6 +16343,167 @@ LoadingProgress.Clock = () => ((typeof performance !== "undefined") ? performanc
|
|
|
16290
16343
|
LoadingProgress.Jobs = new Map();
|
|
16291
16344
|
LoadingProgress.PostStates = new Map();
|
|
16292
16345
|
LoadingProgress.DisposeHooks = new Set();
|
|
16346
|
+
export class LoadingScreenStages {
|
|
16347
|
+
static Attach(loadingScreen, marginTop) {
|
|
16348
|
+
if (typeof document === "undefined" || loadingScreen == null)
|
|
16349
|
+
return;
|
|
16350
|
+
const screens = loadingScreen._loadingDivToRenderingCanvasMap;
|
|
16351
|
+
if (screens == null || typeof screens.forEach !== "function")
|
|
16352
|
+
return;
|
|
16353
|
+
const covers = [];
|
|
16354
|
+
screens.forEach((_canvas, screen) => { covers.push(screen); });
|
|
16355
|
+
LoadingScreenStages.Begin(covers, marginTop);
|
|
16356
|
+
}
|
|
16357
|
+
static FindPageCover() {
|
|
16358
|
+
if (typeof document === "undefined" || document.getElementById("xbabylonjsSplashScreen") != null)
|
|
16359
|
+
return null;
|
|
16360
|
+
return document.getElementById("xbabylonjsLoadingDiv");
|
|
16361
|
+
}
|
|
16362
|
+
static AttachScreen(screen, marginTop) {
|
|
16363
|
+
if (typeof document === "undefined" || screen == null)
|
|
16364
|
+
return;
|
|
16365
|
+
LoadingScreenStages.Begin([screen], marginTop);
|
|
16366
|
+
}
|
|
16367
|
+
static Begin(covers, marginTop) {
|
|
16368
|
+
LoadingScreenStages.Detach(true);
|
|
16369
|
+
if (covers.length === 0)
|
|
16370
|
+
return;
|
|
16371
|
+
covers.forEach((screen) => {
|
|
16372
|
+
screen.style.opacity = "0.995";
|
|
16373
|
+
screen.style.willChange = "opacity";
|
|
16374
|
+
LoadingScreenStages.Views.push(LoadingScreenStages.CreateView(screen, marginTop));
|
|
16375
|
+
});
|
|
16376
|
+
LoadingScreenStages.BarState = -1;
|
|
16377
|
+
LoadingScreenStages.BarProgress = null;
|
|
16378
|
+
LoadingScreenStages.PreviousHostDefers = SceneManager.HostDefersSceneStatus;
|
|
16379
|
+
SceneManager.HostDefersSceneStatus = true;
|
|
16380
|
+
LoadingScreenStages.Observer = SceneManager.OnLoaderStatusObservable.add(LoadingScreenStages.OnLoaderStatus);
|
|
16381
|
+
}
|
|
16382
|
+
static IsShowing() {
|
|
16383
|
+
return LoadingScreenStages.Views.some((view) => view.screen.isConnected !== false && view.screen.style.opacity !== "0");
|
|
16384
|
+
}
|
|
16385
|
+
static SceneToReveal(engine) {
|
|
16386
|
+
if (SceneShaderWarmup == null || LoadingScreenStages.IsShowing() !== true)
|
|
16387
|
+
return null;
|
|
16388
|
+
const scene = (engine.scenes != null && engine.scenes.length > 0) ? engine.scenes[engine.scenes.length - 1] : null;
|
|
16389
|
+
if (scene == null || scene.isDisposed || SceneShaderWarmup.IsSceneRevealed(scene))
|
|
16390
|
+
return null;
|
|
16391
|
+
return scene;
|
|
16392
|
+
}
|
|
16393
|
+
static FadeScreen(screen) {
|
|
16394
|
+
if (screen == null)
|
|
16395
|
+
return;
|
|
16396
|
+
const onFadeEnd = (event) => {
|
|
16397
|
+
if (event.target !== screen || event.propertyName !== "opacity")
|
|
16398
|
+
return;
|
|
16399
|
+
screen.removeEventListener("transitionend", onFadeEnd);
|
|
16400
|
+
screen.remove();
|
|
16401
|
+
};
|
|
16402
|
+
screen.addEventListener("transitionend", onFadeEnd);
|
|
16403
|
+
screen.style.opacity = "0";
|
|
16404
|
+
LoadingScreenStages.Detach();
|
|
16405
|
+
}
|
|
16406
|
+
static GetBarState() {
|
|
16407
|
+
return LoadingScreenStages.BarState;
|
|
16408
|
+
}
|
|
16409
|
+
static ShowStatus(status, details, progress, state) {
|
|
16410
|
+
const barProgress = LoadingScreenStages.NextBarProgress(progress, state);
|
|
16411
|
+
LoadingScreenStages.Views.forEach((view) => {
|
|
16412
|
+
if (status != null && status !== "")
|
|
16413
|
+
view.status.textContent = LoadingScreenStages.ToSentenceCase(status);
|
|
16414
|
+
if (details != null)
|
|
16415
|
+
view.details.textContent = LoadingScreenStages.ToSentenceCase(details);
|
|
16416
|
+
if (barProgress != null) {
|
|
16417
|
+
if (view.slide != null) {
|
|
16418
|
+
view.slide.cancel();
|
|
16419
|
+
view.slide = null;
|
|
16420
|
+
}
|
|
16421
|
+
view.fill.style.opacity = "";
|
|
16422
|
+
view.fill.style.width = Math.round(barProgress * 100) + "%";
|
|
16423
|
+
view.stage.setAttribute("aria-valuenow", "" + Math.round(barProgress * 100));
|
|
16424
|
+
}
|
|
16425
|
+
});
|
|
16426
|
+
}
|
|
16427
|
+
static Detach(removeStages = false) {
|
|
16428
|
+
if (LoadingScreenStages.Observer != null) {
|
|
16429
|
+
SceneManager.OnLoaderStatusObservable.remove(LoadingScreenStages.Observer);
|
|
16430
|
+
LoadingScreenStages.Observer = null;
|
|
16431
|
+
SceneManager.HostDefersSceneStatus = LoadingScreenStages.PreviousHostDefers;
|
|
16432
|
+
}
|
|
16433
|
+
LoadingScreenStages.Views.forEach((view) => {
|
|
16434
|
+
if (view.slide != null)
|
|
16435
|
+
view.slide.cancel();
|
|
16436
|
+
if (removeStages === true) {
|
|
16437
|
+
view.stage.remove();
|
|
16438
|
+
view.details.remove();
|
|
16439
|
+
}
|
|
16440
|
+
});
|
|
16441
|
+
LoadingScreenStages.Views = [];
|
|
16442
|
+
}
|
|
16443
|
+
static CreateView(screen, marginTop) {
|
|
16444
|
+
const details = document.createElement("div");
|
|
16445
|
+
details.style.cssText = "position:absolute;right:18px;bottom:12px;font-family:Arial;font-size:12px;color:white;text-align:right;z-index:2;opacity:0.9;letter-spacing:0.3px";
|
|
16446
|
+
const stage = document.createElement("div");
|
|
16447
|
+
stage.setAttribute("role", "progressbar");
|
|
16448
|
+
stage.setAttribute("aria-valuemin", "0");
|
|
16449
|
+
stage.setAttribute("aria-valuemax", "100");
|
|
16450
|
+
stage.style.cssText = "position:absolute;left:50%;top:50%;transform:translateX(-50%);width:min(320px, calc(100% - 32px));font-family:Arial;font-size:14px;color:white;text-align:center;z-index:1";
|
|
16451
|
+
stage.style.marginTop = marginTop;
|
|
16452
|
+
const status = document.createElement("div");
|
|
16453
|
+
status.style.cssText = "min-height:18px;margin-bottom:10px;letter-spacing:0.3px";
|
|
16454
|
+
status.textContent = LoadingScreenStages.START_TEXT;
|
|
16455
|
+
const track = document.createElement("div");
|
|
16456
|
+
track.style.cssText = "position:relative;width:100%;height:4px;overflow:hidden;border-radius:2px;background-color:rgba(255, 255, 255, 0.15)";
|
|
16457
|
+
const fill = document.createElement("div");
|
|
16458
|
+
fill.style.cssText = "width:30%;height:100%;border-radius:2px;transition:width 0.25s ease-out";
|
|
16459
|
+
fill.style.backgroundColor = LoadingScreenStages.BAR_COLOR;
|
|
16460
|
+
track.appendChild(fill);
|
|
16461
|
+
stage.appendChild(status);
|
|
16462
|
+
stage.appendChild(track);
|
|
16463
|
+
screen.appendChild(details);
|
|
16464
|
+
screen.appendChild(stage);
|
|
16465
|
+
return { screen: screen, stage: stage, status: status, details: details, fill: fill, slide: LoadingScreenStages.StartSlide(fill) };
|
|
16466
|
+
}
|
|
16467
|
+
static StartSlide(fill) {
|
|
16468
|
+
const reduced = (typeof window.matchMedia === "function") && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
16469
|
+
if (reduced || typeof fill.animate !== "function") {
|
|
16470
|
+
fill.style.width = "100%";
|
|
16471
|
+
fill.style.opacity = "0.4";
|
|
16472
|
+
return null;
|
|
16473
|
+
}
|
|
16474
|
+
return fill.animate([{ transform: "translateX(-100%)" }, { transform: "translateX(340%)" }], { duration: LoadingScreenStages.SLIDE_DURATION_MS, iterations: Infinity, easing: "ease-in-out" });
|
|
16475
|
+
}
|
|
16476
|
+
static OnLoaderStatus(update) {
|
|
16477
|
+
if (LoadingScreenStages.IsShowing() !== true) {
|
|
16478
|
+
LoadingScreenStages.Detach();
|
|
16479
|
+
return;
|
|
16480
|
+
}
|
|
16481
|
+
LoadingScreenStages.ShowStatus(update.status, update.details, update.progress, update.state);
|
|
16482
|
+
}
|
|
16483
|
+
static NextBarProgress(progress, state) {
|
|
16484
|
+
if (typeof progress !== "number" || !isFinite(progress))
|
|
16485
|
+
return null;
|
|
16486
|
+
const value = Math.max(0, Math.min(1, progress));
|
|
16487
|
+
const restart = (state !== LoadingScreenStages.BarState || !(state >= 0));
|
|
16488
|
+
LoadingScreenStages.BarState = state;
|
|
16489
|
+
LoadingScreenStages.BarProgress = (restart || LoadingScreenStages.BarProgress == null) ? value : Math.max(LoadingScreenStages.BarProgress, value);
|
|
16490
|
+
return LoadingScreenStages.BarProgress;
|
|
16491
|
+
}
|
|
16492
|
+
static ToSentenceCase(text) {
|
|
16493
|
+
if (text === "" || text !== text.toUpperCase())
|
|
16494
|
+
return text;
|
|
16495
|
+
const lower = text.toLowerCase();
|
|
16496
|
+
return lower.charAt(0).toUpperCase() + lower.slice(1);
|
|
16497
|
+
}
|
|
16498
|
+
}
|
|
16499
|
+
LoadingScreenStages.START_TEXT = "Loading Scene ...";
|
|
16500
|
+
LoadingScreenStages.BAR_COLOR = "#E0684B";
|
|
16501
|
+
LoadingScreenStages.SLIDE_DURATION_MS = 1200;
|
|
16502
|
+
LoadingScreenStages.Views = [];
|
|
16503
|
+
LoadingScreenStages.Observer = null;
|
|
16504
|
+
LoadingScreenStages.PreviousHostDefers = false;
|
|
16505
|
+
LoadingScreenStages.BarState = -1;
|
|
16506
|
+
LoadingScreenStages.BarProgress = null;
|
|
16293
16507
|
export class MaterialSamplerBudget {
|
|
16294
16508
|
static InstallForScene(scene) {
|
|
16295
16509
|
if (scene == null)
|
|
@@ -17420,6 +17634,7 @@ export class MetadataParser {
|
|
|
17420
17634
|
babylonLight.metadata.toolkit.hdrpSky = component.hdrpsky;
|
|
17421
17635
|
babylonLight.metadata.toolkit.intensityFactor = intensityFactor;
|
|
17422
17636
|
babylonLight.metadata.toolkit.colorTemperature = (component.colortemperature != null) ? Utilities.ParseColor3(component.colortemperature, Color3.White()) : null;
|
|
17637
|
+
LightingConversions.LinearizeLightColor(babylonLight, scene);
|
|
17423
17638
|
if (component.cookie != null)
|
|
17424
17639
|
UnityLightCookies.Apply(scene, babylonLight, entity, component.cookie);
|
|
17425
17640
|
babylonLight.shadowEnabled = false;
|
|
@@ -17908,6 +18123,10 @@ export class OpaqueSceneColor {
|
|
|
17908
18123
|
}
|
|
17909
18124
|
return all;
|
|
17910
18125
|
}
|
|
18126
|
+
static CopyConversionFor(imageProcessing) {
|
|
18127
|
+
const isEncodedByMaterials = imageProcessing != null && imageProcessing.isEnabled === true && imageProcessing.applyByPostProcess !== true;
|
|
18128
|
+
return isEncodedByMaterials ? OpaqueSceneColor.COPY_TO_LINEAR : OpaqueSceneColor.COPY_PLAIN;
|
|
18129
|
+
}
|
|
17911
18130
|
static QueueSamples(subMeshes, materials) {
|
|
17912
18131
|
if (subMeshes == null || materials == null || materials.size === 0)
|
|
17913
18132
|
return false;
|
|
@@ -17978,7 +18197,7 @@ export class OpaqueSceneColor {
|
|
|
17978
18197
|
if (!copier.isReady())
|
|
17979
18198
|
return;
|
|
17980
18199
|
engine.unBindFramebuffer(current, true);
|
|
17981
|
-
copier.copy(current.texture, destination);
|
|
18200
|
+
copier.copy(current.texture, destination, OpaqueSceneColor.CopyConversionFor(scene.imageProcessingConfiguration));
|
|
17982
18201
|
if (target.__tkMips === true && target.getInternalTexture() != null && typeof engine.generateMipmaps === "function")
|
|
17983
18202
|
engine.generateMipmaps(target.getInternalTexture());
|
|
17984
18203
|
engine.bindFramebuffer(current, undefined, undefined, undefined, true);
|
|
@@ -17989,6 +18208,27 @@ export class OpaqueSceneColor {
|
|
|
17989
18208
|
OpaqueSceneColor.Ratio = null;
|
|
17990
18209
|
OpaqueSceneColor.StoreKey = "__tkOpaqueSceneColor";
|
|
17991
18210
|
OpaqueSceneColor.UseCopy = true;
|
|
18211
|
+
OpaqueSceneColor.COPY_PLAIN = 0;
|
|
18212
|
+
OpaqueSceneColor.COPY_TO_LINEAR = 1;
|
|
18213
|
+
export class PhysicalSkyBlock {
|
|
18214
|
+
static UrpBlock(metadata) {
|
|
18215
|
+
const block = (metadata != null) ? metadata.urp : null;
|
|
18216
|
+
return (block != null && typeof block.version === "number" && block.version >= PhysicalSkyBlock.UrpBlockVersion) ? block : null;
|
|
18217
|
+
}
|
|
18218
|
+
static SkyOf(metadata) {
|
|
18219
|
+
if (HdrpRendering.IsParityExport(metadata) === true)
|
|
18220
|
+
return metadata.hdrp.sky;
|
|
18221
|
+
const block = PhysicalSkyBlock.UrpBlock(metadata);
|
|
18222
|
+
return (block != null) ? block.sky : null;
|
|
18223
|
+
}
|
|
18224
|
+
static FogOf(metadata) {
|
|
18225
|
+
if (HdrpRendering.IsParityExport(metadata) === true)
|
|
18226
|
+
return metadata.hdrp.fog;
|
|
18227
|
+
const block = PhysicalSkyBlock.UrpBlock(metadata);
|
|
18228
|
+
return (block != null) ? block.fog : null;
|
|
18229
|
+
}
|
|
18230
|
+
}
|
|
18231
|
+
PhysicalSkyBlock.UrpBlockVersion = 1;
|
|
17992
18232
|
export class FixedStepClock {
|
|
17993
18233
|
constructor(fixedDeltaTime = FixedStepClock.DEFAULT_FIXED_DELTA_TIME, maximumDeltaTime = FixedStepClock.DEFAULT_MAXIMUM_DELTA_TIME) {
|
|
17994
18234
|
this.accumulatedTime = 0;
|
|
@@ -19257,7 +19497,9 @@ export class SceneShaderWarmup {
|
|
|
19257
19497
|
SceneShaderWarmup.SkipFrustumClipping(scene, frustumState);
|
|
19258
19498
|
const drawHidden = drawWholeScene && ownPipelines && options.includeHiddenMeshes === true && SceneShaderWarmup.DrawHiddenMeshesOnReveal === true;
|
|
19259
19499
|
const shownHiddenMeshes = drawHidden ? SceneShaderWarmup.ShowHiddenMeshesForDraws(scene) : [];
|
|
19260
|
-
const
|
|
19500
|
+
const fullShadowDraws = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.DrawShadowMapsFully(scene) : [];
|
|
19501
|
+
const lodCycle = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.CycleNativeLodLevels(scene) : null;
|
|
19502
|
+
const settleFrames = Math.max(SceneShaderWarmup.PIPELINE_SETTLE_FRAMES, (lodCycle != null) ? lodCycle.length : 0);
|
|
19261
19503
|
const shadowListWraps = SceneShaderWarmup.KeepHiddenCastersInShadowMaps(scene, shownHiddenMeshes);
|
|
19262
19504
|
const scaleVariantDraw = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.AlternateScaleVariants(scene) : null;
|
|
19263
19505
|
const materialVariantDraw = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.AlternateMaterialVariants(scene) : null;
|
|
@@ -19268,10 +19510,11 @@ export class SceneShaderWarmup {
|
|
|
19268
19510
|
const stageFraction = (requested > startRequested) ? (compiled - startCompiled) / (requested - startRequested) : 0;
|
|
19269
19511
|
const pipelineStep = { step: "pipelines", ready: compiled - startCompiled, total: requested - startRequested };
|
|
19270
19512
|
report(effects.ready + compiled - startCompiled, effects.total + requested - startRequested + 1, effectShare + (1 - effectShare) * stageFraction * 0.99, pipelineStep);
|
|
19271
|
-
}).then((timedOut) => {
|
|
19513
|
+
}, settleFrames).then((timedOut) => {
|
|
19272
19514
|
SceneShaderWarmup.RestoreFrustumClipping(scene, frustumState);
|
|
19273
19515
|
SceneShaderWarmup.RestoreShadowCasterLists(shadowListWraps);
|
|
19274
|
-
SceneShaderWarmup.
|
|
19516
|
+
SceneShaderWarmup.RestoreFullShadowMaps(fullShadowDraws);
|
|
19517
|
+
SceneShaderWarmup.RestoreNativeLodLevels(scene, lodCycle);
|
|
19275
19518
|
SceneShaderWarmup.RestoreHiddenMeshes(scene, shownHiddenMeshes);
|
|
19276
19519
|
SceneShaderWarmup.RestoreScaleVariants(scene, scaleVariantDraw);
|
|
19277
19520
|
SceneShaderWarmup.RestoreMaterialVariants(scene, materialVariantDraw);
|
|
@@ -20991,13 +21234,15 @@ export class SceneShaderWarmup {
|
|
|
20991
21234
|
wrap.shadowMap.getCustomRenderList = wrap.customList;
|
|
20992
21235
|
}
|
|
20993
21236
|
}
|
|
20994
|
-
static
|
|
21237
|
+
static DrawShadowMapsFully(scene) {
|
|
20995
21238
|
const draws = [];
|
|
20996
21239
|
for (const shadowMap of SceneShaderWarmup.CollectShadowMaps(scene)) {
|
|
20997
21240
|
const renderGate = shadowMap;
|
|
20998
|
-
|
|
21241
|
+
const isGated = renderGate._tkSkipInvisible === true;
|
|
21242
|
+
if (!isGated && !SceneShaderWarmup.HasCasterCullingList(shadowMap))
|
|
20999
21243
|
continue;
|
|
21000
|
-
|
|
21244
|
+
if (isGated)
|
|
21245
|
+
renderGate._tkPrewarmDrawAll = true;
|
|
21001
21246
|
draws.push({ shadowMap: shadowMap, customList: shadowMap.getCustomRenderList });
|
|
21002
21247
|
shadowMap.getCustomRenderList = null;
|
|
21003
21248
|
try {
|
|
@@ -21007,7 +21252,11 @@ export class SceneShaderWarmup {
|
|
|
21007
21252
|
}
|
|
21008
21253
|
return draws;
|
|
21009
21254
|
}
|
|
21010
|
-
static
|
|
21255
|
+
static HasCasterCullingList(shadowMap) {
|
|
21256
|
+
const customList = shadowMap.getCustomRenderList;
|
|
21257
|
+
return customList != null && (customList.tkCasterCulling === true || customList.tkTerrainCulling === true);
|
|
21258
|
+
}
|
|
21259
|
+
static RestoreFullShadowMaps(draws) {
|
|
21011
21260
|
for (const draw of draws) {
|
|
21012
21261
|
draw.shadowMap._tkPrewarmDrawAll = false;
|
|
21013
21262
|
if (draw.shadowMap.getCustomRenderList == null)
|
|
@@ -21239,6 +21488,53 @@ export class SceneShaderWarmup {
|
|
|
21239
21488
|
draw.frameObserver = null;
|
|
21240
21489
|
SceneShaderWarmup.SetScaleVariantForced(draw.entries, false);
|
|
21241
21490
|
}
|
|
21491
|
+
static NativeLodSequence(mesh) {
|
|
21492
|
+
if (!(mesh instanceof Mesh) || mesh.isDisposed())
|
|
21493
|
+
return [];
|
|
21494
|
+
const levels = mesh.getLODLevels();
|
|
21495
|
+
if (levels == null || levels.length === 0)
|
|
21496
|
+
return [];
|
|
21497
|
+
const sequence = [mesh];
|
|
21498
|
+
for (const level of levels) {
|
|
21499
|
+
if (level.mesh != null && !level.mesh.isDisposed() && sequence.indexOf(level.mesh) < 0)
|
|
21500
|
+
sequence.push(level.mesh);
|
|
21501
|
+
}
|
|
21502
|
+
return (sequence.length > 1) ? sequence : [];
|
|
21503
|
+
}
|
|
21504
|
+
static CycleNativeLodLevels(scene) {
|
|
21505
|
+
if (SceneShaderWarmup.WarmNativeLodLevelsOnReveal !== true)
|
|
21506
|
+
return null;
|
|
21507
|
+
const sequences = new Map();
|
|
21508
|
+
let length = 0;
|
|
21509
|
+
for (const mesh of scene.meshes) {
|
|
21510
|
+
const sequence = SceneShaderWarmup.NativeLodSequence(mesh);
|
|
21511
|
+
if (sequence.length === 0)
|
|
21512
|
+
continue;
|
|
21513
|
+
sequences.set(mesh, sequence);
|
|
21514
|
+
length = Math.max(length, sequence.length);
|
|
21515
|
+
}
|
|
21516
|
+
if (sequences.size === 0)
|
|
21517
|
+
return null;
|
|
21518
|
+
const previousSelector = scene.customLODSelector || null;
|
|
21519
|
+
const cycle = { sequences: sequences, length: length, frameIndex: 0, previousSelector: previousSelector, selector: null, frameObserver: null };
|
|
21520
|
+
cycle.selector = (mesh, camera) => {
|
|
21521
|
+
const sequence = sequences.get(mesh);
|
|
21522
|
+
if (sequence != null)
|
|
21523
|
+
return sequence[cycle.frameIndex % sequence.length];
|
|
21524
|
+
return (previousSelector != null) ? previousSelector(mesh, camera) : mesh.getLOD(camera);
|
|
21525
|
+
};
|
|
21526
|
+
cycle.frameObserver = scene.onAfterRenderObservable.add(() => { cycle.frameIndex++; });
|
|
21527
|
+
scene.customLODSelector = cycle.selector;
|
|
21528
|
+
return cycle;
|
|
21529
|
+
}
|
|
21530
|
+
static RestoreNativeLodLevels(scene, cycle) {
|
|
21531
|
+
if (cycle == null)
|
|
21532
|
+
return;
|
|
21533
|
+
scene.onAfterRenderObservable.remove(cycle.frameObserver);
|
|
21534
|
+
cycle.frameObserver = null;
|
|
21535
|
+
if (scene.customLODSelector === cycle.selector)
|
|
21536
|
+
scene.customLODSelector = cycle.previousSelector;
|
|
21537
|
+
}
|
|
21242
21538
|
static SetScaleVariantForced(entries, forced) {
|
|
21243
21539
|
const prototypeUpdate = AbstractMesh.prototype._updateNonUniformScalingState;
|
|
21244
21540
|
for (const entry of entries) {
|
|
@@ -21382,8 +21678,8 @@ export class SceneShaderWarmup {
|
|
|
21382
21678
|
}
|
|
21383
21679
|
return phases;
|
|
21384
21680
|
}
|
|
21385
|
-
static WaitForPipelinesPerCamera(scene, pipelines, deadline, phases, onProgress) {
|
|
21386
|
-
return SceneShaderWarmup.RunCameraPhasesAsync(scene, phases, false, () => SceneShaderWarmup.WaitForPipelines(scene, pipelines, deadline, onProgress), (timedOut) => timedOut);
|
|
21681
|
+
static WaitForPipelinesPerCamera(scene, pipelines, deadline, phases, onProgress, settleFrames = SceneShaderWarmup.PIPELINE_SETTLE_FRAMES) {
|
|
21682
|
+
return SceneShaderWarmup.RunCameraPhasesAsync(scene, phases, false, () => SceneShaderWarmup.WaitForPipelines(scene, pipelines, deadline, onProgress, settleFrames), (timedOut) => timedOut);
|
|
21387
21683
|
}
|
|
21388
21684
|
static RunCameraPhasesAsync(scene, phases, noPhaseRun, runStep, isTimedOut) {
|
|
21389
21685
|
const runPhase = (phaseIndex, previous) => {
|
|
@@ -21401,7 +21697,7 @@ export class SceneShaderWarmup {
|
|
|
21401
21697
|
};
|
|
21402
21698
|
return runPhase(0, noPhaseRun);
|
|
21403
21699
|
}
|
|
21404
|
-
static WaitForPipelines(scene, pipelines, deadline, onProgress) {
|
|
21700
|
+
static WaitForPipelines(scene, pipelines, deadline, onProgress, settleFrames = SceneShaderWarmup.PIPELINE_SETTLE_FRAMES) {
|
|
21405
21701
|
return new Promise((resolve) => {
|
|
21406
21702
|
let quietFrames = 0;
|
|
21407
21703
|
let lastRequested = -1;
|
|
@@ -21432,7 +21728,7 @@ export class SceneShaderWarmup {
|
|
|
21432
21728
|
quietFrames = quiet ? quietFrames + 1 : 0;
|
|
21433
21729
|
if (onProgress != null)
|
|
21434
21730
|
onProgress(pipelines.compiled, pipelines.requested);
|
|
21435
|
-
if (quietFrames >=
|
|
21731
|
+
if (quietFrames >= settleFrames)
|
|
21436
21732
|
finish(false);
|
|
21437
21733
|
});
|
|
21438
21734
|
timer = setTimeout(() => finish(true), Math.max(0, deadline - SceneShaderWarmup.NowMs()));
|
|
@@ -21989,6 +22285,7 @@ SceneShaderWarmup.BuildScenePipelinesOnReveal = true;
|
|
|
21989
22285
|
SceneShaderWarmup.DrawHiddenMeshesOnReveal = true;
|
|
21990
22286
|
SceneShaderWarmup.WarmEmptyThinInstancesOnReveal = true;
|
|
21991
22287
|
SceneShaderWarmup.WarmScaleVariantsOnReveal = true;
|
|
22288
|
+
SceneShaderWarmup.WarmNativeLodLevelsOnReveal = true;
|
|
21992
22289
|
SceneShaderWarmup.OPTIONAL_UNIT_GRACE_MS = 3000;
|
|
21993
22290
|
SceneShaderWarmup.GIVEN_UP_NAMES_SHOWN = 10;
|
|
21994
22291
|
SceneShaderWarmup.LateCompileReporting = null;
|
|
@@ -23625,6 +23922,27 @@ export class ShaderGlobals {
|
|
|
23625
23922
|
ShaderGlobals._keywords.add(k);
|
|
23626
23923
|
ShaderGlobals._version++;
|
|
23627
23924
|
}
|
|
23925
|
+
static LoadSceneDrivers(scene, drivers) {
|
|
23926
|
+
if (scene == null || drivers == null || drivers.length === 0)
|
|
23927
|
+
return;
|
|
23928
|
+
const observer = scene.onBeforeRenderObservable.add(() => {
|
|
23929
|
+
const deltaSeconds = ShaderGlobals.GetClock(scene).deltaTime;
|
|
23930
|
+
for (const driver of drivers)
|
|
23931
|
+
ShaderGlobals.AdvanceDriver(driver, deltaSeconds);
|
|
23932
|
+
});
|
|
23933
|
+
scene.onDisposeObservable.addOnce(() => { scene.onBeforeRenderObservable.remove(observer); });
|
|
23934
|
+
}
|
|
23935
|
+
static AdvanceDriver(driver, deltaSeconds) {
|
|
23936
|
+
const entry = ShaderGlobals._vectors.get(driver.name);
|
|
23937
|
+
if (entry == null)
|
|
23938
|
+
return;
|
|
23939
|
+
const componentName = ShaderGlobals.DriverComponentNames[driver.component];
|
|
23940
|
+
let advancedValue = entry.value[componentName] + driver.rate * deltaSeconds;
|
|
23941
|
+
if (driver.wrap > 0 && advancedValue > driver.wrap)
|
|
23942
|
+
advancedValue -= driver.wrap;
|
|
23943
|
+
entry.value[componentName] = advancedValue;
|
|
23944
|
+
ShaderGlobals._version++;
|
|
23945
|
+
}
|
|
23628
23946
|
static GetClock(scene) {
|
|
23629
23947
|
if (scene == null)
|
|
23630
23948
|
return { time: 0, deltaTime: 0, smoothDeltaTime: 0, frame: 0 };
|
|
@@ -23659,6 +23977,7 @@ export class ShaderGlobals {
|
|
|
23659
23977
|
return clock;
|
|
23660
23978
|
}
|
|
23661
23979
|
}
|
|
23980
|
+
ShaderGlobals.DriverComponentNames = ["x", "y", "z", "w"];
|
|
23662
23981
|
ShaderGlobals._version = 0;
|
|
23663
23982
|
ShaderGlobals._floats = new Map();
|
|
23664
23983
|
ShaderGlobals._vectors = new Map();
|
|
@@ -24883,6 +25202,23 @@ export class ShaderGraphRuntime {
|
|
|
24883
25202
|
loadTexture(textures[k], (texture) => { clone.setTextureValue(k, texture); });
|
|
24884
25203
|
return clone;
|
|
24885
25204
|
}
|
|
25205
|
+
static RecordExportReport(scene, entries) {
|
|
25206
|
+
if (entries == null || entries.length === 0)
|
|
25207
|
+
return;
|
|
25208
|
+
const record = ShaderGraphRuntime.Record(scene);
|
|
25209
|
+
if (record == null)
|
|
25210
|
+
return;
|
|
25211
|
+
record.exportReport = entries;
|
|
25212
|
+
ShaderGraphRuntime.ScheduleSceneSummary(scene);
|
|
25213
|
+
}
|
|
25214
|
+
static PrintExportReport(record) {
|
|
25215
|
+
const entries = record.exportReport;
|
|
25216
|
+
console.groupCollapsed("Export report: " + entries.length + " items not carried");
|
|
25217
|
+
for (const entry of entries)
|
|
25218
|
+
console.log(entry.feature + " - " + entry.reason + ": " + entry.items.join(", "));
|
|
25219
|
+
console.groupEnd();
|
|
25220
|
+
record.exportReport = null;
|
|
25221
|
+
}
|
|
24886
25222
|
static FlushSceneSummary(scene) {
|
|
24887
25223
|
const record = ShaderGraphRuntime._records.get(scene != null ? scene.uniqueId : -1);
|
|
24888
25224
|
if (record == null || record.scene !== scene)
|
|
@@ -24902,6 +25238,8 @@ export class ShaderGraphRuntime {
|
|
|
24902
25238
|
const report = (ShaderGraphRuntime.ReportDeviations != null) ? ShaderGraphRuntime.ReportDeviations
|
|
24903
25239
|
: (SceneManager != null && typeof SceneManager.IsDebugMode === "function" ? SceneManager.IsDebugMode() : true);
|
|
24904
25240
|
const n = record.materials.size, m = record.lastResorts.size;
|
|
25241
|
+
if (report && record.exportReport != null)
|
|
25242
|
+
ShaderGraphRuntime.PrintExportReport(record);
|
|
24905
25243
|
if (report && (n > 0 || m > 0)) {
|
|
24906
25244
|
console.groupCollapsed("Shader Graph: " + n + " materials with deviations, " + m + " last resort" + (record.flushed ? " (built after the scene summary)" : ""));
|
|
24907
25245
|
record.materials.forEach((entry, name) => {
|
|
@@ -29628,8 +29966,9 @@ export class UnityLodGroups {
|
|
|
29628
29966
|
static ExportedSize(root, metadata) {
|
|
29629
29967
|
if (root == null || metadata == null || !(metadata.lodsize > 0))
|
|
29630
29968
|
return null;
|
|
29631
|
-
const
|
|
29632
|
-
const
|
|
29969
|
+
const exportedCenter = metadata.lodcenter;
|
|
29970
|
+
const hasCenter = (Array.isArray(exportedCenter) && exportedCenter.length >= 3);
|
|
29971
|
+
const localPoint = hasCenter ? new Vector3(0 - exportedCenter[0], exportedCenter[1], exportedCenter[2]) : Vector3.Zero();
|
|
29633
29972
|
const scale = new Vector3();
|
|
29634
29973
|
root.computeWorldMatrix(true).decompose(scale, null, null);
|
|
29635
29974
|
const largest = Math.max(Math.abs(scale.x), Math.abs(scale.y), Math.abs(scale.z));
|
|
@@ -29640,6 +29979,35 @@ export class UnityLodGroups {
|
|
|
29640
29979
|
const useDistances = !hasCoverages && (distances != null && distances.length > 0);
|
|
29641
29980
|
return { useDistances: useDistances, thresholds: useDistances ? distances : (hasCoverages ? coverages : null) };
|
|
29642
29981
|
}
|
|
29982
|
+
static LevelsShareTransform(master, details) {
|
|
29983
|
+
const masterMatrix = master.computeWorldMatrix(true);
|
|
29984
|
+
for (const detail of details) {
|
|
29985
|
+
if (UnityLodGroups.MatricesShareTransform(masterMatrix, detail.computeWorldMatrix(true)) === false)
|
|
29986
|
+
return false;
|
|
29987
|
+
}
|
|
29988
|
+
return true;
|
|
29989
|
+
}
|
|
29990
|
+
static MatricesShareTransform(first, second) {
|
|
29991
|
+
const offset = Vector3.Distance(first.getTranslation(), second.getTranslation());
|
|
29992
|
+
if (offset > UnityLodGroups.NATIVE_TRANSFORM_TOLERANCE_METERS)
|
|
29993
|
+
return false;
|
|
29994
|
+
for (const element of UnityLodGroups.BASIS_ELEMENTS) {
|
|
29995
|
+
if (Math.abs(first.m[element] - second.m[element]) > UnityLodGroups.NATIVE_BASIS_TOLERANCE)
|
|
29996
|
+
return false;
|
|
29997
|
+
}
|
|
29998
|
+
return true;
|
|
29999
|
+
}
|
|
30000
|
+
static RequiresSwitcher(meshLevels, fadeMode) {
|
|
30001
|
+
if (fadeMode !== UnityLodGroups.FADE_NONE)
|
|
30002
|
+
return true;
|
|
30003
|
+
if (meshLevels.some((meshes) => meshes.length > 1))
|
|
30004
|
+
return true;
|
|
30005
|
+
const master = meshLevels[0][0];
|
|
30006
|
+
if (master == null)
|
|
30007
|
+
return false;
|
|
30008
|
+
const details = meshLevels.slice(1).map((meshes) => meshes[0]).filter((mesh) => mesh != null);
|
|
30009
|
+
return UnityLodGroups.LevelsShareTransform(master, details) === false;
|
|
30010
|
+
}
|
|
29643
30011
|
static ParseFadeMode(mode) {
|
|
29644
30012
|
const m = (mode != null) ? String(mode).toLowerCase() : "";
|
|
29645
30013
|
if (m === "crossfade")
|
|
@@ -29802,21 +30170,25 @@ export class UnityLodGroups {
|
|
|
29802
30170
|
addNativeGroup(master, details, coverages, worldSize) {
|
|
29803
30171
|
if (!(master instanceof Mesh) || master instanceof InstancedMesh || coverages == null)
|
|
29804
30172
|
return false;
|
|
29805
|
-
|
|
29806
|
-
if (n <= 0)
|
|
30173
|
+
if (master.instances.length > 0)
|
|
29807
30174
|
return false;
|
|
29808
|
-
|
|
29809
|
-
|
|
29810
|
-
|
|
30175
|
+
const levelCount = Math.min(coverages.length, details.length + 1);
|
|
30176
|
+
if (levelCount <= 0)
|
|
30177
|
+
return false;
|
|
30178
|
+
for (let k = 0; k < levelCount - 1; k++) {
|
|
30179
|
+
const detail = details[k];
|
|
30180
|
+
if (!(detail instanceof Mesh) || detail instanceof InstancedMesh || detail === master || detail._masterMesh != null || details.indexOf(detail) !== k)
|
|
29811
30181
|
return false;
|
|
29812
30182
|
}
|
|
29813
|
-
|
|
30183
|
+
if (UnityLodGroups.LevelsShareTransform(master, details.slice(0, levelCount - 1)) === false)
|
|
30184
|
+
return false;
|
|
30185
|
+
for (let k = 1; k < levelCount; k++)
|
|
29814
30186
|
master.addLODLevel(1e6 + k, details[k - 1]);
|
|
29815
|
-
master.addLODLevel(1e6 +
|
|
30187
|
+
master.addLODLevel(1e6 + levelCount, null);
|
|
29816
30188
|
const all = master.getLODLevels();
|
|
29817
30189
|
const entries = [];
|
|
29818
|
-
for (let i = 0; i <
|
|
29819
|
-
const mesh = (i <
|
|
30190
|
+
for (let i = 0; i < levelCount; i++) {
|
|
30191
|
+
const mesh = (i < levelCount - 1) ? details[i] : null;
|
|
29820
30192
|
let found = null;
|
|
29821
30193
|
for (const level of all) {
|
|
29822
30194
|
if (level.mesh === mesh && entries.indexOf(level) === -1) {
|
|
@@ -29826,7 +30198,7 @@ export class UnityLodGroups {
|
|
|
29826
30198
|
}
|
|
29827
30199
|
entries.push(found);
|
|
29828
30200
|
}
|
|
29829
|
-
this._native.push({ master: master, entries: entries, coverages: coverages.slice(0,
|
|
30201
|
+
this._native.push({ master: master, entries: entries, coverages: coverages.slice(0, levelCount), worldSize: worldSize });
|
|
29830
30202
|
this._nativeKey[0] = NaN;
|
|
29831
30203
|
return true;
|
|
29832
30204
|
}
|
|
@@ -30076,6 +30448,9 @@ UnityLodGroups.FADE_NONE = 0;
|
|
|
30076
30448
|
UnityLodGroups.FADE_CROSSFADE = 1;
|
|
30077
30449
|
UnityLodGroups.FADE_SPEEDTREE = 2;
|
|
30078
30450
|
UnityLodGroups.FADE_SECONDS = 0.5;
|
|
30451
|
+
UnityLodGroups.NATIVE_TRANSFORM_TOLERANCE_METERS = 0.005;
|
|
30452
|
+
UnityLodGroups.NATIVE_BASIS_TOLERANCE = 0.001;
|
|
30453
|
+
UnityLodGroups.BASIS_ELEMENTS = [0, 1, 2, 4, 5, 6, 8, 9, 10];
|
|
30079
30454
|
UnityLodGroups.DeferLoadFadeClones = true;
|
|
30080
30455
|
UnityLodGroups.FadeCloneSliceMs = 8;
|
|
30081
30456
|
UnityLodGroups._cloneQueues = new Map();
|
|
@@ -69707,16 +70082,36 @@ export class HdrpFogPass {
|
|
|
69707
70082
|
return HdrpFogPass.Uniforms.concat((S != null && Array.isArray(S.AtmosphereUniforms)) ? S.AtmosphereUniforms : [], ["scale"]);
|
|
69708
70083
|
}
|
|
69709
70084
|
static FogBlock(scene) {
|
|
69710
|
-
|
|
70085
|
+
if (HdrpRendering == null)
|
|
70086
|
+
return null;
|
|
70087
|
+
const fog = PhysicalSkyBlock.FogOf(HdrpRendering.SceneMetadata(scene));
|
|
70088
|
+
if (fog != null && typeof fog.enabled === "boolean")
|
|
70089
|
+
return fog;
|
|
70090
|
+
const block = HdrpRendering.Block(scene);
|
|
69711
70091
|
return (block != null && block.fog != null && typeof block.fog === "object") ? block.fog : null;
|
|
69712
70092
|
}
|
|
69713
70093
|
static SkyOf(scene) {
|
|
69714
70094
|
const live = (HdrpPhysicallyBasedSky != null) ? HdrpPhysicallyBasedSky.Get(scene) : null;
|
|
69715
70095
|
if (live != null)
|
|
69716
70096
|
return live;
|
|
69717
|
-
|
|
70097
|
+
if (HdrpRendering == null)
|
|
70098
|
+
return null;
|
|
70099
|
+
const sky = PhysicalSkyBlock.SkyOf(HdrpRendering.SceneMetadata(scene));
|
|
70100
|
+
if (sky != null)
|
|
70101
|
+
return sky;
|
|
70102
|
+
const block = HdrpRendering.Block(scene);
|
|
69718
70103
|
return (block != null) ? block.sky : null;
|
|
69719
70104
|
}
|
|
70105
|
+
static BindChain(effect, scene, fog) {
|
|
70106
|
+
if (HdrpRendering.IsParity(scene) === true) {
|
|
70107
|
+
effect.setFloat4("fogChain", 0, 0, 0, 0);
|
|
70108
|
+
return;
|
|
70109
|
+
}
|
|
70110
|
+
const water = (MeshWaterSurface != null) ? MeshWaterSurface.Get(scene) : null;
|
|
70111
|
+
const isDisplayEncoded = scene.imageProcessingConfiguration.applyByPostProcess !== true;
|
|
70112
|
+
const isFogOn = fog != null && fog.enabled === true;
|
|
70113
|
+
effect.setFloat4("fogChain", isDisplayEncoded ? 1 : 0, (water != null) ? 1 : 0, (water != null) ? water.getSurfaceHeight() : 0, isFogOn ? 1 : 0);
|
|
70114
|
+
}
|
|
69720
70115
|
static Frame(scene, fog) {
|
|
69721
70116
|
const s = scene;
|
|
69722
70117
|
const renderId = scene.getRenderId();
|
|
@@ -70016,76 +70411,8 @@ fn hdrpFogAmbient(dir: vec3f) -> vec3f {
|
|
|
70016
70411
|
}
|
|
70017
70412
|
`;
|
|
70018
70413
|
}
|
|
70019
|
-
static
|
|
70414
|
+
static AerialPerspectiveGlsl() {
|
|
70020
70415
|
return `
|
|
70021
|
-
float ${prefix}OpticalDepth(float ext, float baseH, vec2 exps, float cosZ, float startH, float len) {
|
|
70022
|
-
float absZ = max(abs(cosZ), 0.001);
|
|
70023
|
-
float rcpAbsZ = 1.0 / absZ;
|
|
70024
|
-
float endH = startH + len * cosZ;
|
|
70025
|
-
float minH = min(startH, endH);
|
|
70026
|
-
float h = max(minH - baseH, 0.0);
|
|
70027
|
-
float homDist = clamp((baseH - minH) * rcpAbsZ, 0.0, len);
|
|
70028
|
-
float expDist = len - homDist;
|
|
70029
|
-
float expMult = exp(-h * exps.x) * (1.0 - exp(-expDist * absZ * exps.x)) * (rcpAbsZ * exps.y);
|
|
70030
|
-
return ext * (homDist + expMult);
|
|
70031
|
-
}
|
|
70032
|
-
float ${prefix}PhaseHG(float g, float c) {
|
|
70033
|
-
float g2 = g * g;
|
|
70034
|
-
return (1.0 - g2) / (12.566370614 * pow(max(1.0 + g2 - 2.0 * g * c, 0.0001), 1.5));
|
|
70035
|
-
}
|
|
70036
|
-
`;
|
|
70037
|
-
}
|
|
70038
|
-
static WgslHelpers(prefix) {
|
|
70039
|
-
return `
|
|
70040
|
-
fn ${prefix}OpticalDepth(ext: f32, baseH: f32, exps: vec2f, cosZ: f32, startH: f32, len: f32) -> f32 {
|
|
70041
|
-
let absZ: f32 = max(abs(cosZ), 0.001);
|
|
70042
|
-
let rcpAbsZ: f32 = 1.0 / absZ;
|
|
70043
|
-
let endH: f32 = startH + len * cosZ;
|
|
70044
|
-
let minH: f32 = min(startH, endH);
|
|
70045
|
-
let h: f32 = max(minH - baseH, 0.0);
|
|
70046
|
-
let homDist: f32 = clamp((baseH - minH) * rcpAbsZ, 0.0, len);
|
|
70047
|
-
let expDist: f32 = len - homDist;
|
|
70048
|
-
let expMult: f32 = exp(-h * exps.x) * (1.0 - exp(-expDist * absZ * exps.x)) * (rcpAbsZ * exps.y);
|
|
70049
|
-
return ext * (homDist + expMult);
|
|
70050
|
-
}
|
|
70051
|
-
fn ${prefix}PhaseHG(g: f32, c: f32) -> f32 {
|
|
70052
|
-
let g2: f32 = g * g;
|
|
70053
|
-
return (1.0 - g2) / (12.566370614 * pow(max(1.0 + g2 - 2.0 * g * c, 0.0001), 1.5));
|
|
70054
|
-
}
|
|
70055
|
-
`;
|
|
70056
|
-
}
|
|
70057
|
-
static Glsl() {
|
|
70058
|
-
const S = HdrpPbSkyShaders;
|
|
70059
|
-
const ap = (S != null) ? (S.GlslUniforms() + S.GlslCommon()) : "";
|
|
70060
|
-
return `#ifdef HDRP_FOG_AP
|
|
70061
|
-
precision highp sampler2DArray;
|
|
70062
|
-
#endif
|
|
70063
|
-
precision highp float;
|
|
70064
|
-
varying vec2 vUV;
|
|
70065
|
-
uniform sampler2D textureSampler;
|
|
70066
|
-
uniform sampler2D depthSampler;
|
|
70067
|
-
#ifdef HDRP_FOG_ENV
|
|
70068
|
-
uniform samplerCube fogEnvSampler;
|
|
70069
|
-
#endif
|
|
70070
|
-
uniform vec4 fogA;
|
|
70071
|
-
uniform vec4 fogB;
|
|
70072
|
-
uniform vec4 fogC;
|
|
70073
|
-
uniform vec4 fogD;
|
|
70074
|
-
uniform vec4 fogE;
|
|
70075
|
-
uniform vec4 fogSunDir;
|
|
70076
|
-
uniform vec4 fogSun;
|
|
70077
|
-
uniform vec4 fogEnv;
|
|
70078
|
-
uniform vec4 fogSky;
|
|
70079
|
-
${HdrpFogPass.AmbientDeclarations(false)}uniform vec4 fogEye;
|
|
70080
|
-
uniform vec4 fogForward;
|
|
70081
|
-
uniform vec4 fogViewport;
|
|
70082
|
-
uniform vec4 fogInput;
|
|
70083
|
-
uniform mat4 fogInvViewProj;
|
|
70084
|
-
uniform mat4 fogEnvMatrix;
|
|
70085
|
-
#ifdef HDRP_FOG_AP
|
|
70086
|
-
uniform sampler2DArray apLut;
|
|
70087
|
-
uniform vec4 fogApCamera;
|
|
70088
|
-
${ap}
|
|
70089
70416
|
vec3 fogRescaledChapman(vec2 z, vec2 Z, float c) {
|
|
70090
70417
|
float sinTheta = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
|
|
70091
70418
|
vec2 ch = vec2(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
|
|
@@ -70114,116 +70441,28 @@ vec3 fogApOpticalDepth(vec3 O, vec3 V, float t) {
|
|
|
70114
70441
|
float ozone = pbOzoneOpticalDepth(r, cosChi, t);
|
|
70115
70442
|
return od.x * pbAirExt.xyz + od.y * pbAirExt.w + ozone * pbOzone0.xyz;
|
|
70116
70443
|
}
|
|
70117
|
-
|
|
70118
|
-
|
|
70119
|
-
|
|
70120
|
-
|
|
70121
|
-
|
|
70122
|
-
|
|
70123
|
-
|
|
70124
|
-
|
|
70125
|
-
|
|
70126
|
-
|
|
70127
|
-
|
|
70128
|
-
|
|
70129
|
-
|
|
70130
|
-
|
|
70131
|
-
|
|
70132
|
-
|
|
70133
|
-
|
|
70134
|
-
}
|
|
70135
|
-
void main(void) {
|
|
70136
|
-
vec4 src = texture2D(textureSampler, fogInput.xy + vUV * fogInput.zw);
|
|
70137
|
-
float raw = abs(texture2D(depthSampler, fogViewport.xy + vUV * fogViewport.zw).r);
|
|
70138
|
-
vec4 farP = fogInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
|
|
70139
|
-
vec3 dir = normalize(farP.xyz / farP.w - fogEye.xyz);
|
|
70140
|
-
bool isSky = (raw <= 0.0) || (raw >= fogForward.w);
|
|
70141
|
-
float tFrag = isSky ? fogA.w : raw / max(dot(dir, fogForward.xyz), 0.0001);
|
|
70142
|
-
vec3 color = vec3(0.0);
|
|
70143
|
-
vec3 opacity = vec3(0.0);
|
|
70144
|
-
if (fogA.x > 0.5 && fogEye.w > 0.5) {
|
|
70145
|
-
vec3 fc = hdrpFogColor(dir, tFrag);
|
|
70146
|
-
float volEnd = (fogB.w > 0.5) ? min(tFrag, fogEnv.w) : 0.0;
|
|
70147
|
-
float trCamera = 1.0;
|
|
70148
|
-
if (volEnd > 0.0) {
|
|
70149
|
-
trCamera = exp(-hdrpFogOpticalDepth(fogA.y, fogA.z, fogB.xy, dir.y, fogEye.y, volEnd));
|
|
70150
|
-
vec3 lit = hdrpFogAmbient(dir) * fogC.w * fogSun.w + fogSun.rgb * hdrpFogPhaseHG(fogE.w, dot(dir, fogSunDir.xyz)) * fogSunDir.w;
|
|
70151
|
-
color = lit * fogE.rgb * (1.0 - trCamera);
|
|
70152
|
-
}
|
|
70153
|
-
float delta = tFrag - volEnd;
|
|
70154
|
-
if (delta > 0.0) {
|
|
70155
|
-
float tr = exp(-hdrpFogOpticalDepth(fogA.y, fogA.z, fogB.xy, dir.y, fogEye.y + volEnd * dir.y, delta));
|
|
70156
|
-
color += trCamera * fc * fogE.rgb * (1.0 - tr);
|
|
70157
|
-
trCamera *= tr;
|
|
70158
|
-
}
|
|
70159
|
-
#if HDRP_FOG_LOCALS > 0
|
|
70160
|
-
if (fogB.w > 0.5) {
|
|
70161
|
-
vec4 lf = hdrpFogLocals(dir, min(tFrag, fogEnv.w));
|
|
70162
|
-
color = color * lf.a + lf.rgb;
|
|
70163
|
-
trCamera *= lf.a;
|
|
70164
|
-
}
|
|
70165
|
-
#endif
|
|
70166
|
-
opacity = vec3(1.0 - trCamera);
|
|
70167
|
-
}
|
|
70168
|
-
#ifdef HDRP_FOG_AP
|
|
70169
|
-
if (!isSky && fogApCamera.w > 0.5 && fogEye.w > 0.5) {
|
|
70170
|
-
vec3 O = fogApCamera.xyz;
|
|
70171
|
-
float r = length(O);
|
|
70172
|
-
float cosChi = dot(O / r, dir);
|
|
70173
|
-
float tAp = min(tFrag, 128000.0);
|
|
70174
|
-
float off = 1.0 / 128.0;
|
|
70175
|
-
float s = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
|
|
70176
|
-
float layer = clamp(s * 64.0 - 0.5, 0.0, 63.0);
|
|
70177
|
-
float l0 = floor(layer);
|
|
70178
|
-
float l1 = min(l0 + 1.0, 63.0);
|
|
70179
|
-
vec3 apColor = mix(textureLod(apLut, vec3(vUV, l0), 0.0).rgb, textureLod(apLut, vec3(vUV, l1), 0.0).rgb, layer - l0);
|
|
70180
|
-
vec3 apOpacity = vec3(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
|
|
70181
|
-
apOpacity = pbDesaturate(apOpacity, pbZenith.w) * pbArt.x;
|
|
70182
|
-
float chi = acos(clamp(cosChi, -1.0, 1.0));
|
|
70183
|
-
float hor = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
|
|
70184
|
-
float nrm = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
|
|
70185
|
-
float te = exp(pbArt.y * nrm) * pbArt.z - pbArt.z;
|
|
70186
|
-
apColor *= mix(pbHorizon.xyz, pbZenith.xyz, te);
|
|
70187
|
-
color = color + (vec3(1.0) - opacity) * apColor;
|
|
70188
|
-
opacity = opacity + (vec3(1.0) - opacity) * apOpacity;
|
|
70189
|
-
}
|
|
70190
|
-
#endif
|
|
70191
|
-
gl_FragColor = vec4(src.rgb * (vec3(1.0) - opacity) + color, src.a);
|
|
70444
|
+
void fogAerialPerspective(vec2 uv, vec3 O, vec3 dir, float tFrag, out vec3 apColor, out vec3 apOpacity) {
|
|
70445
|
+
float r = length(O);
|
|
70446
|
+
float cosChi = dot(O / r, dir);
|
|
70447
|
+
float tAp = min(tFrag, 128000.0);
|
|
70448
|
+
float off = 1.0 / 128.0;
|
|
70449
|
+
float s = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
|
|
70450
|
+
float layer = clamp(s * 64.0 - 0.5, 0.0, 63.0);
|
|
70451
|
+
float l0 = floor(layer);
|
|
70452
|
+
float l1 = min(l0 + 1.0, 63.0);
|
|
70453
|
+
apColor = mix(textureLod(apLut, vec3(uv, l0), 0.0).rgb, textureLod(apLut, vec3(uv, l1), 0.0).rgb, layer - l0);
|
|
70454
|
+
apOpacity = vec3(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
|
|
70455
|
+
apOpacity = pbDesaturate(apOpacity, pbZenith.w) * pbArt.x;
|
|
70456
|
+
float chi = acos(clamp(cosChi, -1.0, 1.0));
|
|
70457
|
+
float hor = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
|
|
70458
|
+
float nrm = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
|
|
70459
|
+
float te = exp(pbArt.y * nrm) * pbArt.z - pbArt.z;
|
|
70460
|
+
apColor *= mix(pbHorizon.xyz, pbZenith.xyz, te);
|
|
70192
70461
|
}
|
|
70193
70462
|
`;
|
|
70194
70463
|
}
|
|
70195
|
-
static
|
|
70196
|
-
|
|
70197
|
-
const ap = (S != null) ? (S.WgslUniforms() + S.WgslCommon()) : "";
|
|
70198
|
-
return `varying vUV: vec2f;
|
|
70199
|
-
var textureSamplerSampler: sampler;
|
|
70200
|
-
var textureSampler: texture_2d<f32>;
|
|
70201
|
-
var depthSamplerSampler: sampler;
|
|
70202
|
-
var depthSampler: texture_2d<f32>;
|
|
70203
|
-
#ifdef HDRP_FOG_ENV
|
|
70204
|
-
var fogEnvSamplerSampler: sampler;
|
|
70205
|
-
var fogEnvSampler: texture_cube<f32>;
|
|
70206
|
-
#endif
|
|
70207
|
-
uniform fogA: vec4f;
|
|
70208
|
-
uniform fogB: vec4f;
|
|
70209
|
-
uniform fogC: vec4f;
|
|
70210
|
-
uniform fogD: vec4f;
|
|
70211
|
-
uniform fogE: vec4f;
|
|
70212
|
-
uniform fogSunDir: vec4f;
|
|
70213
|
-
uniform fogSun: vec4f;
|
|
70214
|
-
uniform fogEnv: vec4f;
|
|
70215
|
-
uniform fogSky: vec4f;
|
|
70216
|
-
${HdrpFogPass.AmbientDeclarations(true)}uniform fogEye: vec4f;
|
|
70217
|
-
uniform fogForward: vec4f;
|
|
70218
|
-
uniform fogViewport: vec4f;
|
|
70219
|
-
uniform fogInput: vec4f;
|
|
70220
|
-
uniform fogInvViewProj: mat4x4f;
|
|
70221
|
-
uniform fogEnvMatrix: mat4x4f;
|
|
70222
|
-
#ifdef HDRP_FOG_AP
|
|
70223
|
-
var apLutSampler: sampler;
|
|
70224
|
-
var apLut: texture_2d_array<f32>;
|
|
70225
|
-
uniform fogApCamera: vec4f;
|
|
70226
|
-
${ap}
|
|
70464
|
+
static AerialPerspectiveWgsl() {
|
|
70465
|
+
return `
|
|
70227
70466
|
fn fogRescaledChapman(z: vec2f, Z: vec2f, c: f32) -> vec2f {
|
|
70228
70467
|
let sinTheta: f32 = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
|
|
70229
70468
|
var ch: vec2f = vec2f(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
|
|
@@ -70252,6 +70491,218 @@ fn fogApOpticalDepth(O: vec3f, V: vec3f, t: f32) -> vec3f {
|
|
|
70252
70491
|
let ozone: f32 = pbOzoneOpticalDepth(r, cosChi, t);
|
|
70253
70492
|
return od.x * uniforms.pbAirExt.xyz + od.y * uniforms.pbAirExt.w + ozone * uniforms.pbOzone0.xyz;
|
|
70254
70493
|
}
|
|
70494
|
+
struct FogAerial {
|
|
70495
|
+
color: vec3f,
|
|
70496
|
+
opacity: vec3f,
|
|
70497
|
+
};
|
|
70498
|
+
fn fogAerialPerspective(uv: vec2f, O: vec3f, dir: vec3f, tFrag: f32) -> FogAerial {
|
|
70499
|
+
let r: f32 = length(O);
|
|
70500
|
+
let cosChi: f32 = dot(O / r, dir);
|
|
70501
|
+
let tAp: f32 = min(tFrag, 128000.0);
|
|
70502
|
+
let off: f32 = 1.0 / 128.0;
|
|
70503
|
+
let s: f32 = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
|
|
70504
|
+
let layer: f32 = clamp(s * 64.0 - 0.5, 0.0, 63.0);
|
|
70505
|
+
let l0: f32 = floor(layer);
|
|
70506
|
+
let l1: f32 = min(l0 + 1.0, 63.0);
|
|
70507
|
+
let c0: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l0), 0.0).rgb;
|
|
70508
|
+
let c1: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l1), 0.0).rgb;
|
|
70509
|
+
var apColor: vec3f = mix(c0, c1, vec3f(layer - l0));
|
|
70510
|
+
var apOpacity: vec3f = vec3f(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
|
|
70511
|
+
apOpacity = pbDesaturate(apOpacity, uniforms.pbZenith.w) * uniforms.pbArt.x;
|
|
70512
|
+
let chi: f32 = acos(clamp(cosChi, -1.0, 1.0));
|
|
70513
|
+
let hor: f32 = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
|
|
70514
|
+
let nrm: f32 = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
|
|
70515
|
+
let te: f32 = exp(uniforms.pbArt.y * nrm) * uniforms.pbArt.z - uniforms.pbArt.z;
|
|
70516
|
+
apColor *= mix(uniforms.pbHorizon.xyz, uniforms.pbZenith.xyz, vec3f(te));
|
|
70517
|
+
return FogAerial(apColor, apOpacity);
|
|
70518
|
+
}
|
|
70519
|
+
`;
|
|
70520
|
+
}
|
|
70521
|
+
static GlslHelpers(prefix) {
|
|
70522
|
+
return `
|
|
70523
|
+
float ${prefix}OpticalDepth(float ext, float baseH, vec2 exps, float cosZ, float startH, float len) {
|
|
70524
|
+
float absZ = max(abs(cosZ), 0.001);
|
|
70525
|
+
float rcpAbsZ = 1.0 / absZ;
|
|
70526
|
+
float endH = startH + len * cosZ;
|
|
70527
|
+
float minH = min(startH, endH);
|
|
70528
|
+
float h = max(minH - baseH, 0.0);
|
|
70529
|
+
float homDist = clamp((baseH - minH) * rcpAbsZ, 0.0, len);
|
|
70530
|
+
float expDist = len - homDist;
|
|
70531
|
+
float expMult = exp(-h * exps.x) * (1.0 - exp(-expDist * absZ * exps.x)) * (rcpAbsZ * exps.y);
|
|
70532
|
+
return ext * (homDist + expMult);
|
|
70533
|
+
}
|
|
70534
|
+
float ${prefix}PhaseHG(float g, float c) {
|
|
70535
|
+
float g2 = g * g;
|
|
70536
|
+
return (1.0 - g2) / (12.566370614 * pow(max(1.0 + g2 - 2.0 * g * c, 0.0001), 1.5));
|
|
70537
|
+
}
|
|
70538
|
+
`;
|
|
70539
|
+
}
|
|
70540
|
+
static WgslHelpers(prefix) {
|
|
70541
|
+
return `
|
|
70542
|
+
fn ${prefix}OpticalDepth(ext: f32, baseH: f32, exps: vec2f, cosZ: f32, startH: f32, len: f32) -> f32 {
|
|
70543
|
+
let absZ: f32 = max(abs(cosZ), 0.001);
|
|
70544
|
+
let rcpAbsZ: f32 = 1.0 / absZ;
|
|
70545
|
+
let endH: f32 = startH + len * cosZ;
|
|
70546
|
+
let minH: f32 = min(startH, endH);
|
|
70547
|
+
let h: f32 = max(minH - baseH, 0.0);
|
|
70548
|
+
let homDist: f32 = clamp((baseH - minH) * rcpAbsZ, 0.0, len);
|
|
70549
|
+
let expDist: f32 = len - homDist;
|
|
70550
|
+
let expMult: f32 = exp(-h * exps.x) * (1.0 - exp(-expDist * absZ * exps.x)) * (rcpAbsZ * exps.y);
|
|
70551
|
+
return ext * (homDist + expMult);
|
|
70552
|
+
}
|
|
70553
|
+
fn ${prefix}PhaseHG(g: f32, c: f32) -> f32 {
|
|
70554
|
+
let g2: f32 = g * g;
|
|
70555
|
+
return (1.0 - g2) / (12.566370614 * pow(max(1.0 + g2 - 2.0 * g * c, 0.0001), 1.5));
|
|
70556
|
+
}
|
|
70557
|
+
`;
|
|
70558
|
+
}
|
|
70559
|
+
static Glsl() {
|
|
70560
|
+
const S = HdrpPbSkyShaders;
|
|
70561
|
+
const ap = (S != null) ? (S.GlslUniforms() + S.GlslCommon()) : "";
|
|
70562
|
+
return `#ifdef HDRP_FOG_AP
|
|
70563
|
+
precision highp sampler2DArray;
|
|
70564
|
+
#endif
|
|
70565
|
+
precision highp float;
|
|
70566
|
+
#define FOG_DISPLAY_GAMMA 2.2
|
|
70567
|
+
varying vec2 vUV;
|
|
70568
|
+
uniform sampler2D textureSampler;
|
|
70569
|
+
uniform sampler2D depthSampler;
|
|
70570
|
+
#ifdef HDRP_FOG_ENV
|
|
70571
|
+
uniform samplerCube fogEnvSampler;
|
|
70572
|
+
#endif
|
|
70573
|
+
uniform vec4 fogA;
|
|
70574
|
+
uniform vec4 fogB;
|
|
70575
|
+
uniform vec4 fogC;
|
|
70576
|
+
uniform vec4 fogD;
|
|
70577
|
+
uniform vec4 fogE;
|
|
70578
|
+
uniform vec4 fogSunDir;
|
|
70579
|
+
uniform vec4 fogSun;
|
|
70580
|
+
uniform vec4 fogEnv;
|
|
70581
|
+
uniform vec4 fogSky;
|
|
70582
|
+
${HdrpFogPass.AmbientDeclarations(false)}uniform vec4 fogEye;
|
|
70583
|
+
uniform vec4 fogForward;
|
|
70584
|
+
uniform vec4 fogViewport;
|
|
70585
|
+
uniform vec4 fogInput;
|
|
70586
|
+
uniform vec4 fogChain;
|
|
70587
|
+
uniform mat4 fogInvViewProj;
|
|
70588
|
+
uniform mat4 fogEnvMatrix;
|
|
70589
|
+
#ifdef HDRP_FOG_AP
|
|
70590
|
+
uniform sampler2DArray apLut;
|
|
70591
|
+
uniform vec4 fogApCamera;
|
|
70592
|
+
${ap}
|
|
70593
|
+
${HdrpFogPass.AerialPerspectiveGlsl()}
|
|
70594
|
+
#endif
|
|
70595
|
+
${HdrpFogPass.AmbientGlsl()}${HdrpFogPass.GlslHelpers("hdrpFog")}${HdrpFogPass.LocalGlsl()}
|
|
70596
|
+
vec3 hdrpFogColor(vec3 dir, float dist) {
|
|
70597
|
+
vec3 c = fogC.rgb;
|
|
70598
|
+
if (fogB.z > 0.5) {
|
|
70599
|
+
#ifdef HDRP_FOG_ENV
|
|
70600
|
+
float t = clamp((dist - fogD.x) / max(fogD.y - fogD.x, 0.0001), 0.0, 1.0);
|
|
70601
|
+
float lod = (1.0 - fogD.z * t) * fogD.w;
|
|
70602
|
+
vec3 d = (fogEnvMatrix * vec4(dir, 0.0)).xyz;
|
|
70603
|
+
vec4 e = textureLod(fogEnvSampler, d, lod);
|
|
70604
|
+
vec3 rgb = (fogEnv.y > 0.5) ? pow(e.rgb, vec3(2.2)) / max(e.a, 0.0001) : e.rgb;
|
|
70605
|
+
c *= rgb * fogEnv.x;
|
|
70606
|
+
#else
|
|
70607
|
+
c *= fogSky.rgb;
|
|
70608
|
+
#endif
|
|
70609
|
+
}
|
|
70610
|
+
return c * fogC.w;
|
|
70611
|
+
}
|
|
70612
|
+
void main(void) {
|
|
70613
|
+
vec4 src = texture2D(textureSampler, fogInput.xy + vUV * fogInput.zw);
|
|
70614
|
+
float raw = abs(texture2D(depthSampler, fogViewport.xy + vUV * fogViewport.zw).r);
|
|
70615
|
+
vec4 farP = fogInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
|
|
70616
|
+
vec3 dir = normalize(farP.xyz / farP.w - fogEye.xyz);
|
|
70617
|
+
bool isSky = (raw <= 0.0) || (raw >= fogForward.w);
|
|
70618
|
+
float tFrag = isSky ? fogA.w : raw / max(dot(dir, fogForward.xyz), 0.0001);
|
|
70619
|
+
vec3 color = vec3(0.0);
|
|
70620
|
+
vec3 opacity = vec3(0.0);
|
|
70621
|
+
if (fogA.x > 0.5 && fogEye.w > 0.5) {
|
|
70622
|
+
vec3 fc = hdrpFogColor(dir, tFrag);
|
|
70623
|
+
float volEnd = (fogB.w > 0.5) ? min(tFrag, fogEnv.w) : 0.0;
|
|
70624
|
+
float trCamera = 1.0;
|
|
70625
|
+
if (volEnd > 0.0) {
|
|
70626
|
+
trCamera = exp(-hdrpFogOpticalDepth(fogA.y, fogA.z, fogB.xy, dir.y, fogEye.y, volEnd));
|
|
70627
|
+
vec3 lit = hdrpFogAmbient(dir) * fogC.w * fogSun.w + fogSun.rgb * hdrpFogPhaseHG(fogE.w, dot(dir, fogSunDir.xyz)) * fogSunDir.w;
|
|
70628
|
+
color = lit * fogE.rgb * (1.0 - trCamera);
|
|
70629
|
+
}
|
|
70630
|
+
float delta = tFrag - volEnd;
|
|
70631
|
+
if (delta > 0.0) {
|
|
70632
|
+
float tr = exp(-hdrpFogOpticalDepth(fogA.y, fogA.z, fogB.xy, dir.y, fogEye.y + volEnd * dir.y, delta));
|
|
70633
|
+
color += trCamera * fc * fogE.rgb * (1.0 - tr);
|
|
70634
|
+
trCamera *= tr;
|
|
70635
|
+
}
|
|
70636
|
+
#if HDRP_FOG_LOCALS > 0
|
|
70637
|
+
if (fogB.w > 0.5) {
|
|
70638
|
+
vec4 lf = hdrpFogLocals(dir, min(tFrag, fogEnv.w));
|
|
70639
|
+
color = color * lf.a + lf.rgb;
|
|
70640
|
+
trCamera *= lf.a;
|
|
70641
|
+
}
|
|
70642
|
+
#endif
|
|
70643
|
+
opacity = vec3(1.0 - trCamera);
|
|
70644
|
+
}
|
|
70645
|
+
#ifdef HDRP_FOG_AP
|
|
70646
|
+
// fogChain.w: a URP sky with its fog on gives sky pixels aerial perspective up to the fog distance, as Unity's opaque pass does
|
|
70647
|
+
if ((!isSky || fogChain.w > 0.5) && fogApCamera.w > 0.5 && fogEye.w > 0.5) {
|
|
70648
|
+
vec3 apColor = vec3(0.0);
|
|
70649
|
+
vec3 apOpacity = vec3(0.0);
|
|
70650
|
+
fogAerialPerspective(vUV, fogApCamera.xyz, dir, tFrag, apColor, apOpacity);
|
|
70651
|
+
color = color + (vec3(1.0) - opacity) * apColor;
|
|
70652
|
+
opacity = opacity + (vec3(1.0) - opacity) * apOpacity;
|
|
70653
|
+
}
|
|
70654
|
+
#endif
|
|
70655
|
+
// fogChain.yz: a URP sky fogs before transparents, so a ray that meets the water before the opaque depth keeps its colour
|
|
70656
|
+
if (fogChain.y > 0.5 && dir.y < 0.0 && fogEye.y > fogChain.z) {
|
|
70657
|
+
float tWater = (fogChain.z - fogEye.y) / dir.y;
|
|
70658
|
+
if (isSky || tWater < tFrag) {
|
|
70659
|
+
color = vec3(0.0);
|
|
70660
|
+
opacity = vec3(0.0);
|
|
70661
|
+
}
|
|
70662
|
+
}
|
|
70663
|
+
// fogChain.x: the chain holds display values, so the fog composites in linear and encodes again
|
|
70664
|
+
vec3 scene = (fogChain.x > 0.5) ? pow(src.rgb, vec3(FOG_DISPLAY_GAMMA)) : src.rgb;
|
|
70665
|
+
vec3 fogged = scene * (vec3(1.0) - opacity) + color;
|
|
70666
|
+
if (fogChain.x > 0.5) fogged = pow(clamp(fogged, vec3(0.0), vec3(1.0)), vec3(1.0 / FOG_DISPLAY_GAMMA));
|
|
70667
|
+
gl_FragColor = vec4(fogged, src.a);
|
|
70668
|
+
}
|
|
70669
|
+
`;
|
|
70670
|
+
}
|
|
70671
|
+
static Wgsl() {
|
|
70672
|
+
const S = HdrpPbSkyShaders;
|
|
70673
|
+
const ap = (S != null) ? (S.WgslUniforms() + S.WgslCommon()) : "";
|
|
70674
|
+
return `varying vUV: vec2f;
|
|
70675
|
+
const FOG_DISPLAY_GAMMA: f32 = 2.2;
|
|
70676
|
+
var textureSamplerSampler: sampler;
|
|
70677
|
+
var textureSampler: texture_2d<f32>;
|
|
70678
|
+
var depthSamplerSampler: sampler;
|
|
70679
|
+
var depthSampler: texture_2d<f32>;
|
|
70680
|
+
#ifdef HDRP_FOG_ENV
|
|
70681
|
+
var fogEnvSamplerSampler: sampler;
|
|
70682
|
+
var fogEnvSampler: texture_cube<f32>;
|
|
70683
|
+
#endif
|
|
70684
|
+
uniform fogA: vec4f;
|
|
70685
|
+
uniform fogB: vec4f;
|
|
70686
|
+
uniform fogC: vec4f;
|
|
70687
|
+
uniform fogD: vec4f;
|
|
70688
|
+
uniform fogE: vec4f;
|
|
70689
|
+
uniform fogSunDir: vec4f;
|
|
70690
|
+
uniform fogSun: vec4f;
|
|
70691
|
+
uniform fogEnv: vec4f;
|
|
70692
|
+
uniform fogSky: vec4f;
|
|
70693
|
+
${HdrpFogPass.AmbientDeclarations(true)}uniform fogEye: vec4f;
|
|
70694
|
+
uniform fogForward: vec4f;
|
|
70695
|
+
uniform fogViewport: vec4f;
|
|
70696
|
+
uniform fogInput: vec4f;
|
|
70697
|
+
uniform fogChain: vec4f;
|
|
70698
|
+
uniform fogInvViewProj: mat4x4f;
|
|
70699
|
+
uniform fogEnvMatrix: mat4x4f;
|
|
70700
|
+
#ifdef HDRP_FOG_AP
|
|
70701
|
+
var apLutSampler: sampler;
|
|
70702
|
+
var apLut: texture_2d_array<f32>;
|
|
70703
|
+
uniform fogApCamera: vec4f;
|
|
70704
|
+
${ap}
|
|
70705
|
+
${HdrpFogPass.AerialPerspectiveWgsl()}
|
|
70255
70706
|
#endif
|
|
70256
70707
|
${HdrpFogPass.AmbientWgsl()}${HdrpFogPass.WgslHelpers("hdrpFog")}${HdrpFogPass.LocalWgsl()}
|
|
70257
70708
|
fn hdrpFogColor(dir: vec3f, dist: f32) -> vec3f {
|
|
@@ -70307,31 +70758,29 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
70307
70758
|
opacity = vec3f(1.0 - trCamera);
|
|
70308
70759
|
}
|
|
70309
70760
|
#ifdef HDRP_FOG_AP
|
|
70310
|
-
|
|
70311
|
-
|
|
70312
|
-
let
|
|
70313
|
-
let
|
|
70314
|
-
let
|
|
70315
|
-
let off: f32 = 1.0 / 128.0;
|
|
70316
|
-
let s: f32 = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
|
|
70317
|
-
let layer: f32 = clamp(s * 64.0 - 0.5, 0.0, 63.0);
|
|
70318
|
-
let l0: f32 = floor(layer);
|
|
70319
|
-
let l1: f32 = min(l0 + 1.0, 63.0);
|
|
70320
|
-
let c0: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l0), 0.0).rgb;
|
|
70321
|
-
let c1: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l1), 0.0).rgb;
|
|
70322
|
-
var apColor: vec3f = mix(c0, c1, vec3f(layer - l0));
|
|
70323
|
-
var apOpacity: vec3f = vec3f(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
|
|
70324
|
-
apOpacity = pbDesaturate(apOpacity, uniforms.pbZenith.w) * uniforms.pbArt.x;
|
|
70325
|
-
let chi: f32 = acos(clamp(cosChi, -1.0, 1.0));
|
|
70326
|
-
let hor: f32 = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
|
|
70327
|
-
let nrm: f32 = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
|
|
70328
|
-
let te: f32 = exp(uniforms.pbArt.y * nrm) * uniforms.pbArt.z - uniforms.pbArt.z;
|
|
70329
|
-
apColor *= mix(uniforms.pbHorizon.xyz, uniforms.pbZenith.xyz, vec3f(te));
|
|
70761
|
+
// fogChain.w: a URP sky with its fog on gives sky pixels aerial perspective up to the fog distance, as Unity's opaque pass does
|
|
70762
|
+
if ((!isSky || uniforms.fogChain.w > 0.5) && uniforms.fogApCamera.w > 0.5 && uniforms.fogEye.w > 0.5) {
|
|
70763
|
+
let aerial: FogAerial = fogAerialPerspective(uv, uniforms.fogApCamera.xyz, dir, tFrag);
|
|
70764
|
+
let apColor: vec3f = aerial.color;
|
|
70765
|
+
let apOpacity: vec3f = aerial.opacity;
|
|
70330
70766
|
color = color + (vec3f(1.0) - opacity) * apColor;
|
|
70331
70767
|
opacity = opacity + (vec3f(1.0) - opacity) * apOpacity;
|
|
70332
70768
|
}
|
|
70333
70769
|
#endif
|
|
70334
|
-
|
|
70770
|
+
// fogChain.yz: a URP sky fogs before transparents, so a ray that meets the water before the opaque depth keeps its colour
|
|
70771
|
+
if (uniforms.fogChain.y > 0.5 && dir.y < 0.0 && uniforms.fogEye.y > uniforms.fogChain.z) {
|
|
70772
|
+
let tWater: f32 = (uniforms.fogChain.z - uniforms.fogEye.y) / dir.y;
|
|
70773
|
+
if (isSky || tWater < tFrag) {
|
|
70774
|
+
color = vec3f(0.0);
|
|
70775
|
+
opacity = vec3f(0.0);
|
|
70776
|
+
}
|
|
70777
|
+
}
|
|
70778
|
+
// fogChain.x: the chain holds display values, so the fog composites in linear and encodes again
|
|
70779
|
+
var scene: vec3f = src.rgb;
|
|
70780
|
+
if (uniforms.fogChain.x > 0.5) { scene = pow(src.rgb, vec3f(FOG_DISPLAY_GAMMA)); }
|
|
70781
|
+
var fogged: vec3f = scene * (vec3f(1.0) - opacity) + color;
|
|
70782
|
+
if (uniforms.fogChain.x > 0.5) { fogged = pow(clamp(fogged, vec3f(0.0), vec3f(1.0)), vec3f(1.0 / FOG_DISPLAY_GAMMA)); }
|
|
70783
|
+
fragmentOutputs.color = vec4f(fogged, src.a);
|
|
70335
70784
|
}
|
|
70336
70785
|
`;
|
|
70337
70786
|
}
|
|
@@ -70412,6 +70861,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
70412
70861
|
effect.setFloat4("fogE", frame.e[0], frame.e[1], frame.e[2], frame.e[3]);
|
|
70413
70862
|
effect.setFloat4("fogSunDir", frame.sunDir[0], frame.sunDir[1], frame.sunDir[2], frame.sunDir[3]);
|
|
70414
70863
|
effect.setFloat4("fogSun", frame.sun[0], frame.sun[1], frame.sun[2], frame.sun[3]);
|
|
70864
|
+
HdrpFogPass.BindChain(effect, scene, state.fog);
|
|
70415
70865
|
effect.setFloat4("fogEnv", frame.env[0], frame.env[1], frame.env[2], frame.env[3]);
|
|
70416
70866
|
effect.setFloat4("fogSky", frame.sky[0], frame.sky[1], frame.sky[2], frame.sky[3]);
|
|
70417
70867
|
for (let i = 0; i < HdrpFogPass.AmbientVectors; i++)
|
|
@@ -70443,7 +70893,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
70443
70893
|
}
|
|
70444
70894
|
}
|
|
70445
70895
|
HdrpFogPass.ShaderName = "toolkitHdrpFog";
|
|
70446
|
-
HdrpFogPass.Uniforms = ["fogA", "fogB", "fogC", "fogD", "fogE", "fogSunDir", "fogSun", "fogEnv", "fogSky", "fogAmb0", "fogAmb1", "fogAmb2", "fogAmb3", "fogAmb4", "fogAmb5", "fogAmb6", "fogAmb7", "fogAmb8", "fogEye", "fogForward", "fogViewport", "fogInput", "fogInvViewProj", "fogEnvMatrix", "fogApCamera"];
|
|
70896
|
+
HdrpFogPass.Uniforms = ["fogA", "fogB", "fogC", "fogD", "fogE", "fogSunDir", "fogSun", "fogEnv", "fogSky", "fogAmb0", "fogAmb1", "fogAmb2", "fogAmb3", "fogAmb4", "fogAmb5", "fogAmb6", "fogAmb7", "fogAmb8", "fogEye", "fogForward", "fogViewport", "fogInput", "fogInvViewProj", "fogEnvMatrix", "fogApCamera", "fogChain"];
|
|
70447
70897
|
HdrpFogPass.Samplers = ["depthSampler", "fogEnvSampler", "apLut"];
|
|
70448
70898
|
HdrpFogPass.AmbientVectors = 9;
|
|
70449
70899
|
HdrpFogPass.registered = false;
|
|
@@ -71068,6 +71518,8 @@ export class HdrpPbSkyShaders {
|
|
|
71068
71518
|
#define PB_TWO_PI 6.28318530718
|
|
71069
71519
|
#define PB_HALF_PI 1.57079632679
|
|
71070
71520
|
#define PB_INV_PI 0.31830988618
|
|
71521
|
+
#define PB_DISPLAY_GAMMA 0.45454545454
|
|
71522
|
+
#define PB_NO_BODY_HIT 3.0e38
|
|
71071
71523
|
#define PB_FLT_MIN 1.175494351e-38
|
|
71072
71524
|
#define PB_FLT_EPS 5.960464478e-8
|
|
71073
71525
|
float pbSat(float x) { return clamp(x, 0.0, 1.0); }
|
|
@@ -71497,6 +71949,10 @@ vec2 pbCloudLighting${X}(vec2 uv) {
|
|
|
71497
71949
|
` + HdrpPbSkyShaders.GlslCloudUniforms() + `#ifdef PB_CLOUDS
|
|
71498
71950
|
uniform sampler2D cloudTex;
|
|
71499
71951
|
#endif
|
|
71952
|
+
#ifdef PBSKY_SPACE_EMISSION
|
|
71953
|
+
uniform samplerCube spaceCube;
|
|
71954
|
+
uniform vec4 pbSpace;
|
|
71955
|
+
#endif
|
|
71500
71956
|
` + HdrpPbSkyShaders.GlslCommon() + `
|
|
71501
71957
|
vec2 pbMapSkyView(float cosChi, vec3 V) {
|
|
71502
71958
|
float coord = pbFastACos(cosChi) / PB_HALF_PI;
|
|
@@ -71591,6 +72047,12 @@ vec3 pbRenderSky(vec3 d) {
|
|
|
71591
72047
|
}
|
|
71592
72048
|
}
|
|
71593
72049
|
}
|
|
72050
|
+
#ifdef PBSKY_SPACE_EMISSION
|
|
72051
|
+
if (lookAbove && tFrag >= PB_NO_BODY_HIT) {
|
|
72052
|
+
vec3 spaceDirection = vec3((pbSpace.y * d.x - pbSpace.z * d.z) * pbSpace.w, d.y, pbSpace.z * d.x + pbSpace.y * d.z);
|
|
72053
|
+
radiance += textureLod(spaceCube, spaceDirection, 0.0).rgb * pbSpace.x;
|
|
72054
|
+
}
|
|
72055
|
+
#endif
|
|
71594
72056
|
vec3 skyColor = vec3(0.0);
|
|
71595
72057
|
vec3 skyOpacity = vec3(0.0);
|
|
71596
72058
|
if (lookAbove) {
|
|
@@ -71613,7 +72075,12 @@ vec3 pbRenderSky(vec3 d) {
|
|
|
71613
72075
|
}
|
|
71614
72076
|
void main(void) {
|
|
71615
72077
|
vec3 d = normalize(vDir);
|
|
71616
|
-
|
|
72078
|
+
// pbCounts.w: a URP sky on an LDR camera. Its world has no ground below the horizon, where the sky pass has no in-scatter
|
|
72079
|
+
if (pbCounts.w > 0.5) d.y = max(d.y, 0.0);
|
|
72080
|
+
vec3 color = pbRenderSky(normalize(d)) * pbArt.w;
|
|
72081
|
+
// the same flag: the camera has no HDR chain, so the sky encodes for display as materials do
|
|
72082
|
+
if (pbCounts.w > 0.5) color = pow(clamp(color, vec3(0.0), vec3(1.0)), vec3(PB_DISPLAY_GAMMA));
|
|
72083
|
+
gl_FragColor = vec4(color, 1.0);
|
|
71617
72084
|
}
|
|
71618
72085
|
`;
|
|
71619
72086
|
}
|
|
@@ -71640,6 +72107,8 @@ const PB_PI: f32 = 3.14159265359;
|
|
|
71640
72107
|
const PB_TWO_PI: f32 = 6.28318530718;
|
|
71641
72108
|
const PB_HALF_PI: f32 = 1.57079632679;
|
|
71642
72109
|
const PB_INV_PI: f32 = 0.31830988618;
|
|
72110
|
+
const PB_DISPLAY_GAMMA: f32 = 0.45454545454;
|
|
72111
|
+
const PB_NO_BODY_HIT: f32 = 3.0e38;
|
|
71643
72112
|
const PB_FLT_MIN: f32 = 1.175494351e-38;
|
|
71644
72113
|
const PB_FLT_EPS: f32 = 5.960464478e-8;
|
|
71645
72114
|
fn pbSat(x: f32) -> f32 { return clamp(x, 0.0, 1.0); }
|
|
@@ -72082,6 +72551,11 @@ fn pbCloudLighting${X}(uv: vec2f) -> vec2f {
|
|
|
72082
72551
|
var cloudTexSampler: sampler;
|
|
72083
72552
|
var cloudTex: texture_2d<f32>;
|
|
72084
72553
|
#endif
|
|
72554
|
+
#ifdef PBSKY_SPACE_EMISSION
|
|
72555
|
+
var spaceCubeSampler: sampler;
|
|
72556
|
+
var spaceCube: texture_cube<f32>;
|
|
72557
|
+
uniform pbSpace: vec4f;
|
|
72558
|
+
#endif
|
|
72085
72559
|
` + HdrpPbSkyShaders.WgslCommon() + `
|
|
72086
72560
|
fn pbMapSkyView(cosChi: f32, V: vec3f) -> vec2f {
|
|
72087
72561
|
let coord: f32 = pbFastACos(cosChi) / PB_HALF_PI;
|
|
@@ -72177,6 +72651,12 @@ fn pbRenderSky(d: vec3f) -> vec3f {
|
|
|
72177
72651
|
}
|
|
72178
72652
|
}
|
|
72179
72653
|
}
|
|
72654
|
+
#ifdef PBSKY_SPACE_EMISSION
|
|
72655
|
+
if (lookAbove && tFrag >= PB_NO_BODY_HIT) {
|
|
72656
|
+
let spaceDirection: vec3f = vec3f((uniforms.pbSpace.y * d.x - uniforms.pbSpace.z * d.z) * uniforms.pbSpace.w, d.y, uniforms.pbSpace.z * d.x + uniforms.pbSpace.y * d.z);
|
|
72657
|
+
radiance += textureSampleLevel(spaceCube, spaceCubeSampler, spaceDirection, 0.0).rgb * uniforms.pbSpace.x;
|
|
72658
|
+
}
|
|
72659
|
+
#endif
|
|
72180
72660
|
var skyColor: vec3f = vec3f(0.0);
|
|
72181
72661
|
var skyOpacity: vec3f = vec3f(0.0);
|
|
72182
72662
|
if (lookAbove) {
|
|
@@ -72199,8 +72679,13 @@ fn pbRenderSky(d: vec3f) -> vec3f {
|
|
|
72199
72679
|
}
|
|
72200
72680
|
@fragment
|
|
72201
72681
|
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
72202
|
-
|
|
72203
|
-
|
|
72682
|
+
var d: vec3f = normalize(fragmentInputs.vDir);
|
|
72683
|
+
// pbCounts.w: a URP sky on an LDR camera. Its world has no ground below the horizon, where the sky pass has no in-scatter
|
|
72684
|
+
if (uniforms.pbCounts.w > 0.5) { d.y = max(d.y, 0.0); }
|
|
72685
|
+
var color: vec3f = pbRenderSky(normalize(d)) * uniforms.pbArt.w;
|
|
72686
|
+
// the same flag: the camera has no HDR chain, so the sky encodes for display as materials do
|
|
72687
|
+
if (uniforms.pbCounts.w > 0.5) { color = pow(clamp(color, vec3f(0.0), vec3f(1.0)), vec3f(PB_DISPLAY_GAMMA)); }
|
|
72688
|
+
fragmentOutputs.color = vec4f(color, 1.0);
|
|
72204
72689
|
}
|
|
72205
72690
|
`;
|
|
72206
72691
|
}
|
|
@@ -72453,17 +72938,16 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72453
72938
|
return (sky != null && sky.disposed !== true) ? sky : null;
|
|
72454
72939
|
}
|
|
72455
72940
|
static Decide(metadata, engine) {
|
|
72456
|
-
|
|
72457
|
-
|
|
72458
|
-
const sky = metadata.hdrp.sky;
|
|
72941
|
+
const sky = PhysicalSkyBlock.SkyOf(metadata);
|
|
72942
|
+
const isUrp = PhysicalSkyBlock.UrpBlock(metadata) != null && !HdrpRendering.IsParityExport(metadata);
|
|
72459
72943
|
if (sky == null)
|
|
72460
|
-
return { live: false, key: null, reason: "no
|
|
72944
|
+
return { live: false, key: null, reason: "no physical sky block" };
|
|
72461
72945
|
const type = (typeof sky.type === "string") ? sky.type.toLowerCase() : "";
|
|
72462
72946
|
if (type !== "physicallybased")
|
|
72463
72947
|
return { live: false, key: null, reason: "sky type " + (type || "unknown") };
|
|
72464
72948
|
if (HdrpPhysicallyBasedSky.IsSupported(engine) === false)
|
|
72465
72949
|
return { live: false, key: "hdrp:pbsky:unsupported", reason: "no WebGL2 / WebGPU half-float render target" };
|
|
72466
|
-
if (sky.volumetricclouds === true)
|
|
72950
|
+
if (isUrp === false && sky.volumetricclouds === true)
|
|
72467
72951
|
return { live: false, key: "hdrp:pbsky:volumetricclouds", reason: "an active VolumetricClouds is not ported (the bake carries it)" };
|
|
72468
72952
|
return { live: true, key: null, reason: "live" };
|
|
72469
72953
|
}
|
|
@@ -72475,11 +72959,12 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72475
72959
|
if (decision.key != null)
|
|
72476
72960
|
LightingConversions.WarnOnce(decision.key, "HDRP PhysicallyBasedSky: " + decision.reason + " - the baked sky is used");
|
|
72477
72961
|
if (decision.key != null)
|
|
72478
|
-
HdrpPhysicallyBasedSky.AttachBakedSkySunAttenuation(scene, metadata
|
|
72962
|
+
HdrpPhysicallyBasedSky.AttachBakedSkySunAttenuation(scene, PhysicalSkyBlock.SkyOf(metadata));
|
|
72479
72963
|
return null;
|
|
72480
72964
|
}
|
|
72481
72965
|
try {
|
|
72482
|
-
|
|
72966
|
+
const environment = (metadata.skybox != null) ? metadata.skybox.environment : null;
|
|
72967
|
+
return HdrpPhysicallyBasedSky.Create(scene, PhysicalSkyBlock.SkyOf(metadata), null, { root: root, bakedSky: (options != null) ? options.bakedSky : null, rendergroup: (options != null) ? options.rendergroup : 0, environmentSh: (environment != null) ? environment.sh : null });
|
|
72483
72968
|
}
|
|
72484
72969
|
catch (e) {
|
|
72485
72970
|
LightingConversions.WarnOnce("hdrp:pbsky:create", "HDRP PhysicallyBasedSky could not be created (" + ((e && e.message) ? e.message : e) + ") - the baked sky is used");
|
|
@@ -72565,6 +73050,13 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72565
73050
|
}
|
|
72566
73051
|
return precomputationHash !== state.lastPrecompute || lightHash !== state.lastLight || state.requested === true;
|
|
72567
73052
|
}
|
|
73053
|
+
static ProbeRenderOutcome(capturing, pendingFilter, drewSky) {
|
|
73054
|
+
if (capturing === true)
|
|
73055
|
+
return (drewSky === true) ? HdrpPhysicallyBasedSky.ProbeCaptureAccepted : HdrpPhysicallyBasedSky.ProbeCaptureRetry;
|
|
73056
|
+
if (pendingFilter === true && drewSky !== true)
|
|
73057
|
+
return HdrpPhysicallyBasedSky.ProbeCaptureRetry;
|
|
73058
|
+
return HdrpPhysicallyBasedSky.ProbeRenderKept;
|
|
73059
|
+
}
|
|
72568
73060
|
static SelectAmbientSh(skyAmbientMode, readback, exported) {
|
|
72569
73061
|
if (skyAmbientMode === 1)
|
|
72570
73062
|
return (readback != null) ? readback : exported;
|
|
@@ -72653,6 +73145,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72653
73145
|
this.filtering = null;
|
|
72654
73146
|
this.pendingFilter = false;
|
|
72655
73147
|
this.capturing = false;
|
|
73148
|
+
this.probeDrawsSky = false;
|
|
72656
73149
|
this.shReading = false;
|
|
72657
73150
|
this.readbackHarmonics = null;
|
|
72658
73151
|
this.exportedHarmonics = null;
|
|
@@ -72667,6 +73160,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72667
73160
|
this.startTime = Date.now();
|
|
72668
73161
|
this.scene = scene;
|
|
72669
73162
|
this.sky = sky || {};
|
|
73163
|
+
this.sunIntensityScale = (typeof this.sky.sunintensityscale === "number") ? this.sky.sunintensityscale : 1;
|
|
72670
73164
|
this.options = options;
|
|
72671
73165
|
this.fixedLights = lights;
|
|
72672
73166
|
this.coefficients = HdrpPbSkyMath.Coefficients(this.sky);
|
|
@@ -72717,16 +73211,23 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72717
73211
|
defines.push("#define PB_CLOUDS");
|
|
72718
73212
|
if (layers.length >= 2)
|
|
72719
73213
|
defines.push("#define PB_CLOUDS2");
|
|
73214
|
+
this.spaceCube = this.loadSpaceEmission();
|
|
73215
|
+
if (this.spaceCube != null)
|
|
73216
|
+
defines.push("#define PBSKY_SPACE_EMISSION");
|
|
72720
73217
|
const material = new ShaderMaterial("HDRP PBSky Material", scene, { vertex: HdrpPbSkyShaders.SkyName, fragment: HdrpPbSkyShaders.SkyName }, {
|
|
72721
73218
|
attributes: ["position"],
|
|
72722
|
-
uniforms: ["world", "viewProjection"].concat(HdrpPbSkyShaders.AtmosphereUniforms, HdrpPbSkyShaders.CloudUniforms),
|
|
72723
|
-
samplers: ["skyViewLut", "cloudTex"],
|
|
73219
|
+
uniforms: ["world", "viewProjection", "pbSpace"].concat(HdrpPbSkyShaders.AtmosphereUniforms, HdrpPbSkyShaders.CloudUniforms),
|
|
73220
|
+
samplers: ["skyViewLut", "cloudTex", "spaceCube"],
|
|
72724
73221
|
defines: defines,
|
|
72725
73222
|
shaderLanguage: language
|
|
72726
73223
|
});
|
|
72727
73224
|
material.backFaceCulling = false;
|
|
72728
73225
|
material.disableDepthWrite = true;
|
|
72729
73226
|
material.setTexture("skyViewLut", this.skyViewLut);
|
|
73227
|
+
if (this.spaceCube != null) {
|
|
73228
|
+
material.setTexture("spaceCube", this.spaceCube);
|
|
73229
|
+
material.setVector4("pbSpace", this.spaceEmissionUniform());
|
|
73230
|
+
}
|
|
72730
73231
|
if (layers.length > 0) {
|
|
72731
73232
|
for (let i = 0; i < layers.length; i++)
|
|
72732
73233
|
this.cloudMaps.push(this.loadCloudMap(layers[i]));
|
|
@@ -72865,7 +73366,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72865
73366
|
let exposure = 1;
|
|
72866
73367
|
const body = new Array(HdrpPbSkyShaders.BodyVec4s * HdrpPbSkyShaders.MaxBodies * 4).fill(0);
|
|
72867
73368
|
const peSafe = (pe > 0) ? pe : 1;
|
|
72868
|
-
const lux = (l) => l.intensity / peSafe;
|
|
73369
|
+
const lux = (l) => (l.intensity * this.sunIntensityScale) / peSafe;
|
|
72869
73370
|
const sunFor = (self) => {
|
|
72870
73371
|
let best = null, max = 0;
|
|
72871
73372
|
for (const l of this.scene.lights)
|
|
@@ -72938,7 +73439,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72938
73439
|
U.pbHorizon = [horizon[0], horizon[1], horizon[2], (typeof p.colorsaturation === "number") ? p.colorsaturation : 1];
|
|
72939
73440
|
U.pbZenith = [zenith[0], zenith[1], zenith[2], (typeof p.alphasaturation === "number") ? p.alphasaturation : 1];
|
|
72940
73441
|
U.pbArt = [(typeof p.alphamultiplier === "number") ? p.alphamultiplier : 1, exp[0], exp[1], pe];
|
|
72941
|
-
U.pbCounts = [lightCount, lights.length, 1, 0];
|
|
73442
|
+
U.pbCounts = [lightCount, lights.length, 1, this.encodesForDisplay() ? 1 : 0];
|
|
72942
73443
|
U.pbBody = body;
|
|
72943
73444
|
this.writeCloudUniforms(lights);
|
|
72944
73445
|
}
|
|
@@ -72973,14 +73474,16 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72973
73474
|
}
|
|
72974
73475
|
}
|
|
72975
73476
|
cloudAmbient() {
|
|
72976
|
-
|
|
73477
|
+
return this.ambientAt(new Vector3(0, -1, 0));
|
|
73478
|
+
}
|
|
73479
|
+
ambientAt(direction) {
|
|
72977
73480
|
if (this.ambientPolynomial != null)
|
|
72978
|
-
return HdrpPbSkyMath.EvaluatePolynomial(this.ambientPolynomial,
|
|
73481
|
+
return HdrpPbSkyMath.EvaluatePolynomial(this.ambientPolynomial, direction);
|
|
72979
73482
|
if (this.exportedHarmonics == null)
|
|
72980
73483
|
this.exportedHarmonics = this.parseExportedAmbient();
|
|
72981
73484
|
if (this.exportedHarmonics == null)
|
|
72982
73485
|
return new Color3(0, 0, 0);
|
|
72983
|
-
return HdrpPbSkyMath.EvaluatePolynomial(LightingConversions.PolynomialFromPreScaled(this.exportedHarmonics, 1),
|
|
73486
|
+
return HdrpPbSkyMath.EvaluatePolynomial(LightingConversions.PolynomialFromPreScaled(this.exportedHarmonics, 1), direction);
|
|
72984
73487
|
}
|
|
72985
73488
|
cloudLayers() {
|
|
72986
73489
|
const cl = this.sky.cloudlayer;
|
|
@@ -72988,6 +73491,22 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72988
73491
|
return [];
|
|
72989
73492
|
return cl.layerlist.filter((l) => l != null && l.cloudmap != null && typeof l.cloudmap.url === "string").slice(0, 2);
|
|
72990
73493
|
}
|
|
73494
|
+
loadSpaceEmission() {
|
|
73495
|
+
const url = (this.sky.params != null) ? this.sky.params.spaceemissionurl : null;
|
|
73496
|
+
if (typeof url !== "string")
|
|
73497
|
+
return null;
|
|
73498
|
+
const root = (typeof this.options.root === "string") ? this.options.root : "";
|
|
73499
|
+
const cube = new CubeTexture(root + url, this.scene, null, false, null, null, null, Constants.TEXTUREFORMAT_RGBA, false, ".env");
|
|
73500
|
+
cube.name = "HDRP PBSky Space Emission";
|
|
73501
|
+
return cube;
|
|
73502
|
+
}
|
|
73503
|
+
spaceEmissionUniform() {
|
|
73504
|
+
const params = this.sky.params;
|
|
73505
|
+
const rotation = Array.isArray(params.spacerotation) ? params.spacerotation : [0, 0, 0];
|
|
73506
|
+
const radians = Tools.ToRadians(rotation[1]);
|
|
73507
|
+
const multiplier = (typeof params.spaceemissionmultiplier === "number") ? params.spaceemissionmultiplier : 1;
|
|
73508
|
+
return new Vector4(multiplier, Math.cos(radians), Math.sin(radians), this.scene.useRightHandedSystem ? -1 : 1);
|
|
73509
|
+
}
|
|
72991
73510
|
loadCloudMap(layer) {
|
|
72992
73511
|
const url = ((typeof this.options.root === "string") ? this.options.root : "") + layer.cloudmap.url;
|
|
72993
73512
|
const tex = new Texture(url, this.scene, true, true, Texture.BILINEAR_SAMPLINGMODE);
|
|
@@ -73019,7 +73538,10 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73019
73538
|
this.applyMaterialUniforms();
|
|
73020
73539
|
return;
|
|
73021
73540
|
}
|
|
73022
|
-
this.material.setVector4("pbCounts", new Vector4(counts[0], counts[1], (this.sky.includesuninbaking === true) ? 1 : 0,
|
|
73541
|
+
this.material.setVector4("pbCounts", new Vector4(counts[0], counts[1], (this.sky.includesuninbaking === true) ? 1 : 0, 0));
|
|
73542
|
+
}
|
|
73543
|
+
encodesForDisplay() {
|
|
73544
|
+
return HdrpRendering.IsParity(this.scene) !== true && this.scene.imageProcessingConfiguration.applyByPostProcess !== true;
|
|
73023
73545
|
}
|
|
73024
73546
|
applyMaterialUniforms() {
|
|
73025
73547
|
const m = this.material;
|
|
@@ -73048,7 +73570,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73048
73570
|
const p = this.sky.params || {};
|
|
73049
73571
|
if (p.renderingmode === 1)
|
|
73050
73572
|
LightingConversions.WarnOnce("hdrp:pbsky:material", "HDRP PhysicallyBasedSky: the Material rendering mode (a Shader Graph sky) renders the default live atmosphere");
|
|
73051
|
-
if (p.hasgroundtextures === true || p.hasspaceemission === true)
|
|
73573
|
+
if (p.hasgroundtextures === true || (p.hasspaceemission === true && typeof p.spaceemissionurl !== "string"))
|
|
73052
73574
|
LightingConversions.WarnOnce("hdrp:pbsky:textures", "HDRP PhysicallyBasedSky: ground / space emission textures are not rendered");
|
|
73053
73575
|
for (const layer of this.cloudLayers())
|
|
73054
73576
|
if (typeof layer.distortion === "number" && layer.distortion !== 0)
|
|
@@ -73081,20 +73603,28 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73081
73603
|
scene.customRenderTargets.push(probe.cubeTexture);
|
|
73082
73604
|
const p = this.sky.params || {};
|
|
73083
73605
|
probe.cubeTexture.onBeforeRenderObservable.add(() => {
|
|
73084
|
-
|
|
73085
|
-
|
|
73606
|
+
const subMesh = (this.mesh.subMeshes != null) ? this.mesh.subMeshes[0] : null;
|
|
73607
|
+
this.probeDrawsSky = (subMesh != null) ? this.material.isReadyForSubMesh(this.mesh, subMesh) : this.material.isReady(this.mesh);
|
|
73086
73608
|
const art = this.uniforms.pbArt, counts = this.uniforms.pbCounts;
|
|
73087
73609
|
if (art != null)
|
|
73088
73610
|
this.material.setVector4("pbArt", new Vector4(art[0], art[1], art[2], 1));
|
|
73089
73611
|
if (counts != null)
|
|
73090
|
-
this.material.setVector4("pbCounts", new Vector4(counts[0], counts[1], (this.sky.includesuninbaking === true) ? 1 : 0,
|
|
73612
|
+
this.material.setVector4("pbCounts", new Vector4(counts[0], counts[1], (this.sky.includesuninbaking === true) ? 1 : 0, 0));
|
|
73091
73613
|
});
|
|
73092
73614
|
probe.cubeTexture.onAfterRenderObservable.add((faceIndex) => {
|
|
73093
|
-
if (
|
|
73615
|
+
if (typeof faceIndex === "number" && faceIndex < 5)
|
|
73094
73616
|
return;
|
|
73095
|
-
this.capturing = false;
|
|
73096
73617
|
this.applyMaterialUniforms();
|
|
73097
|
-
this.pendingFilter
|
|
73618
|
+
const state = HdrpPhysicallyBasedSky.ProbeRenderOutcome(this.capturing, this.pendingFilter, this.probeDrawsSky);
|
|
73619
|
+
if (state === HdrpPhysicallyBasedSky.ProbeCaptureAccepted) {
|
|
73620
|
+
this.capturing = false;
|
|
73621
|
+
this.pendingFilter = true;
|
|
73622
|
+
}
|
|
73623
|
+
else if (state === HdrpPhysicallyBasedSky.ProbeCaptureRetry) {
|
|
73624
|
+
this.capturing = true;
|
|
73625
|
+
this.pendingFilter = false;
|
|
73626
|
+
probe.cubeTexture.resetRefreshCounter();
|
|
73627
|
+
}
|
|
73098
73628
|
});
|
|
73099
73629
|
this.exportedHarmonics = this.parseExportedAmbient();
|
|
73100
73630
|
}
|
|
@@ -73297,6 +73827,8 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73297
73827
|
this.apTarget = engine.createRenderTargetTexture({ width: size[0], height: size[1], layers: size[2] }, { generateMipMaps: false, type: this.textureType, samplingMode: Constants.TEXTURE_BILINEAR_SAMPLINGMODE, format: Constants.TEXTUREFORMAT_RGBA, generateDepthBuffer: false, generateStencilBuffer: false, label: "HDRP PBSky Aerial Perspective" });
|
|
73298
73828
|
this.apTexture = new BaseTexture(scene, this.apTarget.texture);
|
|
73299
73829
|
this.apTexture.name = "HDRP PBSky Aerial Perspective";
|
|
73830
|
+
this.apTexture.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
|
|
73831
|
+
this.apTexture.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
|
|
73300
73832
|
LightingConversions.WarnOnce("hdrp:pbsky:aerialshadow", "HDRP PhysicallyBasedSky: the aerial perspective has no shadowed in-scatter (the sun shadow map is not sampled)");
|
|
73301
73833
|
}
|
|
73302
73834
|
if (!this.apPass.isReady())
|
|
@@ -73385,7 +73917,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73385
73917
|
catch (e) { }
|
|
73386
73918
|
for (const light of this.attenuated)
|
|
73387
73919
|
HdrpPhysicallyBasedSky.RestoreLight(light);
|
|
73388
|
-
const disposables = [this.msPass, this.viewPass, this.cloudPass, this.renderer, this.msLut, this.skyViewLut, this.cloudTex, this.material, this.mesh].concat(this.cloudMaps);
|
|
73920
|
+
const disposables = [this.msPass, this.viewPass, this.cloudPass, this.renderer, this.msLut, this.skyViewLut, this.cloudTex, this.spaceCube, this.material, this.mesh].concat(this.cloudMaps);
|
|
73389
73921
|
for (const d of disposables) {
|
|
73390
73922
|
try {
|
|
73391
73923
|
if (d != null)
|
|
@@ -73404,6 +73936,9 @@ HdrpPhysicallyBasedSky.AerialPerspectiveSize = [32, 32, 64];
|
|
|
73404
73936
|
HdrpPhysicallyBasedSky.AerialMaxDistance = 128000;
|
|
73405
73937
|
HdrpPhysicallyBasedSky.MeshName = "HDRP Physically Based Sky";
|
|
73406
73938
|
HdrpPhysicallyBasedSky.OnEnvironmentUpdated = new Observable();
|
|
73939
|
+
HdrpPhysicallyBasedSky.ProbeRenderKept = 0;
|
|
73940
|
+
HdrpPhysicallyBasedSky.ProbeCaptureAccepted = 1;
|
|
73941
|
+
HdrpPhysicallyBasedSky.ProbeCaptureRetry = 2;
|
|
73407
73942
|
HdrpPhysicallyBasedSky.RealtimeMinFrames = 9;
|
|
73408
73943
|
HdrpPhysicallyBasedSky.ShProjectionFaceSize = 64;
|
|
73409
73944
|
HdrpPhysicallyBasedSky.CaptureFaceCount = 6;
|
|
@@ -91714,6 +92249,238 @@ LightProbeNetwork.DebugProbeSize = 0.1;
|
|
|
91714
92249
|
LightProbeNetwork.InspectorSection = true;
|
|
91715
92250
|
LightProbeNetwork.OnNetworkChangedObservable = new Observable();
|
|
91716
92251
|
LightProbeNetwork.Sections = ["positions", "probesh", "tetraindices", "tetraadjacency", "renderersh", "rendererocclusion", "rendereranchor"];
|
|
92252
|
+
export class MeshWaterSurface extends ScriptComponent {
|
|
92253
|
+
constructor(transform, scene, properties = {}, alias = "TOOLKIT.MeshWaterSurface") {
|
|
92254
|
+
super(transform, scene, properties, alias);
|
|
92255
|
+
this.waterProperties = null;
|
|
92256
|
+
this.isUnderwater = false;
|
|
92257
|
+
this.fogColorChannels = [0, 0, 0];
|
|
92258
|
+
this.surfaceFogColor = new Color3(0, 0, 0);
|
|
92259
|
+
this.surfaceFogDensity = 0;
|
|
92260
|
+
this.surfaceFogStart = 0;
|
|
92261
|
+
this.surfaceFogEnd = 0;
|
|
92262
|
+
this.shownNodes = [];
|
|
92263
|
+
this.shownParticles = [];
|
|
92264
|
+
this.volumeNodes = [];
|
|
92265
|
+
this.flippedNodes = [];
|
|
92266
|
+
this.followers = [];
|
|
92267
|
+
this.followPosition = new Vector3(0, 0, 0);
|
|
92268
|
+
}
|
|
92269
|
+
static Get(scene) {
|
|
92270
|
+
if (scene == null)
|
|
92271
|
+
return null;
|
|
92272
|
+
return MeshWaterSurface.Registry.get(scene.uniqueId) ?? null;
|
|
92273
|
+
}
|
|
92274
|
+
awake() {
|
|
92275
|
+
this.waterProperties = this.getProperties();
|
|
92276
|
+
if (MeshWaterSurface.Registry.has(this.scene.uniqueId)) {
|
|
92277
|
+
Tools.Warn("MeshWaterSurface: '" + this.transform.name + "' replaces the scene's first water surface");
|
|
92278
|
+
}
|
|
92279
|
+
MeshWaterSurface.Registry.set(this.scene.uniqueId, this);
|
|
92280
|
+
}
|
|
92281
|
+
ready() {
|
|
92282
|
+
const properties = this.getProperties();
|
|
92283
|
+
if (properties.underwater == null)
|
|
92284
|
+
return;
|
|
92285
|
+
for (const node of this.findNodes(properties.underwater.shownodes)) {
|
|
92286
|
+
for (const component of SceneManager.GetComponents(node) ?? [])
|
|
92287
|
+
ScriptComponent.AwakeInstance(component);
|
|
92288
|
+
}
|
|
92289
|
+
}
|
|
92290
|
+
start() {
|
|
92291
|
+
const underwater = this.waterProperties.underwater;
|
|
92292
|
+
if (underwater == null)
|
|
92293
|
+
return;
|
|
92294
|
+
this.shownNodes = this.findNodes(underwater.shownodes);
|
|
92295
|
+
this.shownParticles = this.findParticles(this.shownNodes);
|
|
92296
|
+
this.volumeNodes = this.findNodes(underwater.volumenodes);
|
|
92297
|
+
this.flippedNodes = this.findNodes(underwater.flipnodes).map((node) => this.prepareFlip(node));
|
|
92298
|
+
this.followers = this.prepareFollowers(underwater.follownodes);
|
|
92299
|
+
this.update();
|
|
92300
|
+
}
|
|
92301
|
+
update() {
|
|
92302
|
+
if (this.waterProperties == null || this.waterProperties.underwater == null)
|
|
92303
|
+
return;
|
|
92304
|
+
const camera = this.scene.activeCamera;
|
|
92305
|
+
if (camera == null)
|
|
92306
|
+
return;
|
|
92307
|
+
const isCameraUnder = this.isCameraUnderwater(camera);
|
|
92308
|
+
if (isCameraUnder !== this.isUnderwater) {
|
|
92309
|
+
this.isUnderwater = isCameraUnder;
|
|
92310
|
+
if (isCameraUnder)
|
|
92311
|
+
this.enterUnderwater();
|
|
92312
|
+
else
|
|
92313
|
+
this.leaveUnderwater();
|
|
92314
|
+
}
|
|
92315
|
+
if (!isCameraUnder)
|
|
92316
|
+
return;
|
|
92317
|
+
this.updateFog(this.waterProperties.underwater.sealevel - camera.globalPosition.y);
|
|
92318
|
+
this.updateFollowers(camera);
|
|
92319
|
+
}
|
|
92320
|
+
destroy() {
|
|
92321
|
+
if (this.isUnderwater)
|
|
92322
|
+
this.leaveUnderwater();
|
|
92323
|
+
this.isUnderwater = false;
|
|
92324
|
+
if (MeshWaterSurface.Registry.get(this.scene.uniqueId) === this)
|
|
92325
|
+
MeshWaterSurface.Registry.delete(this.scene.uniqueId);
|
|
92326
|
+
}
|
|
92327
|
+
getSeaLevel() {
|
|
92328
|
+
return this.waterProperties.sealevel;
|
|
92329
|
+
}
|
|
92330
|
+
getSurfaceHeight() {
|
|
92331
|
+
const properties = this.waterProperties;
|
|
92332
|
+
const clockSeconds = ShaderGlobals.GetClock(this.scene).time;
|
|
92333
|
+
return properties.sealevel + Math.sin(clockSeconds * properties.wavespeed) * properties.waveamplitude;
|
|
92334
|
+
}
|
|
92335
|
+
getCameraHeight(camera) {
|
|
92336
|
+
if (camera == null)
|
|
92337
|
+
return 0;
|
|
92338
|
+
return camera.globalPosition.y - this.getSurfaceHeight();
|
|
92339
|
+
}
|
|
92340
|
+
isCameraUnderwater(camera) {
|
|
92341
|
+
if (camera == null)
|
|
92342
|
+
return false;
|
|
92343
|
+
const underwater = this.waterProperties.underwater;
|
|
92344
|
+
const crossingHeight = (underwater != null) ? underwater.sealevel : this.getSurfaceHeight();
|
|
92345
|
+
return camera.globalPosition.y <= crossingHeight;
|
|
92346
|
+
}
|
|
92347
|
+
ProjectPointOnWaterSurface(searchParameters) {
|
|
92348
|
+
if (searchParameters == null || searchParameters.targetPositionWS == null)
|
|
92349
|
+
return null;
|
|
92350
|
+
const target = searchParameters.targetPositionWS;
|
|
92351
|
+
const targetX = Array.isArray(target) ? target[0] : target.x;
|
|
92352
|
+
const targetZ = Array.isArray(target) ? target[2] : target.z;
|
|
92353
|
+
const projectedPosition = new Vector3(targetX, this.getSurfaceHeight(), targetZ);
|
|
92354
|
+
return {
|
|
92355
|
+
projectedPositionWS: projectedPosition,
|
|
92356
|
+
normalWS: Vector3.Up(),
|
|
92357
|
+
candidateLocationWS: projectedPosition.clone(),
|
|
92358
|
+
currentDirectionWS: Vector3.Zero(),
|
|
92359
|
+
numIterations: 1,
|
|
92360
|
+
error: 0
|
|
92361
|
+
};
|
|
92362
|
+
}
|
|
92363
|
+
FindWaterSurfaceHeight(searchParameters) {
|
|
92364
|
+
return this.ProjectPointOnWaterSurface(searchParameters);
|
|
92365
|
+
}
|
|
92366
|
+
findNodes(nodeIds) {
|
|
92367
|
+
const nodes = [];
|
|
92368
|
+
for (const nodeId of nodeIds ?? []) {
|
|
92369
|
+
const node = SceneManager.GetTransformNodeByID(this.scene, nodeId);
|
|
92370
|
+
if (node != null)
|
|
92371
|
+
nodes.push(node);
|
|
92372
|
+
}
|
|
92373
|
+
return nodes;
|
|
92374
|
+
}
|
|
92375
|
+
findParticles(nodes) {
|
|
92376
|
+
const particles = [];
|
|
92377
|
+
for (const node of nodes) {
|
|
92378
|
+
const system = SceneManager.GetComponent(node, MeshWaterSurface.PARTICLES_CLASS);
|
|
92379
|
+
if (system != null)
|
|
92380
|
+
particles.push(system);
|
|
92381
|
+
}
|
|
92382
|
+
return particles;
|
|
92383
|
+
}
|
|
92384
|
+
prepareFlip(node) {
|
|
92385
|
+
if (node.isWorldMatrixFrozen)
|
|
92386
|
+
node.unfreezeWorldMatrix();
|
|
92387
|
+
if (node.rotationQuaternion == null)
|
|
92388
|
+
node.rotationQuaternion = Quaternion.FromEulerVector(node.rotation);
|
|
92389
|
+
const surfaceRotation = node.rotationQuaternion.clone();
|
|
92390
|
+
const flipRotation = Quaternion.FromEulerAngles(Math.PI, 0, 0);
|
|
92391
|
+
return { node: node, surfaceRotation: surfaceRotation, underwaterRotation: flipRotation.multiply(surfaceRotation) };
|
|
92392
|
+
}
|
|
92393
|
+
prepareFollowers(follownodes) {
|
|
92394
|
+
const followers = [];
|
|
92395
|
+
for (const follower of follownodes ?? []) {
|
|
92396
|
+
const node = SceneManager.GetTransformNodeByID(this.scene, follower.node);
|
|
92397
|
+
if (node == null)
|
|
92398
|
+
continue;
|
|
92399
|
+
if (follower.scale != null)
|
|
92400
|
+
node.scaling.set(follower.scale[0], follower.scale[1], follower.scale[2]);
|
|
92401
|
+
followers.push({ node: node, offset: Vector3.FromArray(follower.offset) });
|
|
92402
|
+
}
|
|
92403
|
+
return followers;
|
|
92404
|
+
}
|
|
92405
|
+
enterUnderwater() {
|
|
92406
|
+
const underwater = this.waterProperties.underwater;
|
|
92407
|
+
if (underwater.fogcolors.length > 0)
|
|
92408
|
+
this.writeUnderwaterFog(underwater);
|
|
92409
|
+
for (const node of this.shownNodes)
|
|
92410
|
+
node.setEnabled(true);
|
|
92411
|
+
for (const particles of this.shownParticles)
|
|
92412
|
+
particles.play();
|
|
92413
|
+
this.setVolumesEnabled(true);
|
|
92414
|
+
for (const flipped of this.flippedNodes)
|
|
92415
|
+
flipped.node.rotationQuaternion.copyFrom(flipped.underwaterRotation);
|
|
92416
|
+
}
|
|
92417
|
+
leaveUnderwater() {
|
|
92418
|
+
if (this.waterProperties.underwater.fogcolors.length > 0)
|
|
92419
|
+
this.restoreSurfaceFog();
|
|
92420
|
+
for (const particles of this.shownParticles)
|
|
92421
|
+
particles.stop(true, MeshWaterSurface.STOP_AND_CLEAR);
|
|
92422
|
+
for (const node of this.shownNodes)
|
|
92423
|
+
node.setEnabled(false);
|
|
92424
|
+
this.setVolumesEnabled(false);
|
|
92425
|
+
for (const flipped of this.flippedNodes)
|
|
92426
|
+
flipped.node.rotationQuaternion.copyFrom(flipped.surfaceRotation);
|
|
92427
|
+
}
|
|
92428
|
+
writeUnderwaterFog(underwater) {
|
|
92429
|
+
const scene = this.scene;
|
|
92430
|
+
this.surfaceFogColor.copyFrom(scene.fogColor);
|
|
92431
|
+
this.surfaceFogDensity = scene.fogDensity;
|
|
92432
|
+
this.surfaceFogStart = scene.fogStart;
|
|
92433
|
+
this.surfaceFogEnd = scene.fogEnd;
|
|
92434
|
+
const isExponentialSquared = scene.fogMode === Scene.FOGMODE_EXP2;
|
|
92435
|
+
const densityScale = isExponentialSquared ? SceneManager.FogExp2DensityScale : SceneManager.FogExpDensityScale;
|
|
92436
|
+
scene.fogDensity = underwater.fogdensity * densityScale;
|
|
92437
|
+
scene.fogStart = underwater.fogstart;
|
|
92438
|
+
scene.fogEnd = underwater.fogend * MeshWaterSurface.LINEAR_FOG_END_SCALE;
|
|
92439
|
+
}
|
|
92440
|
+
restoreSurfaceFog() {
|
|
92441
|
+
const scene = this.scene;
|
|
92442
|
+
scene.fogColor.copyFrom(this.surfaceFogColor);
|
|
92443
|
+
scene.fogDensity = this.surfaceFogDensity;
|
|
92444
|
+
scene.fogStart = this.surfaceFogStart;
|
|
92445
|
+
scene.fogEnd = this.surfaceFogEnd;
|
|
92446
|
+
}
|
|
92447
|
+
setVolumesEnabled(enabled) {
|
|
92448
|
+
for (const node of this.volumeNodes) {
|
|
92449
|
+
node.setEnabled(enabled);
|
|
92450
|
+
const volume = SceneManager.GetComponent(node, MeshWaterSurface.POST_PROCESSOR_CLASS);
|
|
92451
|
+
if (volume != null)
|
|
92452
|
+
volume.SetVolumeEnabled(enabled);
|
|
92453
|
+
}
|
|
92454
|
+
}
|
|
92455
|
+
updateFog(depthMeters) {
|
|
92456
|
+
const underwater = this.waterProperties.underwater;
|
|
92457
|
+
if (underwater.fogcolors.length === 0)
|
|
92458
|
+
return;
|
|
92459
|
+
const depthFraction = Scalar.Clamp(depthMeters / underwater.fogdepth, 0, 1);
|
|
92460
|
+
const keyPosition = depthFraction * (underwater.fogcolors.length - 1);
|
|
92461
|
+
const lowerKey = Math.floor(keyPosition);
|
|
92462
|
+
const upperKey = Math.min(lowerKey + 1, underwater.fogcolors.length - 1);
|
|
92463
|
+
const keyBlend = keyPosition - lowerKey;
|
|
92464
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
92465
|
+
const gradientValue = Scalar.Lerp(underwater.fogcolors[lowerKey][channel], underwater.fogcolors[upperKey][channel], keyBlend);
|
|
92466
|
+
const litValue = gradientValue * underwater.suncolor[channel];
|
|
92467
|
+
const addedValue = underwater.fogcolormultiplier[channel];
|
|
92468
|
+
this.fogColorChannels[channel] = (addedValue >= 0) ? Scalar.Clamp(litValue + addedValue, 0, 1) : litValue;
|
|
92469
|
+
}
|
|
92470
|
+
this.scene.fogColor.set(this.fogColorChannels[0], this.fogColorChannels[1], this.fogColorChannels[2]);
|
|
92471
|
+
}
|
|
92472
|
+
updateFollowers(camera) {
|
|
92473
|
+
for (const follower of this.followers) {
|
|
92474
|
+
camera.globalPosition.addToRef(follower.offset, this.followPosition);
|
|
92475
|
+
follower.node.setAbsolutePosition(this.followPosition);
|
|
92476
|
+
}
|
|
92477
|
+
}
|
|
92478
|
+
}
|
|
92479
|
+
MeshWaterSurface.POST_PROCESSOR_CLASS = "TOOLKIT.PostProcessor";
|
|
92480
|
+
MeshWaterSurface.PARTICLES_CLASS = "TOOLKIT.ShurikenParticles";
|
|
92481
|
+
MeshWaterSurface.STOP_AND_CLEAR = 1;
|
|
92482
|
+
MeshWaterSurface.LINEAR_FOG_END_SCALE = 2;
|
|
92483
|
+
MeshWaterSurface.Registry = new Map();
|
|
91717
92484
|
export class NavigationAgent extends ScriptComponent {
|
|
91718
92485
|
isReady() { return this.m_agentReady; }
|
|
91719
92486
|
isNavigating() { return (this.m_navStatus === NavMoveStatus.Moving); }
|
|
@@ -108513,6 +109280,7 @@ export class PostProcessor extends ScriptComponent {
|
|
|
108513
109280
|
const props = this.getProperties();
|
|
108514
109281
|
this.cameraOwner = PostProcessingContract.isCameraOwner(props);
|
|
108515
109282
|
this.volumeProperties = this.cameraOwner ? null : PostProcessingContract.parseVolume(props);
|
|
109283
|
+
this.volumeEnabled = !this.isSwitchedVolume();
|
|
108516
109284
|
this.volumeLayer = SceneManager.GetTransformLayer(this.transform);
|
|
108517
109285
|
PostProcessor.Registry.push(this);
|
|
108518
109286
|
if (PostProcessor.GlobalInstance == null)
|
|
@@ -108749,7 +109517,8 @@ export class PostProcessor extends ScriptComponent {
|
|
|
108749
109517
|
PostProcessor.warnOnce("cameras:" + selection.mode, selection.warning);
|
|
108750
109518
|
const hasLocal = entries.some(e => e.properties.isglobal === false && e.properties.bounds != null);
|
|
108751
109519
|
const hasCinemachine = volumes.some((volume) => volume.isCinemachineVolume());
|
|
108752
|
-
const
|
|
109520
|
+
const hasSwitched = volumes.some((volume) => volume.isSwitchedVolume());
|
|
109521
|
+
const liveBlend = hasLocal || hasCinemachine || hasSwitched;
|
|
108753
109522
|
const localLuts = liveBlend ? entries.map((e) => {
|
|
108754
109523
|
const lut = PostProcessingContract.normalise(e.properties).lut;
|
|
108755
109524
|
return (lut != null && lut.identity !== true && typeof lut.url === "string" && lut.url !== "") ? lut : null;
|
|
@@ -108844,6 +109613,9 @@ export class PostProcessor extends ScriptComponent {
|
|
|
108844
109613
|
isCinemachineVolume() {
|
|
108845
109614
|
return this.volumeProperties != null && this.volumeProperties.role === PostProcessingContract.CINEMACHINE_VOLUME_ROLE;
|
|
108846
109615
|
}
|
|
109616
|
+
isSwitchedVolume() {
|
|
109617
|
+
return this.volumeProperties != null && this.volumeProperties.role === PostProcessingContract.SWITCHED_VOLUME_ROLE;
|
|
109618
|
+
}
|
|
108847
109619
|
IsVolumeEnabled() {
|
|
108848
109620
|
if (this.volumeEnabled === false)
|
|
108849
109621
|
return false;
|
|
@@ -109458,7 +110230,7 @@ export class PostProcessor extends ScriptComponent {
|
|
|
109458
110230
|
applyHdrpFog(camera, textureType) {
|
|
109459
110231
|
if (camera == null || this.scene == null || HdrpRendering == null || HdrpFogPass == null)
|
|
109460
110232
|
return;
|
|
109461
|
-
if (HdrpRendering.IsParity(this.scene) !== true)
|
|
110233
|
+
if (HdrpRendering.IsParity(this.scene) !== true && PhysicalSkyBlock.UrpBlock(HdrpRendering.SceneMetadata(this.scene)) == null)
|
|
109462
110234
|
return;
|
|
109463
110235
|
const fog = HdrpFogPass.FogBlock(this.scene);
|
|
109464
110236
|
if (!HdrpFogMath.PassNeeded(fog, HdrpFogPass.SkyOf(this.scene)))
|
|
@@ -114890,6 +115662,7 @@ export class PostProcessingContract {
|
|
|
114890
115662
|
}
|
|
114891
115663
|
PostProcessingContract.VOLUME_TYPES = ["BuiltIn", "URP", "HDRP"];
|
|
114892
115664
|
PostProcessingContract.CINEMACHINE_VOLUME_ROLE = "cinemachine";
|
|
115665
|
+
PostProcessingContract.SWITCHED_VOLUME_ROLE = "switched";
|
|
114893
115666
|
PostProcessingContract.GRADING_MODES = ["LowDefinitionRange", "HighDefinitionRange", "External"];
|
|
114894
115667
|
PostProcessingContract.RENDERING_PATH_DEFERRED = "DeferredShading";
|
|
114895
115668
|
PostProcessingContract.RENDERING_PATH_FORWARD = "Forward";
|
|
@@ -130653,6 +131426,20 @@ export class RigidbodyPhysics extends ScriptComponent {
|
|
|
130653
131426
|
RigidbodyPhysics.ConfigRigidbodyPhysics(scene, entity, false, false, physics, mass, centerOfMass);
|
|
130654
131427
|
return body;
|
|
130655
131428
|
}
|
|
131429
|
+
static MarkCompoundChildForDisposal(entity) {
|
|
131430
|
+
if (RigidbodyPhysics.RendersItself(entity) === false) {
|
|
131431
|
+
Utilities.AddLoaderItemMarkedForDisposal(entity);
|
|
131432
|
+
return;
|
|
131433
|
+
}
|
|
131434
|
+
const colliderMesh = SceneManager.FindChildTransformNode(entity, (entity.name + ".Collider"), SearchType.ExactMatch, true);
|
|
131435
|
+
if (colliderMesh != null)
|
|
131436
|
+
Utilities.AddLoaderItemMarkedForDisposal(colliderMesh);
|
|
131437
|
+
}
|
|
131438
|
+
static RendersItself(node) {
|
|
131439
|
+
if (!(node instanceof AbstractMesh) || node.getTotalVertices() <= 0 || node.name.endsWith(".Collider"))
|
|
131440
|
+
return false;
|
|
131441
|
+
return node.metadata != null && node.metadata.toolkit != null && node.metadata.toolkit.renderer != null;
|
|
131442
|
+
}
|
|
130656
131443
|
static CreateCompoundPhysicsShapeAndBody(scene, root, entity, element, impostortype, staticfriction, dynamicfriction, restitution, fcombine, rcombine, sitems, item, center, complex, trigger, persist, layer, filter) {
|
|
130657
131444
|
let center_xx = 0;
|
|
130658
131445
|
let center_yy = 0;
|
|
@@ -130663,7 +131450,7 @@ export class RigidbodyPhysics extends ScriptComponent {
|
|
|
130663
131450
|
const rootScale = RigidbodyPhysics.GetColliderScale(root);
|
|
130664
131451
|
const entityScale = (entity === root) ? rootScale : RigidbodyPhysics.RoundColliderScale(entity.scaling);
|
|
130665
131452
|
if (persist === false && complex === true)
|
|
130666
|
-
|
|
131453
|
+
RigidbodyPhysics.MarkCompoundChildForDisposal(entity);
|
|
130667
131454
|
if (impostortype == RigidbodyPhysics.ConvexHullImpostor) {
|
|
130668
131455
|
let convexHullMesh = SceneManager.FindChildTransformNode(entity, (entity.name + ".Collider"), SearchType.ExactMatch, true);
|
|
130669
131456
|
if (convexHullMesh != null) {
|
|
@@ -133501,7 +134288,7 @@ export class ShurikenParticles extends ScriptComponent {
|
|
|
133501
134288
|
}
|
|
133502
134289
|
canBeginWarmupDraw() {
|
|
133503
134290
|
const particleSystem = this._prtSystem;
|
|
133504
|
-
if (particleSystem == null || particleSystem.isDisposed || this._prtState == null || this._prtProps == null
|
|
134291
|
+
if (particleSystem == null || particleSystem.isDisposed || this._prtState == null || this._prtProps == null)
|
|
133505
134292
|
return false;
|
|
133506
134293
|
if (this._prtWarming || this._prtPrewarmJob != null)
|
|
133507
134294
|
return false;
|
|
@@ -141333,6 +142120,7 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
141333
142120
|
if (heights == null)
|
|
141334
142121
|
throw new Error("heights block has no data");
|
|
141335
142122
|
hf = new TerrainHeightfield(props, heights);
|
|
142123
|
+
hf.setEdgeNeighbors(this.resolveEdgeNeighbors());
|
|
141336
142124
|
this._heightfield = hf;
|
|
141337
142125
|
}
|
|
141338
142126
|
catch (e) {
|
|
@@ -141525,6 +142313,45 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
141525
142313
|
this._isBuilt = true;
|
|
141526
142314
|
this.onBuiltObservable.notifyObservers(this);
|
|
141527
142315
|
}
|
|
142316
|
+
resolveEdgeNeighbors() {
|
|
142317
|
+
const contract = this._contract;
|
|
142318
|
+
const tiles = TerrainBuilder.GetTerrains(this.scene);
|
|
142319
|
+
const ids = contract.neighbors;
|
|
142320
|
+
const hasIds = ids != null && (ids.left != null || ids.right != null || ids.top != null || ids.bottom != null);
|
|
142321
|
+
const neighbors = {
|
|
142322
|
+
left: this.edgeNeighborFor(tiles, hasIds ? ids.left : null, hasIds, -1, 0),
|
|
142323
|
+
right: this.edgeNeighborFor(tiles, hasIds ? ids.right : null, hasIds, 1, 0),
|
|
142324
|
+
top: this.edgeNeighborFor(tiles, hasIds ? ids.top : null, hasIds, 0, 1),
|
|
142325
|
+
bottom: this.edgeNeighborFor(tiles, hasIds ? ids.bottom : null, hasIds, 0, -1),
|
|
142326
|
+
};
|
|
142327
|
+
const anyNeighbor = neighbors.left != null || neighbors.right != null || neighbors.top != null || neighbors.bottom != null;
|
|
142328
|
+
return anyNeighbor ? neighbors : null;
|
|
142329
|
+
}
|
|
142330
|
+
edgeNeighborFor(tiles, neighborId, useIds, offsetX, offsetZ) {
|
|
142331
|
+
for (const other of tiles) {
|
|
142332
|
+
if (other === this || !this.canShareEdgeWith(other))
|
|
142333
|
+
continue;
|
|
142334
|
+
const isNeighbor = useIds ? other.transform.id === neighborId : this.isAdjacentTile(other, offsetX, offsetZ);
|
|
142335
|
+
if (!isNeighbor)
|
|
142336
|
+
continue;
|
|
142337
|
+
const heights = TerrainContract.GetBlock(other.contract, "heights", "u16");
|
|
142338
|
+
return (heights != null) ? { heights: heights, heightScale: other.contract.size[1] } : null;
|
|
142339
|
+
}
|
|
142340
|
+
return null;
|
|
142341
|
+
}
|
|
142342
|
+
canShareEdgeWith(other) {
|
|
142343
|
+
const contract = this._contract, otherContract = other.contract;
|
|
142344
|
+
return otherContract.heightresolution === contract.heightresolution && otherContract.size[0] === contract.size[0] && otherContract.size[2] === contract.size[2];
|
|
142345
|
+
}
|
|
142346
|
+
isAdjacentTile(other, offsetX, offsetZ) {
|
|
142347
|
+
const contract = this._contract, otherContract = other.contract;
|
|
142348
|
+
if (contract.allowautoconnect !== true || otherContract.allowautoconnect !== true || otherContract.groupingid !== contract.groupingid)
|
|
142349
|
+
return false;
|
|
142350
|
+
this.transform.computeWorldMatrix(true);
|
|
142351
|
+
const offset = this.worldToUnityUV(other.transform.getAbsolutePosition());
|
|
142352
|
+
const toleranceU = TERRAIN_EDGE_TOLERANCE_METERS / contract.size[0], toleranceV = TERRAIN_EDGE_TOLERANCE_METERS / contract.size[2];
|
|
142353
|
+
return Math.abs(offset[0] - offsetX) <= toleranceU && Math.abs(offset[1] - offsetZ) <= toleranceV;
|
|
142354
|
+
}
|
|
141528
142355
|
applyTreesAndFoliageVisibility() {
|
|
141529
142356
|
if (this._trees != null)
|
|
141530
142357
|
this._trees.setVisible(this._drawTreesAndFoliage);
|
|
@@ -142109,12 +142936,21 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
142109
142936
|
worldToUnityUV(worldPosition) {
|
|
142110
142937
|
if (this._contract == null || worldPosition == null)
|
|
142111
142938
|
return null;
|
|
142112
|
-
|
|
142113
|
-
const local =
|
|
142939
|
+
this.transform.getWorldMatrix().invertToRef(TerrainBuilder.ScratchInverse);
|
|
142940
|
+
const local = TerrainBuilder.ScratchLocal;
|
|
142941
|
+
Vector3.TransformCoordinatesToRef(worldPosition, TerrainBuilder.ScratchInverse, local);
|
|
142114
142942
|
const size = this._contract.size;
|
|
142115
142943
|
const ux = TerrainMath.UnityToLocalX(local.x, this._contract.handedness);
|
|
142116
142944
|
return [ux / size[0], local.z / size[2]];
|
|
142117
142945
|
}
|
|
142946
|
+
containsWorldPosition(worldPosition, tolerance) {
|
|
142947
|
+
const uv = this.worldToUnityUV(worldPosition);
|
|
142948
|
+
if (uv == null)
|
|
142949
|
+
return false;
|
|
142950
|
+
const size = this._contract.size;
|
|
142951
|
+
const slackU = tolerance / size[0], slackV = tolerance / size[2];
|
|
142952
|
+
return uv[0] >= -slackU && uv[0] <= 1 + slackU && uv[1] >= -slackV && uv[1] <= 1 + slackV;
|
|
142953
|
+
}
|
|
142118
142954
|
SampleHeight(worldPosition) {
|
|
142119
142955
|
if (this._heightfield == null)
|
|
142120
142956
|
return 0;
|
|
@@ -142183,6 +143019,10 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
142183
143019
|
if (list[i]._heightfield != null && list[i].WorldToTerrainUV(worldPosition) != null)
|
|
142184
143020
|
return list[i];
|
|
142185
143021
|
}
|
|
143022
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
143023
|
+
if (list[i]._heightfield != null && list[i].containsWorldPosition(worldPosition, TERRAIN_EDGE_TOLERANCE_METERS))
|
|
143024
|
+
return list[i];
|
|
143025
|
+
}
|
|
142186
143026
|
return null;
|
|
142187
143027
|
}
|
|
142188
143028
|
static GetWorldHeightAt(scene, worldPosition) {
|
|
@@ -142200,6 +143040,8 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
142200
143040
|
}
|
|
142201
143041
|
}
|
|
142202
143042
|
TerrainBuilder._registry = new Map();
|
|
143043
|
+
TerrainBuilder.ScratchInverse = new Matrix();
|
|
143044
|
+
TerrainBuilder.ScratchLocal = new Vector3();
|
|
142203
143045
|
TerrainBuilder.OnLoadProgressObservable = new Observable();
|
|
142204
143046
|
TerrainBuilder.PRELOAD_TIMEOUT_MS = 120000;
|
|
142205
143047
|
TerrainBuilder.SETTLE_MS = 30000;
|
|
@@ -142620,7 +143462,7 @@ export class TerrainDetails {
|
|
|
142620
143462
|
}
|
|
142621
143463
|
const F = TerrainFoliagePlugin;
|
|
142622
143464
|
const layer = rt.data;
|
|
142623
|
-
const sway = layer.sway;
|
|
143465
|
+
const sway = F.GraphOwnsMotion(source) ? null : layer.sway;
|
|
142624
143466
|
const urp = ((this._owner.contract != null && this._owner.contract.flavour) || "urp") === "urp";
|
|
142625
143467
|
const modes = F.MODE_FADE | ((sway != null) ? F.MODE_SWAY : 0) | ((layer.rendermode === "Grass") ? F.MODE_GRASSWAVE : 0) | (urp ? F.MODE_URPSPECULAR : 0);
|
|
142626
143468
|
const swayKey = (sway != null) ? (sway.amount + "," + sway.speed + "," + sway.wavelength) : "-";
|
|
@@ -142897,19 +143739,12 @@ export class TerrainDetails {
|
|
|
142897
143739
|
rt.mainMatrices = matrices;
|
|
142898
143740
|
rt.mainColors = colors;
|
|
142899
143741
|
rt.main.thinInstanceSetBuffer("matrix", matrices, 16, false);
|
|
142900
|
-
|
|
143742
|
+
TerrainTrees.ResizePreviousMatrices(rt.main, matrices);
|
|
142901
143743
|
if (colors != null)
|
|
142902
143744
|
rt.main.thinInstanceSetBuffer(TerrainDetails.TintBuffer(rt), colors, 4, false);
|
|
142903
143745
|
if (ShaderGraphRuntime != null)
|
|
142904
143746
|
ShaderGraphRuntime.EnsureThinInstanceBuffers(rt.main);
|
|
142905
143747
|
}
|
|
142906
|
-
static ResizePreviousMatrices(mesh, matrices) {
|
|
142907
|
-
const storage = mesh._thinInstanceDataStorage;
|
|
142908
|
-
if (storage == null || storage.previousMatrixBuffer == null || storage.previousMatrixData != null)
|
|
142909
|
-
return;
|
|
142910
|
-
storage.previousMatrixBuffer.dispose();
|
|
142911
|
-
storage.previousMatrixBuffer = mesh._thinInstanceCreateMatrixBuffer("previousWorld", matrices, false);
|
|
142912
|
-
}
|
|
142913
143748
|
static TintBuffer(rt) { return (rt.grass != null) ? "tkGrassTint" : "color"; }
|
|
142914
143749
|
chunkDistance(c, x, z, size) {
|
|
142915
143750
|
const x0 = c.cx * this._cw, x1 = Math.min((c.cx + 1) * this._cw, size[0]);
|
|
@@ -143503,6 +144338,9 @@ export class TerrainFoliagePlugin extends MaterialPluginBase {
|
|
|
143503
144338
|
const shader = m.getShaderName();
|
|
143504
144339
|
return shader != null && shader !== "UniversalShaderMaterial" && shader !== "CustomShaderMaterial";
|
|
143505
144340
|
}
|
|
144341
|
+
static GraphOwnsMotion(material) {
|
|
144342
|
+
return TerrainFoliagePlugin.IsGraphMaterial(material) && material.__speedTreeWind == null;
|
|
144343
|
+
}
|
|
143506
144344
|
getClassName() {
|
|
143507
144345
|
return "TerrainFoliagePlugin";
|
|
143508
144346
|
}
|
|
@@ -144675,6 +145513,7 @@ export class TerrainHeightfield {
|
|
|
144675
145513
|
this._errors = null;
|
|
144676
145514
|
this._minH = null;
|
|
144677
145515
|
this._maxH = null;
|
|
145516
|
+
this.edgeNeighbors = null;
|
|
144678
145517
|
this.R = contract.heightresolution;
|
|
144679
145518
|
this.size = [contract.size[0], contract.size[1], contract.size[2]];
|
|
144680
145519
|
this.handedness = (contract.handedness != null) ? contract.handedness : 1;
|
|
@@ -144703,9 +145542,12 @@ export class TerrainHeightfield {
|
|
|
144703
145542
|
return this.heights[z * this.R + x] / 65535 * this.size[1];
|
|
144704
145543
|
}
|
|
144705
145544
|
normalAt(u, v, out) {
|
|
144706
|
-
TerrainMath.SampleNormal(this.heights, this.R, this.size, u, v, out);
|
|
145545
|
+
TerrainMath.SampleNormal(this.heights, this.R, this.size, u, v, out, this.edgeNeighbors);
|
|
144707
145546
|
out[0] *= (this.handedness === 0 ? -1 : 1);
|
|
144708
145547
|
}
|
|
145548
|
+
setEdgeNeighbors(neighbors) {
|
|
145549
|
+
this.edgeNeighbors = neighbors;
|
|
145550
|
+
}
|
|
144709
145551
|
setHoles(pixels, width) {
|
|
144710
145552
|
const n = this.R - 1;
|
|
144711
145553
|
const height = (width > 0) ? Math.floor(pixels.length / 4 / width) : 0;
|
|
@@ -144984,15 +145826,31 @@ export class TerrainMath {
|
|
|
144984
145826
|
const b = h01 + (h11 - h01) * tx;
|
|
144985
145827
|
return TerrainMath.DecodeHeight(a + (b - a) * tz, sizeY);
|
|
144986
145828
|
}
|
|
144987
|
-
static SampleNormal(h, R, size, u, v, out) {
|
|
145829
|
+
static SampleNormal(h, R, size, u, v, out, neighbors = null) {
|
|
144988
145830
|
const i = Math.round(TerrainMath.Clamp01(u) * (R - 1));
|
|
144989
145831
|
const k = Math.round(TerrainMath.Clamp01(v) * (R - 1));
|
|
144990
145832
|
const iL = Math.max(i - 1, 0), iR = Math.min(i + 1, R - 1);
|
|
144991
145833
|
const kD = Math.max(k - 1, 0), kU = Math.min(k + 1, R - 1);
|
|
144992
145834
|
const cellX = size[0] / (R - 1), cellZ = size[2] / (R - 1);
|
|
144993
145835
|
const sy = size[1];
|
|
144994
|
-
|
|
144995
|
-
|
|
145836
|
+
let dhdx = (iR > iL) ? (TerrainMath.DecodeHeight(h[k * R + iR], sy) - TerrainMath.DecodeHeight(h[k * R + iL], sy)) / ((iR - iL) * cellX) : 0;
|
|
145837
|
+
let dhdz = (kU > kD) ? (TerrainMath.DecodeHeight(h[kU * R + i], sy) - TerrainMath.DecodeHeight(h[kD * R + i], sy)) / ((kU - kD) * cellZ) : 0;
|
|
145838
|
+
if (i === 0 && neighbors?.left != null) {
|
|
145839
|
+
const outsideHeight = TerrainMath.DecodeHeight(neighbors.left.heights[k * R + (R - 2)], neighbors.left.heightScale);
|
|
145840
|
+
dhdx = (TerrainMath.DecodeHeight(h[k * R + 1], sy) - outsideHeight) / (2 * cellX);
|
|
145841
|
+
}
|
|
145842
|
+
else if (i === R - 1 && neighbors?.right != null) {
|
|
145843
|
+
const outsideHeight = TerrainMath.DecodeHeight(neighbors.right.heights[k * R + 1], neighbors.right.heightScale);
|
|
145844
|
+
dhdx = (outsideHeight - TerrainMath.DecodeHeight(h[k * R + (R - 2)], sy)) / (2 * cellX);
|
|
145845
|
+
}
|
|
145846
|
+
if (k === 0 && neighbors?.bottom != null) {
|
|
145847
|
+
const outsideHeight = TerrainMath.DecodeHeight(neighbors.bottom.heights[(R - 2) * R + i], neighbors.bottom.heightScale);
|
|
145848
|
+
dhdz = (TerrainMath.DecodeHeight(h[1 * R + i], sy) - outsideHeight) / (2 * cellZ);
|
|
145849
|
+
}
|
|
145850
|
+
else if (k === R - 1 && neighbors?.top != null) {
|
|
145851
|
+
const outsideHeight = TerrainMath.DecodeHeight(neighbors.top.heights[1 * R + i], neighbors.top.heightScale);
|
|
145852
|
+
dhdz = (outsideHeight - TerrainMath.DecodeHeight(h[(R - 2) * R + i], sy)) / (2 * cellZ);
|
|
145853
|
+
}
|
|
144996
145854
|
const nx = -dhdx, ny = 1, nz = -dhdz;
|
|
144997
145855
|
const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
144998
145856
|
out[0] = nx / len;
|
|
@@ -146251,6 +147109,7 @@ export class TerrainTrees {
|
|
|
146251
147109
|
this._colliderShapes = [];
|
|
146252
147110
|
this._distance = 2000;
|
|
146253
147111
|
this._built = false;
|
|
147112
|
+
this._warnedUniqueHost = false;
|
|
146254
147113
|
this._n = 0;
|
|
146255
147114
|
this._proto = null;
|
|
146256
147115
|
this._local = null;
|
|
@@ -146343,13 +147202,21 @@ export class TerrainTrees {
|
|
|
146343
147202
|
return casters;
|
|
146344
147203
|
}
|
|
146345
147204
|
getStats() {
|
|
146346
|
-
let drawn = 0, visibleHosts = 0;
|
|
147205
|
+
let drawn = 0, visibleHosts = 0, capacity = 0;
|
|
146347
147206
|
for (let i = 0; i < this._hosts.length; i++) {
|
|
146348
|
-
|
|
146349
|
-
|
|
147207
|
+
const host = this._hosts[i];
|
|
147208
|
+
drawn += host.count;
|
|
147209
|
+
if (host.count > 0)
|
|
146350
147210
|
visibleHosts++;
|
|
147211
|
+
capacity += host.matrices.length / 16;
|
|
147212
|
+
for (let slotIndex = 0; slotIndex < host.slots.length; slotIndex++)
|
|
147213
|
+
capacity += host.slots[slotIndex].matrices.length / 16;
|
|
146351
147214
|
}
|
|
146352
|
-
return { hosts: this._hosts.length, instances: this._n, drawn: drawn, visibleHosts: visibleHosts, colliders: this._colliderBody != null };
|
|
147215
|
+
return { hosts: this._hosts.length, instances: this._n, drawn: drawn, visibleHosts: visibleHosts, colliders: this._colliderBody != null, capacity: capacity };
|
|
147216
|
+
}
|
|
147217
|
+
static PreparedHostCount(scene) {
|
|
147218
|
+
const prepared = TerrainTrees.PreparedHosts.get(scene.uniqueId);
|
|
147219
|
+
return (prepared != null) ? prepared.size : 0;
|
|
146353
147220
|
}
|
|
146354
147221
|
build() {
|
|
146355
147222
|
const owner = this._owner;
|
|
@@ -146407,43 +147274,44 @@ export class TerrainTrees {
|
|
|
146407
147274
|
this._built = true;
|
|
146408
147275
|
this._dirty = true;
|
|
146409
147276
|
}
|
|
146410
|
-
buildPrototype(
|
|
146411
|
-
const
|
|
146412
|
-
const runtime = { data:
|
|
146413
|
-
const
|
|
146414
|
-
|
|
146415
|
-
|
|
146416
|
-
|
|
146417
|
-
|
|
146418
|
-
|
|
146419
|
-
|
|
146420
|
-
|
|
147277
|
+
buildPrototype(prototype, template, instanceCount) {
|
|
147278
|
+
const prototypeLods = Array.isArray(prototype.lods) ? prototype.lods : [];
|
|
147279
|
+
const runtime = { data: prototype, heights: prototypeLods.map((prototypeLod) => (prototypeLod != null && prototypeLod.height != null) ? prototypeLod.height : 0), lodHosts: [], fade: (prototype.fademode > 0 && prototype.animatecrossfade === true) };
|
|
147280
|
+
const templateWorldInverse = Matrix.Invert(template.computeWorldMatrix(true));
|
|
147281
|
+
const initialCapacity = Math.min(instanceCount, TREE_INITIAL_CAPACITY);
|
|
147282
|
+
let hasHost = false;
|
|
147283
|
+
for (let lodIndex = 0; lodIndex < prototypeLods.length; lodIndex++) {
|
|
147284
|
+
const lodHostIndices = [];
|
|
147285
|
+
runtime.lodHosts.push(lodHostIndices);
|
|
147286
|
+
const renderers = (prototypeLods[lodIndex] != null && Array.isArray(prototypeLods[lodIndex].renderers)) ? prototypeLods[lodIndex].renderers : [];
|
|
147287
|
+
for (let rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
|
|
147288
|
+
const node = this._scene.getNodeById(renderers[rendererIndex]);
|
|
146421
147289
|
if (node == null)
|
|
146422
147290
|
continue;
|
|
146423
147291
|
let sources = [];
|
|
146424
147292
|
if (node instanceof AbstractMesh && TerrainTrees.HasGeometry(node))
|
|
146425
147293
|
sources = [node];
|
|
146426
147294
|
else
|
|
146427
|
-
sources = node.getChildMeshes(true).filter((
|
|
146428
|
-
const flags = (
|
|
146429
|
-
const probeLit = TerrainTrees.RendererUsesProbes(flags,
|
|
146430
|
-
for (let
|
|
146431
|
-
const matrices = new Float32Array(16 *
|
|
146432
|
-
const cast = !(
|
|
146433
|
-
const host = this.createHost(
|
|
147295
|
+
sources = node.getChildMeshes(true).filter((mesh) => TerrainTrees.HasGeometry(mesh));
|
|
147296
|
+
const flags = (prototypeLods[lodIndex] != null) ? prototypeLods[lodIndex].lightprobes : null;
|
|
147297
|
+
const probeLit = TerrainTrees.RendererUsesProbes(flags, rendererIndex) && TerrainProbePlugin != null;
|
|
147298
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
|
|
147299
|
+
const matrices = new Float32Array(16 * initialCapacity), fades = new Float32Array(initialCapacity);
|
|
147300
|
+
const cast = !(prototypeLods[lodIndex] != null && prototypeLods[lodIndex].billboard === true) && TerrainTrees.RendererCastsShadows(sources[sourceIndex], node);
|
|
147301
|
+
const host = this.createHost(prototype, lodIndex, sources[sourceIndex], templateWorldInverse, matrices, fades);
|
|
146434
147302
|
if (host == null)
|
|
146435
147303
|
continue;
|
|
146436
147304
|
if (probeLit)
|
|
146437
147305
|
this._probeHosts++;
|
|
146438
|
-
|
|
146439
|
-
this._hosts.push({ mesh: host, matrices: matrices, fades: fades, count: 0, lastCount: -1, cast: cast, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3(), probes: null, probeBuffer: null, probeLit: probeLit, slots: [] });
|
|
147306
|
+
lodHostIndices.push(this._hosts.length);
|
|
147307
|
+
this._hosts.push({ mesh: host, matrices: matrices, fades: fades, count: 0, lastCount: -1, cast: cast, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3(), probes: null, probeBuffer: null, probeLit: probeLit, slots: [], capacityCeiling: instanceCount, grown: false });
|
|
146440
147308
|
if (cast)
|
|
146441
|
-
this.createCasterSlots(this._hosts[this._hosts.length - 1],
|
|
146442
|
-
|
|
147309
|
+
this.createCasterSlots(this._hosts[this._hosts.length - 1], initialCapacity);
|
|
147310
|
+
hasHost = true;
|
|
146443
147311
|
}
|
|
146444
147312
|
}
|
|
146445
147313
|
}
|
|
146446
|
-
return
|
|
147314
|
+
return hasHost ? runtime : null;
|
|
146447
147315
|
}
|
|
146448
147316
|
static RendererUsesProbes(flags, index) {
|
|
146449
147317
|
if (!Array.isArray(flags) || index < 0 || index >= flags.length)
|
|
@@ -146451,20 +147319,32 @@ export class TerrainTrees {
|
|
|
146451
147319
|
return flags[index] !== false;
|
|
146452
147320
|
}
|
|
146453
147321
|
static AttachProbeBuffer(host, probes) {
|
|
146454
|
-
const
|
|
146455
|
-
for (let
|
|
146456
|
-
probes[
|
|
146457
|
-
const buffer =
|
|
146458
|
-
|
|
146459
|
-
|
|
146460
|
-
host.onDisposeObservable.addOnce(() => { buffer.dispose(); });
|
|
147322
|
+
const probePlugin = TerrainProbePlugin;
|
|
147323
|
+
for (let recordOffset = 0; recordOffset < probes.length; recordOffset += probePlugin.STRIDE)
|
|
147324
|
+
probes[recordOffset] = -1;
|
|
147325
|
+
const buffer = TerrainTrees.CreateProbeBuffer(host, probes);
|
|
147326
|
+
TerrainTrees.ProbeBuffers.set(host, buffer);
|
|
147327
|
+
host.onDisposeObservable.addOnce(() => { TerrainTrees.ProbeBuffers.get(host).dispose(); });
|
|
146461
147328
|
const material = host.material;
|
|
146462
|
-
const
|
|
146463
|
-
for (let i = 0; i <
|
|
146464
|
-
if (
|
|
146465
|
-
|
|
147329
|
+
const subMaterials = (material != null && material.subMaterials != null) ? material.subMaterials : [material];
|
|
147330
|
+
for (let i = 0; i < subMaterials.length; i++)
|
|
147331
|
+
if (subMaterials[i] != null)
|
|
147332
|
+
probePlugin.Attach(subMaterials[i]);
|
|
146466
147333
|
return buffer;
|
|
146467
147334
|
}
|
|
147335
|
+
static CreateProbeBuffer(host, probes) {
|
|
147336
|
+
const probePlugin = TerrainProbePlugin;
|
|
147337
|
+
const buffer = new Buffer(host.getEngine(), probes, true, probePlugin.STRIDE, false, true);
|
|
147338
|
+
for (let viewIndex = 0; viewIndex < 3; viewIndex++)
|
|
147339
|
+
host.setVerticesBuffer(buffer.createVertexBuffer(probePlugin.ATTRIBUTES[viewIndex], 4 * viewIndex, 4, probePlugin.STRIDE, true));
|
|
147340
|
+
return buffer;
|
|
147341
|
+
}
|
|
147342
|
+
static ResizeProbeBuffer(host) {
|
|
147343
|
+
const buffer = TerrainTrees.CreateProbeBuffer(host.mesh, host.probes);
|
|
147344
|
+
host.probeBuffer.dispose();
|
|
147345
|
+
host.probeBuffer = buffer;
|
|
147346
|
+
TerrainTrees.ProbeBuffers.set(host.mesh, buffer);
|
|
147347
|
+
}
|
|
146468
147348
|
sampleProbes(network) {
|
|
146469
147349
|
if (network == null || typeof network.sampleInto !== "function" || !TerrainTrees.NetworkLightsProbes(network))
|
|
146470
147350
|
return false;
|
|
@@ -146620,31 +147500,85 @@ export class TerrainTrees {
|
|
|
146620
147500
|
throw error;
|
|
146621
147501
|
}
|
|
146622
147502
|
}
|
|
146623
|
-
|
|
146624
|
-
const
|
|
146625
|
-
const
|
|
146626
|
-
if (
|
|
147503
|
+
static BakeHostGeometry(source, templateWorldInverse, name) {
|
|
147504
|
+
const geometrySource = (source instanceof InstancedMesh) ? source.sourceMesh : source;
|
|
147505
|
+
const mesh = geometrySource.clone(name, null, true);
|
|
147506
|
+
if (mesh == null)
|
|
147507
|
+
return null;
|
|
147508
|
+
mesh.metadata = null;
|
|
147509
|
+
mesh.makeGeometryUnique();
|
|
147510
|
+
const rendererToTemplate = source.computeWorldMatrix(true).multiply(templateWorldInverse);
|
|
147511
|
+
mesh.position.setAll(0);
|
|
147512
|
+
mesh.rotationQuaternion = null;
|
|
147513
|
+
mesh.rotation.setAll(0);
|
|
147514
|
+
mesh.scaling.setAll(1);
|
|
147515
|
+
mesh.bakeTransformIntoVertices(rendererToTemplate);
|
|
147516
|
+
TerrainTrees.InterleaveVertexData(mesh);
|
|
147517
|
+
return mesh;
|
|
147518
|
+
}
|
|
147519
|
+
prepareHostGeometry(prototype, lod, source, templateWorldInverse) {
|
|
147520
|
+
const scene = this._scene;
|
|
147521
|
+
const sceneKey = scene.uniqueId;
|
|
147522
|
+
let prepared = TerrainTrees.PreparedHosts.get(sceneKey);
|
|
147523
|
+
if (prepared == null) {
|
|
147524
|
+
const hosts = new Map();
|
|
147525
|
+
prepared = hosts;
|
|
147526
|
+
TerrainTrees.PreparedHosts.set(sceneKey, hosts);
|
|
147527
|
+
scene.onDisposeObservable.addOnce(() => {
|
|
147528
|
+
TerrainTrees.PreparedHosts.delete(sceneKey);
|
|
147529
|
+
hosts.forEach((mesh) => { mesh.dispose(false, false); });
|
|
147530
|
+
});
|
|
147531
|
+
}
|
|
147532
|
+
const key = prototype.templateid + "|" + lod + "|" + source.uniqueId;
|
|
147533
|
+
const existing = prepared.get(key);
|
|
147534
|
+
if (existing != null)
|
|
147535
|
+
return existing;
|
|
147536
|
+
const mesh = TerrainTrees.BakeHostGeometry(source, templateWorldInverse, prototype.name + ".L" + lod + "." + source.name + ".Prepared");
|
|
147537
|
+
if (mesh == null)
|
|
146627
147538
|
return null;
|
|
146628
|
-
|
|
146629
|
-
|
|
146630
|
-
|
|
146631
|
-
|
|
146632
|
-
|
|
146633
|
-
|
|
146634
|
-
|
|
146635
|
-
|
|
147539
|
+
mesh.setEnabled(false);
|
|
147540
|
+
mesh.isVisible = false;
|
|
147541
|
+
mesh.isPickable = false;
|
|
147542
|
+
prepared.set(key, mesh);
|
|
147543
|
+
return mesh;
|
|
147544
|
+
}
|
|
147545
|
+
createUniqueHost(source, templateWorldInverse, name, reason) {
|
|
147546
|
+
if (!this._warnedUniqueHost) {
|
|
147547
|
+
this._warnedUniqueHost = true;
|
|
147548
|
+
Tools.Warn("[TerrainBuilder] " + this._owner.transform.name + ": tree hosts cannot share gpu buffers, copying geometry per tile — " + reason);
|
|
147549
|
+
}
|
|
147550
|
+
const host = TerrainTrees.BakeHostGeometry(source, templateWorldInverse, name);
|
|
147551
|
+
if (host != null)
|
|
147552
|
+
host.setEnabled(true);
|
|
147553
|
+
return host;
|
|
147554
|
+
}
|
|
147555
|
+
createHost(prototype, lod, source, templateWorldInverse, matrices, fades) {
|
|
147556
|
+
const prepared = this.prepareHostGeometry(prototype, lod, source, templateWorldInverse);
|
|
147557
|
+
if (prepared == null)
|
|
147558
|
+
return null;
|
|
147559
|
+
const name = prototype.name + ".L" + lod + "." + source.name;
|
|
147560
|
+
let host = null;
|
|
147561
|
+
try {
|
|
147562
|
+
host = TerrainTrees.CreateSharedHost(this._scene, prepared, name);
|
|
147563
|
+
for (const key of TerrainTrees.HOST_DRAW_STATE)
|
|
147564
|
+
host[key] = prepared[key];
|
|
147565
|
+
}
|
|
147566
|
+
catch (error) {
|
|
147567
|
+
host = this.createUniqueHost(source, templateWorldInverse, name, String(error));
|
|
147568
|
+
if (host == null)
|
|
147569
|
+
return null;
|
|
147570
|
+
}
|
|
147571
|
+
const sourceMaterial = prepared.material;
|
|
146636
147572
|
host.parent = this._root;
|
|
146637
|
-
host.setEnabled(true);
|
|
146638
147573
|
host.isVisible = false;
|
|
146639
147574
|
host.isPickable = false;
|
|
146640
147575
|
host.receiveShadows = true;
|
|
146641
147576
|
host.alwaysSelectAsActiveMesh = true;
|
|
146642
147577
|
host.doNotSyncBoundingInfo = true;
|
|
146643
|
-
TerrainTrees.InterleaveVertexData(host);
|
|
146644
147578
|
host.thinInstanceSetBuffer("matrix", matrices, 16, false);
|
|
146645
147579
|
host.thinInstanceSetBuffer("tkLodFade", fades, 1, false);
|
|
146646
147580
|
host.thinInstanceCount = 0;
|
|
146647
|
-
host.material = this.hostMaterial(
|
|
147581
|
+
host.material = this.hostMaterial(prototype, lod, sourceMaterial, TerrainTrees.HasSpeedTreeWindVertexData(prepared));
|
|
146648
147582
|
if (ShaderGraphRuntime != null)
|
|
146649
147583
|
ShaderGraphRuntime.EnsureThinInstanceBuffers(host);
|
|
146650
147584
|
return host;
|
|
@@ -146672,7 +147606,7 @@ export class TerrainTrees {
|
|
|
146672
147606
|
ShaderGraphRuntime.EnsureThinInstanceBuffers(mesh);
|
|
146673
147607
|
if (TerrainBuilder != null)
|
|
146674
147608
|
TerrainBuilder.SetCasterSlot(mesh, slotIndex);
|
|
146675
|
-
slots.push({ mesh: mesh, matrices: matrices, fades: fades, probes: null, count: 0, lastCount: -1, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3() });
|
|
147609
|
+
slots.push({ mesh: mesh, matrices: matrices, fades: fades, probes: null, count: 0, lastCount: -1, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3(), capacityCeiling: host.capacityCeiling, grown: false });
|
|
146676
147610
|
}
|
|
146677
147611
|
host.slots = slots;
|
|
146678
147612
|
}
|
|
@@ -146713,7 +147647,8 @@ export class TerrainTrees {
|
|
|
146713
147647
|
const hue = (info != null && Array.isArray(info.hue) && info.hue.length >= 4) ? info.hue : null;
|
|
146714
147648
|
const F = TerrainFoliagePlugin;
|
|
146715
147649
|
const urp = (this._owner.contract != null && (this._owner.contract.flavour || "urp") === "urp");
|
|
146716
|
-
const
|
|
147650
|
+
const graphMoves = p.speedtree !== true && F.GraphOwnsMotion(source);
|
|
147651
|
+
const bend = (ownWind || graphMoves) ? 0 : F.MODE_TREE;
|
|
146717
147652
|
const plugin = F.Attach(entry.material, bend | F.MODE_CROSSFADE | (billboard ? F.MODE_BILLBOARD : 0) | (hue != null ? F.MODE_HUE : 0) | (urp ? F.MODE_URPSPECULAR : 0));
|
|
146718
147653
|
if (plugin != null) {
|
|
146719
147654
|
plugin.params.bendFactor = (p.bendfactor != null) ? p.bendfactor : 0;
|
|
@@ -147418,7 +148353,7 @@ export class TerrainTrees {
|
|
|
147418
148353
|
}
|
|
147419
148354
|
appendInstance(target, instanceIndex, fade) {
|
|
147420
148355
|
const count = target.count;
|
|
147421
|
-
if (16 * (count + 1) > target.matrices.length)
|
|
148356
|
+
if (16 * (count + 1) > target.matrices.length && !TerrainTrees.GrowTarget(target, target.capacityCeiling))
|
|
147422
148357
|
return;
|
|
147423
148358
|
const sourceOffset = 16 * instanceIndex;
|
|
147424
148359
|
const targetOffset = 16 * count;
|
|
@@ -147456,6 +148391,26 @@ export class TerrainTrees {
|
|
|
147456
148391
|
if (z + radius > box[5])
|
|
147457
148392
|
box[5] = z + radius;
|
|
147458
148393
|
}
|
|
148394
|
+
static GrowTarget(target, ceiling) {
|
|
148395
|
+
const capacity = target.matrices.length / 16;
|
|
148396
|
+
if (capacity >= ceiling)
|
|
148397
|
+
return false;
|
|
148398
|
+
const grownCapacity = Math.min(ceiling, Math.max(target.count + 1, Math.ceil(capacity * TREE_GROWTH_FACTOR)));
|
|
148399
|
+
const matrices = new Float32Array(16 * grownCapacity);
|
|
148400
|
+
matrices.set(target.matrices.subarray(0, 16 * target.count));
|
|
148401
|
+
target.matrices = matrices;
|
|
148402
|
+
const fades = new Float32Array(grownCapacity);
|
|
148403
|
+
fades.set(target.fades.subarray(0, target.count));
|
|
148404
|
+
target.fades = fades;
|
|
148405
|
+
if (target.probes != null) {
|
|
148406
|
+
const stride = TerrainProbePlugin.STRIDE;
|
|
148407
|
+
const probes = new Float32Array(stride * grownCapacity);
|
|
148408
|
+
probes.set(target.probes.subarray(0, stride * target.count));
|
|
148409
|
+
target.probes = probes;
|
|
148410
|
+
}
|
|
148411
|
+
target.grown = true;
|
|
148412
|
+
return true;
|
|
148413
|
+
}
|
|
147459
148414
|
static LodCasts(hosts, proto, lod) {
|
|
147460
148415
|
if (lod < 0 || lod >= proto.lodHosts.length)
|
|
147461
148416
|
return false;
|
|
@@ -147493,16 +148448,39 @@ export class TerrainTrees {
|
|
|
147493
148448
|
}
|
|
147494
148449
|
}
|
|
147495
148450
|
}
|
|
148451
|
+
static RebindGrownTarget(target) {
|
|
148452
|
+
const mesh = target.mesh;
|
|
148453
|
+
mesh.thinInstanceSetBuffer("matrix", target.matrices, 16, false);
|
|
148454
|
+
TerrainTrees.ResizePreviousMatrices(mesh, target.matrices);
|
|
148455
|
+
mesh.thinInstanceSetBuffer("tkLodFade", target.fades, 1, false);
|
|
148456
|
+
if (target.probes != null)
|
|
148457
|
+
TerrainTrees.ResizeProbeBuffer(target);
|
|
148458
|
+
if (ShaderGraphRuntime != null)
|
|
148459
|
+
ShaderGraphRuntime.EnsureThinInstanceBuffers(mesh);
|
|
148460
|
+
target.grown = false;
|
|
148461
|
+
}
|
|
148462
|
+
static ResizePreviousMatrices(mesh, matrices) {
|
|
148463
|
+
const storage = mesh._thinInstanceDataStorage;
|
|
148464
|
+
if (storage == null || storage.previousMatrixBuffer == null || storage.previousMatrixData != null)
|
|
148465
|
+
return;
|
|
148466
|
+
storage.previousMatrixBuffer.dispose();
|
|
148467
|
+
storage.previousMatrixBuffer = mesh._thinInstanceCreateMatrixBuffer("previousWorld", matrices, false);
|
|
148468
|
+
}
|
|
147496
148469
|
uploadTarget(target, probeBuffer, isSlot) {
|
|
147497
148470
|
const mesh = target.mesh;
|
|
147498
148471
|
if (target.count > 0) {
|
|
147499
148472
|
if (isSlot && target.lastCount <= 0)
|
|
147500
148473
|
mesh.setEnabled(true);
|
|
148474
|
+
const grown = target.grown;
|
|
148475
|
+
if (grown)
|
|
148476
|
+
TerrainTrees.RebindGrownTarget(target);
|
|
147501
148477
|
mesh.thinInstanceCount = target.count;
|
|
147502
|
-
|
|
147503
|
-
|
|
147504
|
-
|
|
147505
|
-
probeBuffer
|
|
148478
|
+
if (!grown) {
|
|
148479
|
+
mesh.thinInstancePartialBufferUpdate("matrix", target.count, 0);
|
|
148480
|
+
mesh.thinInstancePartialBufferUpdate("tkLodFade", target.count, 0);
|
|
148481
|
+
if (probeBuffer != null)
|
|
148482
|
+
probeBuffer.updateDirectly(target.probes, 0, target.count);
|
|
148483
|
+
}
|
|
147506
148484
|
target.boxMin.set(target.box[0], target.box[1], target.box[2]);
|
|
147507
148485
|
target.boxMax.set(target.box[3], target.box[4], target.box[5]);
|
|
147508
148486
|
mesh.getBoundingInfo().reConstruct(target.boxMin, target.boxMax, mesh.computeWorldMatrix(isSlot));
|
|
@@ -147626,6 +148604,9 @@ TerrainTrees.CASTER_SLOTS = 4;
|
|
|
147626
148604
|
TerrainTrees.SLOT_MARGIN = 4;
|
|
147627
148605
|
TerrainTrees.NO_PROBE_RECORD = -1;
|
|
147628
148606
|
TerrainTrees.LIGHT_TURN_DOT = 0.99996192;
|
|
148607
|
+
TerrainTrees.HOST_DRAW_STATE = ["sideOrientation", "renderingGroupId", "layerMask", "alphaIndex", "hasVertexAlpha", "useVertexColors", "numBoneInfluencers", "skeleton"];
|
|
148608
|
+
TerrainTrees.PreparedHosts = new Map();
|
|
148609
|
+
TerrainTrees.ProbeBuffers = new WeakMap();
|
|
147629
148610
|
TerrainTrees._materials = new Map();
|
|
147630
148611
|
TerrainTrees.ProbeSampleHeight = 0;
|
|
147631
148612
|
TerrainTrees.SUBSURFACE_FIELDS = ["isTranslucencyEnabled", "isScatteringEnabled", "translucencyIntensity", "legacyTranslucency", "useGltfStyleTextures",
|
|
@@ -166554,6 +167535,1531 @@ export class VisualEffectSpawner {
|
|
|
166554
167535
|
}
|
|
166555
167536
|
VisualEffectSpawner.COUNT_EPSILON = 1e-6;
|
|
166556
167537
|
VisualEffectSpawner.MAX_PHASE_CHANGES_PER_STEP = 64;
|
|
167538
|
+
export class VolumetricCloudsMath {
|
|
167539
|
+
static ShellInterval(rayOrigin, rayDirection, innerRadius, outerRadius, planetRadius) {
|
|
167540
|
+
const radialDistance = Math.hypot(rayOrigin[0], rayOrigin[1], rayOrigin[2]);
|
|
167541
|
+
if (radialDistance <= 0)
|
|
167542
|
+
return null;
|
|
167543
|
+
const cosChi = (rayOrigin[0] * rayDirection[0] + rayOrigin[1] * rayDirection[1] + rayOrigin[2] * rayDirection[2]) / radialDistance;
|
|
167544
|
+
const inner = VolumetricCloudsMath.IntersectSphere(innerRadius, cosChi, radialDistance);
|
|
167545
|
+
const outer = VolumetricCloudsMath.IntersectSphere(outerRadius, cosChi, radialDistance);
|
|
167546
|
+
if (inner[0] < 0 && inner[1] >= 0) {
|
|
167547
|
+
const radius = (typeof planetRadius === "number") ? planetRadius : innerRadius;
|
|
167548
|
+
const sinHorizon = Math.min(1, radius / radialDistance);
|
|
167549
|
+
const horizonCosine = -Math.sqrt(Math.max(0, 1 - sinHorizon * sinHorizon));
|
|
167550
|
+
return (cosChi >= horizonCosine) ? [inner[1], outer[1]] : null;
|
|
167551
|
+
}
|
|
167552
|
+
if (outer[1] < 0)
|
|
167553
|
+
return null;
|
|
167554
|
+
return [Math.max(outer[0], 0), (inner[0] >= 0) ? inner[0] : outer[1]];
|
|
167555
|
+
}
|
|
167556
|
+
static StepSize(shellLength, steps, maximumStep) {
|
|
167557
|
+
return Math.min(shellLength / Math.max(1, steps), maximumStep);
|
|
167558
|
+
}
|
|
167559
|
+
static MaximumStep(altitudeRange, authoredSteps, tierSteps) {
|
|
167560
|
+
const stretch = (typeof authoredSteps === "number" && tierSteps > 0) ? Math.min(Math.max(1, authoredSteps / tierSteps), CLOUD_MAX_STEP_STRETCH) : 1;
|
|
167561
|
+
return altitudeRange / CLOUD_MAX_STEP_DIVISOR * stretch;
|
|
167562
|
+
}
|
|
167563
|
+
static TierSettings(tier, authored) {
|
|
167564
|
+
const block = authored || {};
|
|
167565
|
+
const positiveOr = (value, fallback) => (typeof value === "number" && value > 0) ? value : fallback;
|
|
167566
|
+
const caps = CLOUD_TIER_CAPS[Math.max(0, Math.min(VolumetricCloudsMath.LowestTier, Math.floor(tier)))];
|
|
167567
|
+
return {
|
|
167568
|
+
resolutionScale: Math.min(positiveOr(block.resolutionscale, CLOUD_DEFAULT_SETTINGS.resolutionScale), caps.resolutionScale),
|
|
167569
|
+
primarySteps: Math.min(positiveOr(block.numprimarysteps, CLOUD_DEFAULT_SETTINGS.primarySteps), caps.primarySteps),
|
|
167570
|
+
longRayMultiplier: Math.min(positiveOr(block.longraysamplemultiplier, CLOUD_DEFAULT_SETTINGS.longRayMultiplier), caps.longRayMultiplier),
|
|
167571
|
+
lightSteps: Math.min(positiveOr(block.numlightsteps, CLOUD_DEFAULT_SETTINGS.lightSteps), caps.lightSteps)
|
|
167572
|
+
};
|
|
167573
|
+
}
|
|
167574
|
+
static StartTier(maximumTextureSize, viewportCssWidth) {
|
|
167575
|
+
if (maximumTextureSize < CLOUD_WEAK_GPU_TEXTURE_SIZE || viewportCssWidth < CLOUD_PHONE_WIDTH_CSS_PX)
|
|
167576
|
+
return 1;
|
|
167577
|
+
return 0;
|
|
167578
|
+
}
|
|
167579
|
+
static IsCameraCut(previousPosition, currentPosition) {
|
|
167580
|
+
if (previousPosition == null)
|
|
167581
|
+
return true;
|
|
167582
|
+
const distance = Math.hypot(currentPosition[0] - previousPosition[0], currentPosition[1] - previousPosition[1], currentPosition[2] - previousPosition[2]);
|
|
167583
|
+
return distance > CLOUD_CAMERA_CUT_METERS;
|
|
167584
|
+
}
|
|
167585
|
+
static PackCurves(density, erosion, ambientOcclusion) {
|
|
167586
|
+
const texels = new Float32Array(CLOUD_CURVE_SAMPLES * 4);
|
|
167587
|
+
const hasCurves = Array.isArray(density) && density.length >= CLOUD_CURVE_SAMPLES;
|
|
167588
|
+
const clamp01 = (value) => Math.max(0, Math.min(1, (typeof value === "number") ? value : 0));
|
|
167589
|
+
for (let i = 0; i < CLOUD_CURVE_SAMPLES; i++) {
|
|
167590
|
+
const isEnd = (i === 0 || i === CLOUD_CURVE_SAMPLES - 1);
|
|
167591
|
+
texels[i * 4] = !hasCurves ? 1 : (isEnd ? 0 : clamp01(density[i]));
|
|
167592
|
+
texels[i * 4 + 1] = (!hasCurves || !Array.isArray(erosion)) ? 1 : clamp01(erosion[i]);
|
|
167593
|
+
texels[i * 4 + 2] = (!hasCurves || !Array.isArray(ambientOcclusion)) ? 1 : clamp01(1 - ambientOcclusion[i]);
|
|
167594
|
+
texels[i * 4 + 3] = 1;
|
|
167595
|
+
}
|
|
167596
|
+
return texels;
|
|
167597
|
+
}
|
|
167598
|
+
static IntersectSphere(sphereRadius, cosChi, radialDistance) {
|
|
167599
|
+
const ratio = sphereRadius / radialDistance;
|
|
167600
|
+
const discriminant = ratio * ratio - Math.max(0, Math.min(1, 1 - cosChi * cosChi));
|
|
167601
|
+
if (discriminant < 0)
|
|
167602
|
+
return [discriminant, discriminant];
|
|
167603
|
+
const root = Math.sqrt(discriminant);
|
|
167604
|
+
return [radialDistance * (-cosChi - root), radialDistance * (-cosChi + root)];
|
|
167605
|
+
}
|
|
167606
|
+
}
|
|
167607
|
+
VolumetricCloudsMath.LowestTier = 2;
|
|
167608
|
+
export class VolumetricCloudsTier {
|
|
167609
|
+
constructor(startTier) {
|
|
167610
|
+
this.isSettled = false;
|
|
167611
|
+
this.windowMilliseconds = 0;
|
|
167612
|
+
this.windowFrames = 0;
|
|
167613
|
+
this.meanBeforeStep = 0;
|
|
167614
|
+
this.previousMean = 0;
|
|
167615
|
+
this.tier = Math.max(0, Math.min(VolumetricCloudsMath.LowestTier, startTier));
|
|
167616
|
+
}
|
|
167617
|
+
addFrame(frameMilliseconds) {
|
|
167618
|
+
this.windowMilliseconds += frameMilliseconds;
|
|
167619
|
+
this.windowFrames++;
|
|
167620
|
+
if (this.windowFrames < CLOUD_TIER_WINDOW_FRAMES)
|
|
167621
|
+
return false;
|
|
167622
|
+
const meanMilliseconds = this.windowMilliseconds / this.windowFrames;
|
|
167623
|
+
this.windowMilliseconds = 0;
|
|
167624
|
+
this.windowFrames = 0;
|
|
167625
|
+
if (this.meanBeforeStep > 0) {
|
|
167626
|
+
const savedEnough = meanMilliseconds <= this.meanBeforeStep * (1 - CLOUD_TIER_MINIMUM_SAVING);
|
|
167627
|
+
this.meanBeforeStep = 0;
|
|
167628
|
+
if (!savedEnough) {
|
|
167629
|
+
this.tier--;
|
|
167630
|
+
this.isSettled = true;
|
|
167631
|
+
return true;
|
|
167632
|
+
}
|
|
167633
|
+
}
|
|
167634
|
+
const previousMean = this.previousMean;
|
|
167635
|
+
this.previousMean = meanMilliseconds;
|
|
167636
|
+
const isSteady = previousMean > 0 && meanMilliseconds > previousMean * (1 - CLOUD_TIER_MINIMUM_SAVING);
|
|
167637
|
+
if (this.isSettled || !isSteady || meanMilliseconds <= CLOUD_TIER_BUDGET_MS || this.tier >= VolumetricCloudsMath.LowestTier)
|
|
167638
|
+
return false;
|
|
167639
|
+
this.tier++;
|
|
167640
|
+
this.meanBeforeStep = meanMilliseconds;
|
|
167641
|
+
return true;
|
|
167642
|
+
}
|
|
167643
|
+
}
|
|
167644
|
+
export class VolumetricClouds {
|
|
167645
|
+
static TryCreate(scene, metadata, root) {
|
|
167646
|
+
const block = PhysicalSkyBlock.UrpBlock(metadata);
|
|
167647
|
+
const clouds = (block != null) ? block.clouds : null;
|
|
167648
|
+
if (clouds == null || clouds.state === false || scene == null)
|
|
167649
|
+
return null;
|
|
167650
|
+
if (HdrpPhysicallyBasedSky == null || !HdrpPhysicallyBasedSky.IsSupported(scene.getEngine()))
|
|
167651
|
+
return null;
|
|
167652
|
+
const existing = VolumetricClouds.Get(scene);
|
|
167653
|
+
if (existing != null)
|
|
167654
|
+
return existing;
|
|
167655
|
+
return new VolumetricClouds(scene, clouds, block.sky, (typeof root === "string") ? root : "");
|
|
167656
|
+
}
|
|
167657
|
+
static Get(scene) {
|
|
167658
|
+
if (scene == null)
|
|
167659
|
+
return null;
|
|
167660
|
+
const clouds = scene._tkVolumetricClouds;
|
|
167661
|
+
return (clouds != null && clouds.disposed !== true) ? clouds : null;
|
|
167662
|
+
}
|
|
167663
|
+
constructor(scene, block, sky, root) {
|
|
167664
|
+
this.disposed = false;
|
|
167665
|
+
this.tier = 0;
|
|
167666
|
+
this.hasAerial = false;
|
|
167667
|
+
this.placeholderAerial = null;
|
|
167668
|
+
this.worleyTexture = null;
|
|
167669
|
+
this.erosionTexture = null;
|
|
167670
|
+
this.curveTexture = null;
|
|
167671
|
+
this.placeholderDepth = null;
|
|
167672
|
+
this.marchTarget = null;
|
|
167673
|
+
this.renderer = null;
|
|
167674
|
+
this.marchPass = null;
|
|
167675
|
+
this.temporalPass = null;
|
|
167676
|
+
this.historyTargets = [];
|
|
167677
|
+
this.historyIndex = 0;
|
|
167678
|
+
this.hasHistory = false;
|
|
167679
|
+
this.compositeMesh = null;
|
|
167680
|
+
this.compositeMaterial = null;
|
|
167681
|
+
this.isReady = false;
|
|
167682
|
+
this.isPending = false;
|
|
167683
|
+
this.preExposure = 1;
|
|
167684
|
+
this.preExposureBinding = null;
|
|
167685
|
+
this.frameSeed = 0;
|
|
167686
|
+
this.windOffset = [0, 0];
|
|
167687
|
+
this.verticalShapeOffset = 0;
|
|
167688
|
+
this.verticalErosionOffset = 0;
|
|
167689
|
+
this.sceneDepth = null;
|
|
167690
|
+
this.beforeRenderObserver = null;
|
|
167691
|
+
this.afterRenderObserver = null;
|
|
167692
|
+
this.tierTracker = null;
|
|
167693
|
+
this.targetWidth = 1;
|
|
167694
|
+
this.targetHeight = 1;
|
|
167695
|
+
this.lastCamera = null;
|
|
167696
|
+
this.sunLight = null;
|
|
167697
|
+
this.ambientSource = null;
|
|
167698
|
+
this.ambientCaptureCount = -1;
|
|
167699
|
+
this.previousEye = [0, 0, 0];
|
|
167700
|
+
this.currentEye = [0, 0, 0];
|
|
167701
|
+
this.hasPreviousEye = false;
|
|
167702
|
+
this.beforeTargetsObserver = null;
|
|
167703
|
+
this.afterTargetsObserver = null;
|
|
167704
|
+
this.disposeObserver = null;
|
|
167705
|
+
this.mirror = Matrix.Identity();
|
|
167706
|
+
this.inverseViewProjection = new Matrix();
|
|
167707
|
+
this.previousViewProjection = new Matrix();
|
|
167708
|
+
this.previousViewProjectionUnity = new Matrix();
|
|
167709
|
+
this.sunDirection = new Vector3();
|
|
167710
|
+
this.cameraForward = new Vector3();
|
|
167711
|
+
this.ambientTop = new Color3();
|
|
167712
|
+
this.ambientBottom = new Color3();
|
|
167713
|
+
this.scene = scene;
|
|
167714
|
+
this.block = block;
|
|
167715
|
+
this.sky = sky;
|
|
167716
|
+
this.root = root;
|
|
167717
|
+
scene._tkVolumetricClouds = this;
|
|
167718
|
+
const planet = (sky != null) ? sky.planet : null;
|
|
167719
|
+
this.isLocal = (planet != null && typeof planet.renderingspace === "number") ? planet.renderingspace === 1 : block.localclouds === true;
|
|
167720
|
+
const curvature = (typeof block.earthcurvature === "number") ? block.earthcurvature : 0;
|
|
167721
|
+
this.planetRadius = (planet != null && typeof planet.radius === "number" && planet.radius > 0) ? planet.radius : Scalar.Lerp(1, CLOUD_MINIMUM_CURVATURE_SCALE, curvature) * CLOUD_EARTH_RADIUS_METERS;
|
|
167722
|
+
this.planetCenter = (this.isLocal && planet != null && Array.isArray(planet.center)) ? Vector3.FromArray(planet.center) : new Vector3(0, -this.planetRadius, 0);
|
|
167723
|
+
this.scatteringTint = VolumetricClouds.LinearScatteringTint(block.scatteringtint);
|
|
167724
|
+
if (scene.useRightHandedSystem === true)
|
|
167725
|
+
Matrix.ScalingToRef(-1, 1, 1, this.mirror);
|
|
167726
|
+
this.localForward = new Vector3(0, 0, (scene.useRightHandedSystem === true) ? -1 : 1);
|
|
167727
|
+
this.tier = VolumetricCloudsMath.StartTier(scene.getEngine().getCaps().maxTextureSize, VolumetricClouds.CanvasCssWidth(scene));
|
|
167728
|
+
this.tierTracker = new VolumetricCloudsTier(this.tier);
|
|
167729
|
+
this.settings = VolumetricCloudsMath.TierSettings(this.tier, block);
|
|
167730
|
+
this.reportLoweredSettings();
|
|
167731
|
+
this.isPending = true;
|
|
167732
|
+
scene.addPendingData(this);
|
|
167733
|
+
this.preExposureBinding = HdrpRendering.Bind(scene, 1, 1, (scaled) => { this.preExposure = scaled; });
|
|
167734
|
+
this.disposeObserver = scene.onDisposeObservable.add(() => this.dispose());
|
|
167735
|
+
this.createPasses();
|
|
167736
|
+
this.loadNoise().then((loaded) => {
|
|
167737
|
+
if (this.disposed)
|
|
167738
|
+
return;
|
|
167739
|
+
if (!loaded) {
|
|
167740
|
+
this.releasePending();
|
|
167741
|
+
return;
|
|
167742
|
+
}
|
|
167743
|
+
this.waitUntilReady();
|
|
167744
|
+
});
|
|
167745
|
+
}
|
|
167746
|
+
dispose() {
|
|
167747
|
+
if (this.disposed)
|
|
167748
|
+
return;
|
|
167749
|
+
this.disposed = true;
|
|
167750
|
+
const scene = this.scene;
|
|
167751
|
+
this.releasePending();
|
|
167752
|
+
if (this.beforeRenderObserver != null)
|
|
167753
|
+
scene.onBeforeRenderObservable.remove(this.beforeRenderObserver);
|
|
167754
|
+
if (this.afterRenderObserver != null)
|
|
167755
|
+
scene.onAfterRenderObservable.remove(this.afterRenderObserver);
|
|
167756
|
+
if (this.beforeTargetsObserver != null)
|
|
167757
|
+
scene.onBeforeRenderTargetsRenderObservable.remove(this.beforeTargetsObserver);
|
|
167758
|
+
if (this.afterTargetsObserver != null)
|
|
167759
|
+
scene.onAfterRenderTargetsRenderObservable.remove(this.afterTargetsObserver);
|
|
167760
|
+
if (this.disposeObserver != null)
|
|
167761
|
+
scene.onDisposeObservable.remove(this.disposeObserver);
|
|
167762
|
+
if (this.preExposureBinding != null)
|
|
167763
|
+
HdrpRendering.Unbind(scene, this.preExposureBinding);
|
|
167764
|
+
const disposables = [this.compositeMesh, this.compositeMaterial, this.marchPass, this.temporalPass, this.renderer, this.marchTarget, this.worleyTexture, this.erosionTexture, this.curveTexture, this.placeholderDepth, this.placeholderAerial].concat(this.historyTargets);
|
|
167765
|
+
for (const disposable of disposables) {
|
|
167766
|
+
try {
|
|
167767
|
+
if (disposable != null)
|
|
167768
|
+
disposable.dispose();
|
|
167769
|
+
}
|
|
167770
|
+
catch (error) { }
|
|
167771
|
+
}
|
|
167772
|
+
const sceneWithClouds = scene;
|
|
167773
|
+
if (sceneWithClouds._tkVolumetricClouds === this)
|
|
167774
|
+
sceneWithClouds._tkVolumetricClouds = null;
|
|
167775
|
+
}
|
|
167776
|
+
createPasses() {
|
|
167777
|
+
const scene = this.scene;
|
|
167778
|
+
const engine = scene.getEngine();
|
|
167779
|
+
const isWebGPU = engine.isWebGPU === true;
|
|
167780
|
+
const language = isWebGPU ? ShaderLanguage.WGSL : ShaderLanguage.GLSL;
|
|
167781
|
+
const defines = [];
|
|
167782
|
+
if (this.isLocal)
|
|
167783
|
+
defines.push("#define CLOUD_LOCAL");
|
|
167784
|
+
if (this.block.microerosion === true && this.block.microerosionfactor > 0)
|
|
167785
|
+
defines.push("#define CLOUD_MICRO_EROSION");
|
|
167786
|
+
const liveSky = HdrpPhysicallyBasedSky.Get(scene);
|
|
167787
|
+
this.hasAerial = liveSky != null && HdrpFogMath != null && HdrpFogMath.HasAerialPerspective(liveSky);
|
|
167788
|
+
const marchUniforms = VolumetricCloudsShaders.MarchUniforms.slice();
|
|
167789
|
+
const marchSamplers = VolumetricCloudsShaders.MarchSamplers.slice();
|
|
167790
|
+
if (this.hasAerial) {
|
|
167791
|
+
defines.push("#define CLOUD_AERIAL");
|
|
167792
|
+
marchUniforms.push(VolumetricCloudsShaders.AerialCameraUniform, ...HdrpPbSkyShaders.AtmosphereUniforms);
|
|
167793
|
+
marchSamplers.push(VolumetricCloudsShaders.AerialSampler);
|
|
167794
|
+
this.placeholderAerial = new RawTexture2DArray(new Uint8Array(4), 1, 1, 1, Constants.TEXTUREFORMAT_RGBA, scene, false, false, Texture.NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
|
|
167795
|
+
this.placeholderAerial.name = "Volumetric Clouds Aerial Placeholder";
|
|
167796
|
+
}
|
|
167797
|
+
VolumetricCloudsShaders.Register();
|
|
167798
|
+
this.curveTexture = this.createCurveTexture();
|
|
167799
|
+
this.placeholderDepth = new RawTexture(new Float32Array([0]), 1, 1, Constants.TEXTUREFORMAT_R, scene, false, false, Texture.NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
|
|
167800
|
+
this.placeholderDepth.name = "Volumetric Clouds Depth Placeholder";
|
|
167801
|
+
const textureType = (engine.getCaps().textureHalfFloatRender === true) ? Constants.TEXTURETYPE_HALF_FLOAT : Constants.TEXTURETYPE_FLOAT;
|
|
167802
|
+
this.updateTargetSize();
|
|
167803
|
+
this.marchTarget = this.createCloudTarget("Volumetric Clouds Lighting", textureType);
|
|
167804
|
+
this.historyTargets.push(this.createCloudTarget("Volumetric Clouds History A", textureType), this.createCloudTarget("Volumetric Clouds History B", textureType));
|
|
167805
|
+
this.renderer = new EffectRenderer(engine);
|
|
167806
|
+
this.marchPass = new EffectWrapper({
|
|
167807
|
+
engine: engine, name: VolumetricCloudsShaders.MarchName, fragmentShader: VolumetricCloudsShaders.MarchName, useShaderStore: true,
|
|
167808
|
+
uniformNames: marchUniforms, samplerNames: marchSamplers,
|
|
167809
|
+
defines: defines.join("\n"), shaderLanguage: language
|
|
167810
|
+
});
|
|
167811
|
+
this.marchPass.onApplyObservable.add(() => this.bindMarch(this.marchPass.effect));
|
|
167812
|
+
this.temporalPass = new EffectWrapper({
|
|
167813
|
+
engine: engine, name: VolumetricCloudsShaders.TemporalName, fragmentShader: VolumetricCloudsShaders.TemporalName, useShaderStore: true,
|
|
167814
|
+
uniformNames: VolumetricCloudsShaders.TemporalUniforms.slice(), samplerNames: VolumetricCloudsShaders.TemporalSamplers.slice(),
|
|
167815
|
+
defines: defines.join("\n"), shaderLanguage: language
|
|
167816
|
+
});
|
|
167817
|
+
this.temporalPass.onApplyObservable.add(() => this.bindTemporal(this.temporalPass.effect));
|
|
167818
|
+
this.createCompositeMesh(language);
|
|
167819
|
+
}
|
|
167820
|
+
createCloudTarget(name, textureType) {
|
|
167821
|
+
const target = new RenderTargetTexture(name, { width: this.targetWidth, height: this.targetHeight }, this.scene, false, true, textureType, false, Texture.BILINEAR_SAMPLINGMODE, false, false, false, Constants.TEXTUREFORMAT_RGBA);
|
|
167822
|
+
target.wrapU = Texture.CLAMP_ADDRESSMODE;
|
|
167823
|
+
target.wrapV = Texture.CLAMP_ADDRESSMODE;
|
|
167824
|
+
target.anisotropicFilteringLevel = 1;
|
|
167825
|
+
return target;
|
|
167826
|
+
}
|
|
167827
|
+
createCurveTexture() {
|
|
167828
|
+
const curves = this.block.curves || {};
|
|
167829
|
+
const texels = VolumetricCloudsMath.PackCurves(curves.density, curves.erosion, curves.ambientocclusion);
|
|
167830
|
+
const halfTexels = new Uint16Array(texels.length);
|
|
167831
|
+
for (let i = 0; i < texels.length; i++)
|
|
167832
|
+
halfTexels[i] = TextureTools.ToHalfFloat(texels[i]);
|
|
167833
|
+
const texture = new RawTexture(halfTexels, CLOUD_CURVE_SAMPLES, 1, Constants.TEXTUREFORMAT_RGBA, this.scene, false, false, Texture.BILINEAR_SAMPLINGMODE, Constants.TEXTURETYPE_HALF_FLOAT);
|
|
167834
|
+
texture.name = "Volumetric Clouds Curves";
|
|
167835
|
+
texture.wrapU = Texture.WRAP_ADDRESSMODE;
|
|
167836
|
+
texture.wrapV = Texture.CLAMP_ADDRESSMODE;
|
|
167837
|
+
return texture;
|
|
167838
|
+
}
|
|
167839
|
+
createCompositeMesh(language) {
|
|
167840
|
+
const scene = this.scene;
|
|
167841
|
+
const mesh = new Mesh(VolumetricClouds.MeshName, scene);
|
|
167842
|
+
const vertexData = new VertexData();
|
|
167843
|
+
vertexData.positions = [-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0];
|
|
167844
|
+
vertexData.indices = [0, 1, 2, 0, 2, 3];
|
|
167845
|
+
vertexData.applyToMesh(mesh, false);
|
|
167846
|
+
const material = new ShaderMaterial("Volumetric Clouds Composite", scene, { vertex: VolumetricCloudsShaders.CompositeName, fragment: VolumetricCloudsShaders.CompositeName }, { attributes: ["position"], uniforms: ["cloudDisplayEncoded"], samplers: ["cloudLighting"], needAlphaBlending: true, shaderLanguage: language });
|
|
167847
|
+
material.alphaMode = Constants.ALPHA_PREMULTIPLIED;
|
|
167848
|
+
material.disableDepthWrite = true;
|
|
167849
|
+
material.depthFunction = Constants.ALWAYS;
|
|
167850
|
+
material.backFaceCulling = false;
|
|
167851
|
+
material.setTexture("cloudLighting", this.marchTarget);
|
|
167852
|
+
material.setFloat("cloudDisplayEncoded", this.isDisplayEncoded() ? 1 : 0);
|
|
167853
|
+
mesh.material = material;
|
|
167854
|
+
mesh.renderingGroupId = Utilities.DefaultRenderGroup();
|
|
167855
|
+
mesh.alphaIndex = CLOUD_ALPHA_INDEX;
|
|
167856
|
+
mesh.alwaysSelectAsActiveMesh = true;
|
|
167857
|
+
mesh.isPickable = false;
|
|
167858
|
+
mesh.doNotSyncBoundingInfo = true;
|
|
167859
|
+
mesh.applyFog = false;
|
|
167860
|
+
mesh.setEnabled(false);
|
|
167861
|
+
this.compositeMesh = mesh;
|
|
167862
|
+
this.compositeMaterial = material;
|
|
167863
|
+
this.beforeTargetsObserver = scene.onBeforeRenderTargetsRenderObservable.add(() => { mesh.isVisible = false; });
|
|
167864
|
+
this.afterTargetsObserver = scene.onAfterRenderTargetsRenderObservable.add(() => { mesh.isVisible = true; });
|
|
167865
|
+
}
|
|
167866
|
+
async loadNoise() {
|
|
167867
|
+
const noise = this.block.noise;
|
|
167868
|
+
if (noise == null || noise.worley == null || noise.erosion == null) {
|
|
167869
|
+
LightingConversions.WarnOnce("urp:clouds:nonoise", "Volumetric clouds: the export carries no cloud noise, so the clouds do not draw");
|
|
167870
|
+
return false;
|
|
167871
|
+
}
|
|
167872
|
+
try {
|
|
167873
|
+
const buffers = await Promise.all([
|
|
167874
|
+
Tools.LoadFileAsync(this.root + noise.worley.url, true),
|
|
167875
|
+
Tools.LoadFileAsync(this.root + noise.erosion.url, true)
|
|
167876
|
+
]);
|
|
167877
|
+
if (this.disposed)
|
|
167878
|
+
return false;
|
|
167879
|
+
this.worleyTexture = this.createNoiseTexture("Volumetric Clouds Worley Noise", buffers[0], noise.worley.size);
|
|
167880
|
+
this.erosionTexture = this.createNoiseTexture("Volumetric Clouds Erosion Noise", buffers[1], noise.erosion.size);
|
|
167881
|
+
return this.worleyTexture != null && this.erosionTexture != null;
|
|
167882
|
+
}
|
|
167883
|
+
catch (error) {
|
|
167884
|
+
LightingConversions.WarnOnce("urp:clouds:noiseload", "Volumetric clouds: a cloud noise file failed to load, so the clouds do not draw (" + error + ")");
|
|
167885
|
+
return false;
|
|
167886
|
+
}
|
|
167887
|
+
}
|
|
167888
|
+
createNoiseTexture(name, buffer, size) {
|
|
167889
|
+
if (buffer == null || buffer.byteLength !== size * size * size) {
|
|
167890
|
+
LightingConversions.WarnOnce("urp:clouds:noisesize", "Volumetric clouds: " + name + " is not " + size + "³ bytes, so the clouds do not draw");
|
|
167891
|
+
return null;
|
|
167892
|
+
}
|
|
167893
|
+
const texture = new RawTexture3D(new Uint8Array(buffer), size, size, size, Constants.TEXTUREFORMAT_R, this.scene, true, false, Texture.TRILINEAR_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
|
|
167894
|
+
texture.name = name;
|
|
167895
|
+
texture.wrapU = Texture.WRAP_ADDRESSMODE;
|
|
167896
|
+
texture.wrapV = Texture.WRAP_ADDRESSMODE;
|
|
167897
|
+
texture.wrapR = Texture.WRAP_ADDRESSMODE;
|
|
167898
|
+
return texture;
|
|
167899
|
+
}
|
|
167900
|
+
waitUntilReady() {
|
|
167901
|
+
if (this.disposed)
|
|
167902
|
+
return;
|
|
167903
|
+
const camera = this.scene.activeCamera;
|
|
167904
|
+
if (camera != null && this.sceneDepth == null)
|
|
167905
|
+
this.sceneDepth = CameraDepthCopy.AcquireForSampler(this.scene, camera, this);
|
|
167906
|
+
const noiseReady = this.worleyTexture.isReady() && this.erosionTexture.isReady() && this.curveTexture.isReady();
|
|
167907
|
+
const compositeReady = this.compositeMaterial.isReady(this.compositeMesh);
|
|
167908
|
+
if (!noiseReady || !this.marchPass.isReady() || !this.temporalPass.isReady() || !compositeReady || camera == null) {
|
|
167909
|
+
setTimeout(() => this.waitUntilReady(), CLOUD_READY_POLL_MS);
|
|
167910
|
+
return;
|
|
167911
|
+
}
|
|
167912
|
+
this.isReady = true;
|
|
167913
|
+
this.compositeMesh.setEnabled(true);
|
|
167914
|
+
this.beforeRenderObserver = this.scene.onBeforeRenderObservable.add(() => this.render());
|
|
167915
|
+
this.releasePending();
|
|
167916
|
+
SceneManager.WhenSceneRevealed(this.scene).then((revealed) => {
|
|
167917
|
+
if (!revealed || this.disposed)
|
|
167918
|
+
return;
|
|
167919
|
+
this.afterRenderObserver = this.scene.onAfterRenderObservable.add(() => this.updateTier());
|
|
167920
|
+
});
|
|
167921
|
+
}
|
|
167922
|
+
releasePending() {
|
|
167923
|
+
if (!this.isPending)
|
|
167924
|
+
return;
|
|
167925
|
+
this.isPending = false;
|
|
167926
|
+
this.scene.removePendingData(this);
|
|
167927
|
+
}
|
|
167928
|
+
render() {
|
|
167929
|
+
const camera = this.scene.activeCamera;
|
|
167930
|
+
if (!this.isReady || this.disposed || camera == null)
|
|
167931
|
+
return;
|
|
167932
|
+
this.advanceWind(this.scene.getEngine().getDeltaTime() / 1000);
|
|
167933
|
+
this.resizeTargets();
|
|
167934
|
+
this.frameSeed = (this.frameSeed + 1) % 65536;
|
|
167935
|
+
this.sceneDepth = CameraDepthCopy.AcquireForSampler(this.scene, camera, this);
|
|
167936
|
+
this.trackCamera(camera);
|
|
167937
|
+
this.renderer.render(this.marchPass, this.marchTarget);
|
|
167938
|
+
const accumulation = (typeof this.block.temporalaccumulationfactor === "number") ? this.block.temporalaccumulationfactor : 0;
|
|
167939
|
+
if (accumulation < CLOUD_MINIMUM_ACCUMULATION) {
|
|
167940
|
+
this.compositeMaterial.setTexture("cloudLighting", this.marchTarget);
|
|
167941
|
+
}
|
|
167942
|
+
else {
|
|
167943
|
+
this.historyIndex = 1 - this.historyIndex;
|
|
167944
|
+
this.renderer.render(this.temporalPass, this.historyTargets[this.historyIndex]);
|
|
167945
|
+
this.compositeMaterial.setTexture("cloudLighting", this.historyTargets[this.historyIndex]);
|
|
167946
|
+
this.hasHistory = true;
|
|
167947
|
+
}
|
|
167948
|
+
this.compositeMaterial.setFloat("cloudDisplayEncoded", this.isDisplayEncoded() ? 1 : 0);
|
|
167949
|
+
this.previousViewProjection.copyFrom(camera.getTransformationMatrix());
|
|
167950
|
+
}
|
|
167951
|
+
isDisplayEncoded() {
|
|
167952
|
+
return HdrpRendering.IsParity(this.scene) !== true && this.scene.imageProcessingConfiguration.applyByPostProcess !== true;
|
|
167953
|
+
}
|
|
167954
|
+
trackCamera(camera) {
|
|
167955
|
+
const eye = camera.globalPosition;
|
|
167956
|
+
this.currentEye[0] = eye.x;
|
|
167957
|
+
this.currentEye[1] = eye.y;
|
|
167958
|
+
this.currentEye[2] = eye.z;
|
|
167959
|
+
const isCut = camera !== this.lastCamera || VolumetricCloudsMath.IsCameraCut(this.hasPreviousEye ? this.previousEye : null, this.currentEye);
|
|
167960
|
+
if (isCut)
|
|
167961
|
+
this.hasHistory = false;
|
|
167962
|
+
this.lastCamera = camera;
|
|
167963
|
+
this.previousEye[0] = eye.x;
|
|
167964
|
+
this.previousEye[1] = eye.y;
|
|
167965
|
+
this.previousEye[2] = eye.z;
|
|
167966
|
+
this.hasPreviousEye = true;
|
|
167967
|
+
}
|
|
167968
|
+
updateTier() {
|
|
167969
|
+
if (this.disposed || !this.tierTracker.addFrame(this.scene.getEngine().getDeltaTime()))
|
|
167970
|
+
return;
|
|
167971
|
+
this.tier = this.tierTracker.tier;
|
|
167972
|
+
this.settings = VolumetricCloudsMath.TierSettings(this.tier, this.block);
|
|
167973
|
+
if (this.tierTracker.isSettled) {
|
|
167974
|
+
LightingConversions.WarnOnce("urp:clouds:restored", "Volumetric clouds: a lower tier saved under " + (CLOUD_TIER_MINIMUM_SAVING * 100) + " % of the frame time, so tier " + this.tier + " is kept");
|
|
167975
|
+
return;
|
|
167976
|
+
}
|
|
167977
|
+
LightingConversions.WarnOnce("urp:clouds:tier" + this.tier, "Volumetric clouds: frames ran over " + CLOUD_TIER_BUDGET_MS + " ms, trying tier " + this.tier
|
|
167978
|
+
+ " (resolution " + this.settings.resolutionScale + ", " + this.settings.primarySteps + " primary steps, " + this.settings.lightSteps + " light steps)");
|
|
167979
|
+
}
|
|
167980
|
+
reportLoweredSettings() {
|
|
167981
|
+
const block = this.block;
|
|
167982
|
+
const settings = this.settings;
|
|
167983
|
+
if (typeof block.numprimarysteps === "number" && settings.primarySteps < block.numprimarysteps) {
|
|
167984
|
+
LightingConversions.WarnOnce("urp:clouds:primarysteps:" + this.tier, "Volumetric clouds: " + block.numprimarysteps + " primary steps are capped at " + settings.primarySteps + " in the browser (tier " + this.tier + ")");
|
|
167985
|
+
}
|
|
167986
|
+
if (typeof block.longraysamplemultiplier === "number" && settings.longRayMultiplier < block.longraysamplemultiplier) {
|
|
167987
|
+
LightingConversions.WarnOnce("urp:clouds:longray:" + this.tier, "Volumetric clouds: long ray sample multiplier " + block.longraysamplemultiplier + " is capped at " + settings.longRayMultiplier + " in the browser (tier " + this.tier + ")");
|
|
167988
|
+
}
|
|
167989
|
+
if (typeof block.numlightsteps === "number" && settings.lightSteps < block.numlightsteps) {
|
|
167990
|
+
LightingConversions.WarnOnce("urp:clouds:lightsteps:" + this.tier, "Volumetric clouds: " + block.numlightsteps + " light steps are capped at " + settings.lightSteps + " (tier " + this.tier + ")");
|
|
167991
|
+
}
|
|
167992
|
+
if (typeof block.resolutionscale === "number" && settings.resolutionScale < block.resolutionscale) {
|
|
167993
|
+
LightingConversions.WarnOnce("urp:clouds:resolution:" + this.tier, "Volumetric clouds: resolution scale " + block.resolutionscale + " is lowered to " + settings.resolutionScale + " (tier " + this.tier + ")");
|
|
167994
|
+
}
|
|
167995
|
+
}
|
|
167996
|
+
advanceWind(deltaSeconds) {
|
|
167997
|
+
const block = this.block;
|
|
167998
|
+
const signedDelta = Math.min(Math.max(deltaSeconds, 0), 1) * CLOUD_WIND_KMH_TO_MS;
|
|
167999
|
+
const theta = Tools.ToRadians(block.globalorientation || 0);
|
|
168000
|
+
const speed = block.globalspeed || 0;
|
|
168001
|
+
this.windOffset[0] = (this.windOffset[0] + signedDelta * speed * Math.cos(theta)) % CLOUD_WIND_WRAP_METERS;
|
|
168002
|
+
this.windOffset[1] = (this.windOffset[1] + signedDelta * speed * Math.sin(theta)) % CLOUD_WIND_WRAP_METERS;
|
|
168003
|
+
this.verticalShapeOffset = (this.verticalShapeOffset + signedDelta * (block.verticalshapewindspeed || 0)) % CLOUD_WIND_WRAP_METERS;
|
|
168004
|
+
this.verticalErosionOffset = (this.verticalErosionOffset + signedDelta * (block.erosionspeedmultiplier || 0)) % CLOUD_WIND_WRAP_METERS;
|
|
168005
|
+
}
|
|
168006
|
+
resizeTargets() {
|
|
168007
|
+
if (!this.updateTargetSize())
|
|
168008
|
+
return;
|
|
168009
|
+
const size = { width: this.targetWidth, height: this.targetHeight };
|
|
168010
|
+
this.marchTarget.resize(size);
|
|
168011
|
+
for (const history of this.historyTargets)
|
|
168012
|
+
history.resize(size);
|
|
168013
|
+
this.hasHistory = false;
|
|
168014
|
+
}
|
|
168015
|
+
updateTargetSize() {
|
|
168016
|
+
const engine = this.scene.getEngine();
|
|
168017
|
+
const scale = this.settings.resolutionScale;
|
|
168018
|
+
const width = Math.max(1, Math.round(engine.getRenderWidth() * scale));
|
|
168019
|
+
const height = Math.max(1, Math.round(engine.getRenderHeight() * scale));
|
|
168020
|
+
if (width === this.targetWidth && height === this.targetHeight)
|
|
168021
|
+
return false;
|
|
168022
|
+
this.targetWidth = width;
|
|
168023
|
+
this.targetHeight = height;
|
|
168024
|
+
return true;
|
|
168025
|
+
}
|
|
168026
|
+
bindMarch(effect) {
|
|
168027
|
+
const scene = this.scene;
|
|
168028
|
+
const camera = scene.activeCamera;
|
|
168029
|
+
if (camera == null)
|
|
168030
|
+
return;
|
|
168031
|
+
const block = this.block;
|
|
168032
|
+
const settings = this.settings;
|
|
168033
|
+
const mirrorX = this.mirror.m[0];
|
|
168034
|
+
camera.getTransformationMatrix().invertToRef(this.inverseViewProjection);
|
|
168035
|
+
this.inverseViewProjection.multiplyToRef(this.mirror, this.inverseViewProjection);
|
|
168036
|
+
effect.setMatrix("cloudInvViewProj", this.inverseViewProjection);
|
|
168037
|
+
const eye = camera.globalPosition;
|
|
168038
|
+
effect.setFloat4("cloudEye", eye.x * mirrorX, eye.y, eye.z, 0);
|
|
168039
|
+
camera.getDirectionToRef(this.localForward, this.cameraForward);
|
|
168040
|
+
effect.setFloat4("cloudForward", this.cameraForward.x * mirrorX, this.cameraForward.y, this.cameraForward.z, mirrorX);
|
|
168041
|
+
const hasDepth = this.sceneDepth != null;
|
|
168042
|
+
effect.setTexture("sceneDepth", hasDepth ? this.sceneDepth : this.placeholderDepth);
|
|
168043
|
+
effect.setFloat4("cloudCamera", (camera.maxZ > 0) ? camera.maxZ : Number.MAX_VALUE, hasDepth ? 1 : 0, this.frameSeed, this.preExposure);
|
|
168044
|
+
if (this.hasAerial)
|
|
168045
|
+
this.bindAerial(effect);
|
|
168046
|
+
const center = this.planetCenter;
|
|
168047
|
+
const bottomRadius = this.planetRadius + (block.bottomaltitude || 0);
|
|
168048
|
+
const altitudeRange = block.altituderange || 1;
|
|
168049
|
+
effect.setFloat4("cloudPlanet", center.x * mirrorX, center.y, center.z, this.planetRadius);
|
|
168050
|
+
effect.setFloat4("cloudLayer", bottomRadius, bottomRadius + altitudeRange, VolumetricCloudsMath.MaximumStep(altitudeRange, block.numprimarysteps, settings.primarySteps), settings.primarySteps);
|
|
168051
|
+
const isAutomaticFade = block.fadeinmode !== CLOUD_FADE_MODE_MANUAL;
|
|
168052
|
+
const fadeStart = isAutomaticFade ? Math.max(altitudeRange * CLOUD_AUTO_FADE_START_FRACTION, camera.minZ) : Math.max(block.fadeinstart || 0, camera.minZ);
|
|
168053
|
+
const fadeDistance = isAutomaticFade ? altitudeRange * CLOUD_AUTO_FADE_DISTANCE_FRACTION : (block.fadeindistance || 0);
|
|
168054
|
+
effect.setFloat4("cloudSteps", settings.longRayMultiplier, settings.lightSteps, fadeStart, fadeDistance);
|
|
168055
|
+
effect.setFloat4("cloudShape", block.shapescale, block.shapefactor, block.erosionscale, block.erosionfactor);
|
|
168056
|
+
const density = block.densitymultiplier * block.densitymultiplier * CLOUD_DENSITY_SCALE;
|
|
168057
|
+
effect.setFloat4("cloudDetail", block.erosionocclusion, block.microerosionscale, block.microerosionfactor, density);
|
|
168058
|
+
const shapeOffset = Array.isArray(block.shapeoffset) ? block.shapeoffset : [0, 0, 0];
|
|
168059
|
+
effect.setFloat4("cloudShapeOffset", shapeOffset[0], shapeOffset[2], shapeOffset[1], block.altitudedistortion * CLOUD_ALTITUDE_DISTORTION_SCALE);
|
|
168060
|
+
const theta = Tools.ToRadians(block.globalorientation || 0);
|
|
168061
|
+
effect.setFloat4("cloudWind", -Math.cos(theta), -Math.sin(theta), this.windOffset[0], this.windOffset[1]);
|
|
168062
|
+
effect.setFloat4("cloudWindSpeed", block.shapespeedmultiplier, block.erosionspeedmultiplier, this.verticalShapeOffset, this.verticalErosionOffset);
|
|
168063
|
+
const coverageInverseSize = 1 / Math.max(CLOUD_MINIMUM_COVERAGE_PATTERN_METERS, block.coveragepatternsize || CLOUD_DEFAULT_COVERAGE_PATTERN_METERS);
|
|
168064
|
+
const coverageGap = (typeof block.coveragegapamount === "number") ? block.coveragegapamount : CLOUD_DEFAULT_COVERAGE_GAP;
|
|
168065
|
+
effect.setFloat4("cloudCoverage", block.coveragevariation || 0, coverageInverseSize, coverageGap, 0);
|
|
168066
|
+
const coverageOffset = Array.isArray(block.coveragepatternoffset) ? block.coveragepatternoffset : [0, 0];
|
|
168067
|
+
effect.setFloat4("cloudCoverageOffset", coverageOffset[0], coverageOffset[1], 0, 0);
|
|
168068
|
+
effect.setFloat4("cloudScattering", 1 - block.multiscattering * CLOUD_MULTI_SCATTERING_SCALE, block.powdereffectintensity, 0, 0);
|
|
168069
|
+
effect.setFloat4("cloudTint", this.scatteringTint.r, this.scatteringTint.g, this.scatteringTint.b, 0);
|
|
168070
|
+
this.bindLighting(effect, mirrorX);
|
|
168071
|
+
effect.setTexture("worleyNoise", this.worleyTexture);
|
|
168072
|
+
effect.setTexture("erosionNoise", this.erosionTexture);
|
|
168073
|
+
effect.setTexture("cloudCurves", this.curveTexture);
|
|
168074
|
+
}
|
|
168075
|
+
bindLighting(effect, mirrorX) {
|
|
168076
|
+
const block = this.block;
|
|
168077
|
+
const sun = this.resolveSun();
|
|
168078
|
+
const sunScale = (this.sky != null && typeof this.sky.sunintensityscale === "number") ? this.sky.sunintensityscale : 1;
|
|
168079
|
+
const dimmer = (typeof block.sunlightdimmer === "number") ? block.sunlightdimmer : 1;
|
|
168080
|
+
if (sun != null) {
|
|
168081
|
+
const direction = this.sunDirection;
|
|
168082
|
+
const strength = sun.intensity * sunScale * dimmer;
|
|
168083
|
+
effect.setFloat4("cloudSunDirection", -direction.x * mirrorX, -direction.y, -direction.z, 0);
|
|
168084
|
+
effect.setFloat4("cloudSunColor", sun.diffuse.r * strength, sun.diffuse.g * strength, sun.diffuse.b * strength, 0);
|
|
168085
|
+
}
|
|
168086
|
+
else {
|
|
168087
|
+
effect.setFloat4("cloudSunDirection", 0, 1, 0, 0);
|
|
168088
|
+
effect.setFloat4("cloudSunColor", 0, 0, 0, 0);
|
|
168089
|
+
}
|
|
168090
|
+
this.refreshAmbient();
|
|
168091
|
+
const ambientDimmer = (typeof block.ambientlightprobedimmer === "number") ? block.ambientlightprobedimmer : 1;
|
|
168092
|
+
effect.setFloat4("cloudAmbientTop", this.ambientTop.r * ambientDimmer, this.ambientTop.g * ambientDimmer, this.ambientTop.b * ambientDimmer, 0);
|
|
168093
|
+
effect.setFloat4("cloudAmbientBottom", this.ambientBottom.r * ambientDimmer, this.ambientBottom.g * ambientDimmer, this.ambientBottom.b * ambientDimmer, 0);
|
|
168094
|
+
}
|
|
168095
|
+
bindAerial(effect) {
|
|
168096
|
+
const sky = HdrpPhysicallyBasedSky.Get(this.scene);
|
|
168097
|
+
const isBound = sky != null && sky.bindAerialPerspective(effect, VolumetricCloudsShaders.AerialSampler, VolumetricCloudsShaders.AerialCameraUniform);
|
|
168098
|
+
if (isBound)
|
|
168099
|
+
return;
|
|
168100
|
+
effect.setTexture(VolumetricCloudsShaders.AerialSampler, this.placeholderAerial);
|
|
168101
|
+
effect.setFloat4(VolumetricCloudsShaders.AerialCameraUniform, 0, 0, 1, 0);
|
|
168102
|
+
}
|
|
168103
|
+
resolveSun() {
|
|
168104
|
+
if (this.sunLight == null || this.sunLight.isDisposed() || !this.sunLight.isEnabled()) {
|
|
168105
|
+
const lights = HdrpPhysicallyBasedSky.InteractingLights(this.scene.lights);
|
|
168106
|
+
this.sunLight = (lights.length > 0) ? lights[0] : null;
|
|
168107
|
+
}
|
|
168108
|
+
const sun = this.sunLight;
|
|
168109
|
+
if (sun == null)
|
|
168110
|
+
return null;
|
|
168111
|
+
sun.computeTransformedInformation();
|
|
168112
|
+
const transformed = sun.transformedDirection;
|
|
168113
|
+
this.sunDirection.copyFrom((transformed != null && sun.parent != null) ? transformed : sun.direction).normalize();
|
|
168114
|
+
return sun;
|
|
168115
|
+
}
|
|
168116
|
+
refreshAmbient() {
|
|
168117
|
+
const sky = HdrpPhysicallyBasedSky.Get(this.scene);
|
|
168118
|
+
const source = (sky != null) ? sky : this.scene.environmentTexture;
|
|
168119
|
+
const captureCount = (sky != null) ? sky.captureCount : 0;
|
|
168120
|
+
if (source === this.ambientSource && captureCount === this.ambientCaptureCount && source != null)
|
|
168121
|
+
return;
|
|
168122
|
+
this.ambientSource = source;
|
|
168123
|
+
this.ambientCaptureCount = captureCount;
|
|
168124
|
+
if (sky != null) {
|
|
168125
|
+
this.ambientTop.copyFrom(sky.ambientAt(Vector3.UpReadOnly));
|
|
168126
|
+
this.ambientBottom.copyFrom(sky.ambientAt(Vector3.DownReadOnly));
|
|
168127
|
+
return;
|
|
168128
|
+
}
|
|
168129
|
+
const environment = this.scene.environmentTexture;
|
|
168130
|
+
const polynomial = (environment != null) ? environment.sphericalPolynomial : null;
|
|
168131
|
+
if (polynomial != null) {
|
|
168132
|
+
this.ambientTop.copyFrom(HdrpPbSkyMath.EvaluatePolynomial(polynomial, Vector3.UpReadOnly));
|
|
168133
|
+
this.ambientBottom.copyFrom(HdrpPbSkyMath.EvaluatePolynomial(polynomial, Vector3.DownReadOnly));
|
|
168134
|
+
return;
|
|
168135
|
+
}
|
|
168136
|
+
this.ambientTop.copyFrom(this.scene.ambientColor);
|
|
168137
|
+
this.ambientBottom.copyFrom(this.scene.ambientColor);
|
|
168138
|
+
}
|
|
168139
|
+
bindTemporal(effect) {
|
|
168140
|
+
const camera = this.scene.activeCamera;
|
|
168141
|
+
if (camera == null)
|
|
168142
|
+
return;
|
|
168143
|
+
const mirrorX = this.mirror.m[0];
|
|
168144
|
+
this.mirror.multiplyToRef(this.previousViewProjection, this.previousViewProjectionUnity);
|
|
168145
|
+
effect.setMatrix("cloudInvViewProj", this.inverseViewProjection);
|
|
168146
|
+
effect.setMatrix("cloudPrevViewProj", this.previousViewProjectionUnity);
|
|
168147
|
+
const eye = camera.globalPosition;
|
|
168148
|
+
effect.setFloat4("cloudEye", eye.x * mirrorX, eye.y, eye.z, 0);
|
|
168149
|
+
effect.setFloat4("cloudForward", this.cameraForward.x * mirrorX, this.cameraForward.y, this.cameraForward.z, 0);
|
|
168150
|
+
const hasDepth = this.sceneDepth != null;
|
|
168151
|
+
effect.setTexture("sceneDepth", hasDepth ? this.sceneDepth : this.placeholderDepth);
|
|
168152
|
+
const accumulation = this.block.temporalaccumulationfactor;
|
|
168153
|
+
effect.setFloat4("cloudCamera", (camera.maxZ > 0) ? camera.maxZ : Number.MAX_VALUE, hasDepth ? 1 : 0, this.hasHistory ? 1 : 0, accumulation);
|
|
168154
|
+
const center = this.planetCenter;
|
|
168155
|
+
const bottomRadius = this.planetRadius + (this.block.bottomaltitude || 0);
|
|
168156
|
+
const altitudeRange = this.block.altituderange || 1;
|
|
168157
|
+
effect.setFloat4("cloudPlanet", center.x * mirrorX, center.y, center.z, this.planetRadius);
|
|
168158
|
+
effect.setFloat4("cloudLayer", bottomRadius, bottomRadius + altitudeRange, altitudeRange / CLOUD_MAX_STEP_DIVISOR, this.settings.primarySteps);
|
|
168159
|
+
effect.setFloat4("cloudTexel", 1 / this.targetWidth, 1 / this.targetHeight, 0, 0);
|
|
168160
|
+
effect.setTexture("cloudCurrent", this.marchTarget);
|
|
168161
|
+
effect.setTexture("cloudHistory", this.historyTargets[1 - this.historyIndex]);
|
|
168162
|
+
}
|
|
168163
|
+
static LinearScatteringTint(tint) {
|
|
168164
|
+
const source = Array.isArray(tint) ? tint : [0, 0, 0];
|
|
168165
|
+
return new Color3(1 - source[0] * CLOUD_SCATTERING_TINT_SCALE, 1 - source[1] * CLOUD_SCATTERING_TINT_SCALE, 1 - source[2] * CLOUD_SCATTERING_TINT_SCALE).toLinearSpace();
|
|
168166
|
+
}
|
|
168167
|
+
static CanvasCssWidth(scene) {
|
|
168168
|
+
const engine = scene.getEngine();
|
|
168169
|
+
const canvas = engine.getRenderingCanvas();
|
|
168170
|
+
if (canvas != null && typeof canvas.clientWidth === "number" && canvas.clientWidth > 0)
|
|
168171
|
+
return canvas.clientWidth;
|
|
168172
|
+
return engine.getRenderWidth();
|
|
168173
|
+
}
|
|
168174
|
+
}
|
|
168175
|
+
VolumetricClouds.MeshName = "Volumetric Clouds";
|
|
168176
|
+
export class VolumetricCloudsShaders {
|
|
168177
|
+
static Register() {
|
|
168178
|
+
if (VolumetricCloudsShaders.registered === true)
|
|
168179
|
+
return;
|
|
168180
|
+
const glsl = ShaderStore.ShadersStore;
|
|
168181
|
+
const wgsl = ShaderStore.ShadersStoreWGSL;
|
|
168182
|
+
const shaders = VolumetricCloudsShaders;
|
|
168183
|
+
glsl[shaders.MarchName + "FragmentShader"] = shaders.GlslMarch();
|
|
168184
|
+
glsl[shaders.CompositeName + "VertexShader"] = shaders.GlslCompositeVertex();
|
|
168185
|
+
glsl[shaders.CompositeName + "FragmentShader"] = shaders.GlslCompositeFragment();
|
|
168186
|
+
glsl[shaders.TemporalName + "FragmentShader"] = shaders.GlslTemporal();
|
|
168187
|
+
wgsl[shaders.MarchName + "FragmentShader"] = shaders.WgslMarch();
|
|
168188
|
+
wgsl[shaders.CompositeName + "VertexShader"] = shaders.WgslCompositeVertex();
|
|
168189
|
+
wgsl[shaders.CompositeName + "FragmentShader"] = shaders.WgslCompositeFragment();
|
|
168190
|
+
wgsl[shaders.TemporalName + "FragmentShader"] = shaders.WgslTemporal();
|
|
168191
|
+
VolumetricCloudsShaders.registered = true;
|
|
168192
|
+
}
|
|
168193
|
+
static AllSources() {
|
|
168194
|
+
const shaders = VolumetricCloudsShaders;
|
|
168195
|
+
return [
|
|
168196
|
+
shaders.GlslMarch(), shaders.GlslCompositeVertex(), shaders.GlslCompositeFragment(), shaders.GlslTemporal(),
|
|
168197
|
+
shaders.WgslMarch(), shaders.WgslCompositeVertex(), shaders.WgslCompositeFragment(), shaders.WgslTemporal()
|
|
168198
|
+
];
|
|
168199
|
+
}
|
|
168200
|
+
static GlslMarchUniforms() {
|
|
168201
|
+
return VolumetricCloudsShaders.MarchUniforms.map((name) => (name === "cloudInvViewProj") ? "uniform mat4 " + name + ";" : "uniform vec4 " + name + ";").join("\n") + "\n";
|
|
168202
|
+
}
|
|
168203
|
+
static GlslConstants() {
|
|
168204
|
+
return `const float CLOUD_DENSITY_THRESHOLD = 0.001;
|
|
168205
|
+
const int EMPTY_STEPS_BEFORE_LARGE_STEPS = 8;
|
|
168206
|
+
const float FORWARD_ECCENTRICITY = 0.7;
|
|
168207
|
+
const float BACKWARD_ECCENTRICITY = 0.7;
|
|
168208
|
+
const float MIN_EROSION_DISTANCE = 3000.0;
|
|
168209
|
+
const float MAX_EROSION_DISTANCE = 100000.0;
|
|
168210
|
+
const float NOISE_NORMALIZATION = 100000.0;
|
|
168211
|
+
const float MAX_SKYBOX_DISTANCE = 200000.0;
|
|
168212
|
+
const float LIGHT_STEP_MAXIMAL_SIZE = 1000.0;
|
|
168213
|
+
const float CLOUD_PI = 3.14159265359;
|
|
168214
|
+
const int MAX_PRIMARY_STEPS = 1024;
|
|
168215
|
+
const int MAX_LIGHT_STEPS = 16;
|
|
168216
|
+
`;
|
|
168217
|
+
}
|
|
168218
|
+
static GlslGeometry() {
|
|
168219
|
+
return `vec2 cloudIntersectSphere(float sphereRadius, float cosChi, float radialDistance) {
|
|
168220
|
+
float ratio = sphereRadius / radialDistance;
|
|
168221
|
+
float d = ratio * ratio - clamp(1.0 - cosChi * cosChi, 0.0, 1.0);
|
|
168222
|
+
return (d < 0.0) ? vec2(d) : radialDistance * vec2(-cosChi - sqrt(d), -cosChi + sqrt(d));
|
|
168223
|
+
}
|
|
168224
|
+
float cloudHorizonCosine(float radialDistance) {
|
|
168225
|
+
float sinHorizon = cloudPlanet.w / radialDistance;
|
|
168226
|
+
return -sqrt(clamp(1.0 - sinHorizon * sinHorizon, 0.0, 1.0));
|
|
168227
|
+
}
|
|
168228
|
+
bool cloudIntersectVolume(vec3 originPS, vec3 direction, out float entry, out float exitDistance) {
|
|
168229
|
+
float radialDistance = length(originPS);
|
|
168230
|
+
float cosChi = dot(originPS, direction) / radialDistance;
|
|
168231
|
+
vec2 inner = cloudIntersectSphere(cloudLayer.x, cosChi, radialDistance);
|
|
168232
|
+
vec2 outer = cloudIntersectSphere(cloudLayer.y, cosChi, radialDistance);
|
|
168233
|
+
if (inner.x < 0.0 && inner.y >= 0.0) {
|
|
168234
|
+
entry = inner.y;
|
|
168235
|
+
exitDistance = outer.y;
|
|
168236
|
+
return cosChi >= cloudHorizonCosine(radialDistance);
|
|
168237
|
+
}
|
|
168238
|
+
entry = max(outer.x, 0.0);
|
|
168239
|
+
exitDistance = (inner.x >= 0.0) ? inner.x : outer.y;
|
|
168240
|
+
return outer.y >= 0.0;
|
|
168241
|
+
}
|
|
168242
|
+
int cloudRaySphere(vec3 startWS, vec3 direction, float radius, out vec2 result) {
|
|
168243
|
+
vec3 startPS = startWS + vec3(0.0, cloudPlanet.w, 0.0);
|
|
168244
|
+
float a = dot(direction, direction);
|
|
168245
|
+
float b = 2.0 * dot(direction, startPS);
|
|
168246
|
+
float c = dot(startPS, startPS) - radius * radius;
|
|
168247
|
+
float d = b * b - 4.0 * a * c;
|
|
168248
|
+
result = vec2(0.0);
|
|
168249
|
+
int solutions = 0;
|
|
168250
|
+
if (d >= 0.0) {
|
|
168251
|
+
float q = -0.5 * (b + ((b >= 0.0) ? 1.0 : -1.0) * sqrt(d));
|
|
168252
|
+
result = vec2(c / q, q / a);
|
|
168253
|
+
solutions = 2;
|
|
168254
|
+
if (result.x < 0.0) { solutions--; result.x = result.y; }
|
|
168255
|
+
if (result.y < 0.0) solutions--;
|
|
168256
|
+
}
|
|
168257
|
+
return solutions;
|
|
168258
|
+
}
|
|
168259
|
+
bool cloudRange(vec3 originWS, vec3 direction, out float rangeStart, out float rangeEnd) {
|
|
168260
|
+
#ifdef CLOUD_LOCAL
|
|
168261
|
+
return cloudIntersectVolume(originWS - cloudPlanet.xyz, direction, rangeStart, rangeEnd);
|
|
168262
|
+
#else
|
|
168263
|
+
vec2 inner = vec2(0.0);
|
|
168264
|
+
vec2 outer = vec2(0.0);
|
|
168265
|
+
cloudRaySphere(originWS, direction, cloudLayer.x, inner);
|
|
168266
|
+
cloudRaySphere(originWS, direction, cloudLayer.y, outer);
|
|
168267
|
+
rangeStart = inner.x;
|
|
168268
|
+
rangeEnd = outer.x;
|
|
168269
|
+
return true;
|
|
168270
|
+
#endif
|
|
168271
|
+
}
|
|
168272
|
+
`;
|
|
168273
|
+
}
|
|
168274
|
+
static GlslDensity() {
|
|
168275
|
+
return `struct CloudProperties {
|
|
168276
|
+
float density;
|
|
168277
|
+
float ambientOcclusion;
|
|
168278
|
+
float height;
|
|
168279
|
+
float sigmaT;
|
|
168280
|
+
};
|
|
168281
|
+
float cloudRemap(float x, float a, float b, float c, float d) {
|
|
168282
|
+
return (((x - a) / (b - a)) * (d - c)) + c;
|
|
168283
|
+
}
|
|
168284
|
+
float cloudNormalizedHeight(vec3 positionPS) {
|
|
168285
|
+
return clamp((length(positionPS) - cloudLayer.x) / (cloudLayer.y - cloudLayer.x), 0.0, 1.0);
|
|
168286
|
+
}
|
|
168287
|
+
vec3 cloudAnimateShape(vec3 positionPS) {
|
|
168288
|
+
vec3 position = positionPS;
|
|
168289
|
+
position.y += position.x / 3.0 + position.z / 7.0;
|
|
168290
|
+
return position + vec3(cloudWind.z, 0.0, cloudWind.w) * cloudWindSpeed.x + vec3(0.0, cloudWindSpeed.z, 0.0);
|
|
168291
|
+
}
|
|
168292
|
+
vec3 cloudAnimateErosion(vec3 positionPS) {
|
|
168293
|
+
return positionPS + vec3(cloudWind.z, 0.0, cloudWind.w) * cloudWindSpeed.y + vec3(0.0, cloudWindSpeed.w, 0.0);
|
|
168294
|
+
}
|
|
168295
|
+
float cloudCoverageHash(vec2 cell) {
|
|
168296
|
+
vec3 p = fract(vec3(cell.x, cell.y, cell.x) * 0.1031);
|
|
168297
|
+
p += dot(p, p.yzx + 33.33);
|
|
168298
|
+
return fract((p.x + p.y) * p.z);
|
|
168299
|
+
}
|
|
168300
|
+
float cloudCoverageNoise(vec2 position) {
|
|
168301
|
+
vec2 cell = floor(position);
|
|
168302
|
+
vec2 blend = fract(position);
|
|
168303
|
+
blend = blend * blend * (3.0 - 2.0 * blend);
|
|
168304
|
+
float bottom = mix(cloudCoverageHash(cell), cloudCoverageHash(cell + vec2(1.0, 0.0)), blend.x);
|
|
168305
|
+
float top = mix(cloudCoverageHash(cell + vec2(0.0, 1.0)), cloudCoverageHash(cell + vec2(1.0, 1.0)), blend.x);
|
|
168306
|
+
return mix(bottom, top, blend.y);
|
|
168307
|
+
}
|
|
168308
|
+
vec2 cloudCoverageAt(vec3 positionPS) {
|
|
168309
|
+
float coverage = 0.9;
|
|
168310
|
+
float shapeFill = 0.0;
|
|
168311
|
+
if (cloudCoverage.x != 0.0) {
|
|
168312
|
+
vec2 coordinates = (positionPS.xz + cloudWind.zw * cloudWindSpeed.x + cloudCoverageOffset.xy) * cloudCoverage.y;
|
|
168313
|
+
float coverageNoise = cloudCoverageNoise(coordinates) * 0.7 + cloudCoverageNoise(coordinates * 2.03 + vec2(17.13, 43.71)) * 0.3;
|
|
168314
|
+
float threshold = mix(-0.15, 1.15, cloudCoverage.z);
|
|
168315
|
+
float mask = smoothstep(threshold - 0.12, threshold + 0.12, coverageNoise);
|
|
168316
|
+
if (cloudCoverage.x > 0.0) {
|
|
168317
|
+
coverage *= mix(1.0, mask, cloudCoverage.x);
|
|
168318
|
+
} else {
|
|
168319
|
+
shapeFill = clamp(-cloudCoverage.x, 0.0, 1.0) * (1.0 - mask);
|
|
168320
|
+
coverage = mix(coverage, 1.0, shapeFill);
|
|
168321
|
+
}
|
|
168322
|
+
}
|
|
168323
|
+
return vec2(coverage, shapeFill);
|
|
168324
|
+
}
|
|
168325
|
+
CloudProperties cloudPropertiesAt(vec3 samplePositionPS, float noiseMip, float erosionMip, bool cheapVersion, bool lightSampling) {
|
|
168326
|
+
CloudProperties properties = CloudProperties(0.0, 0.0, 0.0, 0.0);
|
|
168327
|
+
vec3 positionPS = samplePositionPS;
|
|
168328
|
+
#ifndef CLOUD_LOCAL
|
|
168329
|
+
if (positionPS.y < cloudPlanet.w) return properties;
|
|
168330
|
+
#endif
|
|
168331
|
+
properties.ambientOcclusion = 1.0;
|
|
168332
|
+
properties.height = cloudNormalizedHeight(positionPS);
|
|
168333
|
+
#ifndef CLOUD_LOCAL
|
|
168334
|
+
positionPS.xz += cloudEye.xz;
|
|
168335
|
+
#endif
|
|
168336
|
+
vec3 baseCoordinates = (cloudAnimateShape(positionPS).xzy / NOISE_NORMALIZATION) * cloudShape.x - cloudShapeOffset.xyz;
|
|
168337
|
+
baseCoordinates += properties.height * vec3(cloudWind.x, cloudWind.y, 0.0) * cloudShapeOffset.w;
|
|
168338
|
+
float lowFrequencyNoise = textureLod(worleyNoise, baseCoordinates, noiseMip).r;
|
|
168339
|
+
vec2 coverage = cloudCoverageAt(positionPS);
|
|
168340
|
+
if (coverage.x <= CLOUD_DENSITY_THRESHOLD) return properties;
|
|
168341
|
+
vec3 curves = textureLod(cloudCurves, vec2(properties.height, 0.5), 0.0).xyz;
|
|
168342
|
+
float shapeFactor = mix(0.1, 1.0, cloudShape.y * (1.0 - coverage.y)) * curves.y;
|
|
168343
|
+
float erosionFactor = cloudShape.w * curves.y;
|
|
168344
|
+
float microDetailFactor = cloudDetail.z * curves.y;
|
|
168345
|
+
lowFrequencyNoise = mix(1.0, lowFrequencyNoise, shapeFactor);
|
|
168346
|
+
float baseCloud = 1.0 - curves.x * coverage.x * (1.0 - shapeFactor);
|
|
168347
|
+
baseCloud = clamp(cloudRemap(lowFrequencyNoise, baseCloud, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x * coverage.x;
|
|
168348
|
+
properties.sigmaT = 0.04;
|
|
168349
|
+
float occlusionBlend = clamp(1.0 - max(erosionFactor, shapeFactor) * 0.5, 0.0, 1.0);
|
|
168350
|
+
properties.ambientOcclusion = mix(1.0, curves.z, occlusionBlend);
|
|
168351
|
+
if (!cheapVersion) {
|
|
168352
|
+
vec3 erosionCoordinates = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * cloudShape.z;
|
|
168353
|
+
float erosionValue = 1.0 - textureLod(erosionNoise, erosionCoordinates, erosionMip).r;
|
|
168354
|
+
erosionValue = mix(0.0, erosionValue, erosionFactor * 0.75 * coverage.x);
|
|
168355
|
+
properties.ambientOcclusion = clamp(properties.ambientOcclusion - sqrt(erosionValue * cloudDetail.x), 0.0, 1.0);
|
|
168356
|
+
baseCloud = cloudRemap(baseCloud, erosionValue, 1.0, 0.0, 1.0);
|
|
168357
|
+
#ifdef CLOUD_MICRO_EROSION
|
|
168358
|
+
vec3 fineCoordinates = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * cloudDetail.y;
|
|
168359
|
+
float fineValue = 1.0 - textureLod(erosionNoise, fineCoordinates, erosionMip).r;
|
|
168360
|
+
fineValue = mix(0.0, fineValue, microDetailFactor * 0.5 * coverage.x);
|
|
168361
|
+
baseCloud = cloudRemap(baseCloud, fineValue, 1.0, 0.0, 1.0);
|
|
168362
|
+
#endif
|
|
168363
|
+
}
|
|
168364
|
+
if (lightSampling) {
|
|
168365
|
+
baseCloud -= erosionFactor * 0.1;
|
|
168366
|
+
#ifdef CLOUD_MICRO_EROSION
|
|
168367
|
+
baseCloud -= microDetailFactor * 0.15;
|
|
168368
|
+
#endif
|
|
168369
|
+
}
|
|
168370
|
+
properties.density = max(0.0, baseCloud) * cloudDetail.w;
|
|
168371
|
+
return properties;
|
|
168372
|
+
}
|
|
168373
|
+
`;
|
|
168374
|
+
}
|
|
168375
|
+
static GlslLighting() {
|
|
168376
|
+
return `float cloudPhase(float anisotropy, float cosTheta) {
|
|
168377
|
+
float denominator = 1.0 + anisotropy * anisotropy - 2.0 * anisotropy * cosTheta;
|
|
168378
|
+
float falloff = inversesqrt(max(denominator, 1e-6));
|
|
168379
|
+
return (1.0 / (4.0 * CLOUD_PI)) * (1.0 - anisotropy * anisotropy) * falloff * falloff * falloff;
|
|
168380
|
+
}
|
|
168381
|
+
float cloudPowder(float density, float cosAngle, float intensity) {
|
|
168382
|
+
float powder = clamp((1.0 - exp(-density * 4.0)) * 2.0, 0.0, 1.0);
|
|
168383
|
+
return mix(1.0, mix(1.0, powder, smoothstep(0.5, -0.5, cosAngle)), intensity);
|
|
168384
|
+
}
|
|
168385
|
+
vec3 cloudSunTransmittance(vec3 positionPS, vec3 sunDirection, vec2 phase) {
|
|
168386
|
+
float radialDistance = length(positionPS);
|
|
168387
|
+
float cosChi = dot(positionPS, sunDirection) / radialDistance;
|
|
168388
|
+
vec3 transmittance = vec3(0.0);
|
|
168389
|
+
if (cosChi < cloudHorizonCosine(radialDistance)) return transmittance;
|
|
168390
|
+
float lightSteps = cloudSteps.y;
|
|
168391
|
+
float totalLightDistance = clamp(cloudIntersectSphere(cloudLayer.y, cosChi, radialDistance).y, 0.0, lightSteps * LIGHT_STEP_MAXIMAL_SIZE) + 5.0;
|
|
168392
|
+
float intervalSize = totalLightDistance / lightSteps;
|
|
168393
|
+
float opticalDepth = 0.0;
|
|
168394
|
+
for (int j = 0; j < MAX_LIGHT_STEPS; j++) {
|
|
168395
|
+
if (float(j) >= lightSteps) break;
|
|
168396
|
+
float distanceAlongLight = intervalSize * (0.25 + float(j));
|
|
168397
|
+
CloudProperties lightProperties = cloudPropertiesAt(positionPS + sunDirection * distanceAlongLight, 3.0 * float(j) / lightSteps, 0.0, true, true);
|
|
168398
|
+
opticalDepth += lightProperties.density * lightProperties.sigmaT;
|
|
168399
|
+
}
|
|
168400
|
+
vec3 extinction = intervalSize * opticalDepth * cloudTint.rgb;
|
|
168401
|
+
transmittance += exp(-extinction) * phase.x;
|
|
168402
|
+
transmittance += exp(-extinction * cloudScattering.x) * (phase.y * cloudScattering.x);
|
|
168403
|
+
return transmittance;
|
|
168404
|
+
}
|
|
168405
|
+
`;
|
|
168406
|
+
}
|
|
168407
|
+
static GlslAerial() {
|
|
168408
|
+
const skyShaders = HdrpPbSkyShaders;
|
|
168409
|
+
const fogPass = HdrpFogPass;
|
|
168410
|
+
if (skyShaders == null || fogPass == null)
|
|
168411
|
+
return "";
|
|
168412
|
+
return "#ifdef CLOUD_AERIAL\nprecision highp sampler2DArray;\nuniform sampler2DArray apLut;\nuniform vec4 cloudApCamera;\n"
|
|
168413
|
+
+ skyShaders.GlslUniforms() + skyShaders.GlslCommon() + fogPass.AerialPerspectiveGlsl() + "#endif\n";
|
|
168414
|
+
}
|
|
168415
|
+
static GlslMarch() {
|
|
168416
|
+
const shaders = VolumetricCloudsShaders;
|
|
168417
|
+
return "precision highp float;\nprecision highp sampler3D;\nvarying vec2 vUV;\nuniform sampler3D worleyNoise;\nuniform sampler3D erosionNoise;\nuniform sampler2D cloudCurves;\nuniform sampler2D sceneDepth;\n"
|
|
168418
|
+
+ shaders.GlslMarchUniforms() + shaders.GlslConstants() + shaders.GlslGeometry() + shaders.GlslDensity() + shaders.GlslLighting() + shaders.GlslAerial() + `
|
|
168419
|
+
uint cloudJenkins(uint value) {
|
|
168420
|
+
uint x = value;
|
|
168421
|
+
x += (x << 10u);
|
|
168422
|
+
x ^= (x >> 6u);
|
|
168423
|
+
x += (x << 3u);
|
|
168424
|
+
x ^= (x >> 11u);
|
|
168425
|
+
x += (x << 15u);
|
|
168426
|
+
return x;
|
|
168427
|
+
}
|
|
168428
|
+
float cloudRandom(vec2 pixel, float seed) {
|
|
168429
|
+
uvec3 v = uvec3(uvec2(pixel), uint(seed));
|
|
168430
|
+
uint hash = cloudJenkins(v.x ^ cloudJenkins(v.y ^ cloudJenkins(v.z)));
|
|
168431
|
+
return uintBitsToFloat((hash & 0x007FFFFFu) | 0x3F800000u) - 1.0;
|
|
168432
|
+
}
|
|
168433
|
+
void main(void) {
|
|
168434
|
+
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
|
168435
|
+
vec4 farPoint = cloudInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
|
|
168436
|
+
vec3 direction = normalize(farPoint.xyz / farPoint.w - cloudEye.xyz);
|
|
168437
|
+
float maxRayLength = MAX_SKYBOX_DISTANCE;
|
|
168438
|
+
if (cloudCamera.y > 0.5) {
|
|
168439
|
+
float viewDepth = textureLod(sceneDepth, vUV, 0.0).r;
|
|
168440
|
+
if (viewDepth < cloudCamera.x * 0.999) {
|
|
168441
|
+
#ifdef CLOUD_LOCAL
|
|
168442
|
+
maxRayLength = viewDepth / max(dot(direction, cloudForward.xyz), 0.0001);
|
|
168443
|
+
#else
|
|
168444
|
+
return;
|
|
168445
|
+
#endif
|
|
168446
|
+
}
|
|
168447
|
+
}
|
|
168448
|
+
#ifdef CLOUD_LOCAL
|
|
168449
|
+
vec3 originWS = cloudEye.xyz;
|
|
168450
|
+
#else
|
|
168451
|
+
vec3 originWS = vec3(0.0);
|
|
168452
|
+
#endif
|
|
168453
|
+
float rangeStart = 0.0;
|
|
168454
|
+
float rangeEnd = 0.0;
|
|
168455
|
+
if (!cloudRange(originWS, direction, rangeStart, rangeEnd)) return;
|
|
168456
|
+
if (maxRayLength < rangeStart) return;
|
|
168457
|
+
float integrationNoise = cloudRandom(gl_FragCoord.xy, cloudCamera.z);
|
|
168458
|
+
float primarySteps = cloudLayer.w;
|
|
168459
|
+
float totalDistance = min(rangeEnd, maxRayLength) - rangeStart;
|
|
168460
|
+
float stepSize = min(totalDistance / primarySteps, cloudLayer.z);
|
|
168461
|
+
totalDistance = stepSize * primarySteps;
|
|
168462
|
+
float layerThickness = max(1.0, cloudLayer.y - cloudLayer.x);
|
|
168463
|
+
float sampleMultiplier = min(max(1.0, totalDistance / layerThickness), max(1.0, cloudSteps.x));
|
|
168464
|
+
int primaryStepCount = min(int(ceil(primarySteps * sampleMultiplier)), MAX_PRIMARY_STEPS);
|
|
168465
|
+
stepSize = totalDistance / float(max(1, primaryStepCount));
|
|
168466
|
+
vec3 sunDirection = cloudSunDirection.xyz;
|
|
168467
|
+
float cosAngle = dot(direction, sunDirection);
|
|
168468
|
+
vec2 phase = vec2(
|
|
168469
|
+
cloudPhase(FORWARD_ECCENTRICITY, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY, cosAngle),
|
|
168470
|
+
cloudPhase(FORWARD_ECCENTRICITY * cloudScattering.x, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY * cloudScattering.x, cosAngle));
|
|
168471
|
+
vec3 scattering = vec3(0.0);
|
|
168472
|
+
float ambient = 0.0;
|
|
168473
|
+
float transmittance = 1.0;
|
|
168474
|
+
float meanDistance = 0.0;
|
|
168475
|
+
float meanDistanceDivider = 0.0;
|
|
168476
|
+
float currentDistance = integrationNoise;
|
|
168477
|
+
vec3 currentPositionWS = originWS + (rangeStart + currentDistance) * direction;
|
|
168478
|
+
bool activeSampling = true;
|
|
168479
|
+
int sequentialEmptySamples = 0;
|
|
168480
|
+
int currentIndex = 0;
|
|
168481
|
+
for (int iteration = 0; iteration < MAX_PRIMARY_STEPS * 2; iteration++) {
|
|
168482
|
+
if (currentIndex >= primaryStepCount || currentDistance >= totalDistance) break;
|
|
168483
|
+
float cameraDistance = rangeStart + currentDistance;
|
|
168484
|
+
float densityFade = clamp((cameraDistance - cloudSteps.z) / (cloudSteps.z + cloudSteps.w), 0.0, 1.0);
|
|
168485
|
+
float erosionMip = mix(0.0, 4.0, clamp((cameraDistance - MIN_EROSION_DISTANCE) / (MAX_EROSION_DISTANCE - MIN_EROSION_DISTANCE), 0.0, 1.0));
|
|
168486
|
+
vec3 currentPositionPS = currentPositionWS - cloudPlanet.xyz;
|
|
168487
|
+
if (activeSampling) {
|
|
168488
|
+
CloudProperties properties = cloudPropertiesAt(currentPositionPS, 0.0, erosionMip, false, false);
|
|
168489
|
+
properties.density *= densityFade;
|
|
168490
|
+
if (properties.density > CLOUD_DENSITY_THRESHOLD) {
|
|
168491
|
+
float transmittanceDensity = transmittance * properties.density;
|
|
168492
|
+
meanDistance += cameraDistance * transmittanceDensity;
|
|
168493
|
+
meanDistanceDivider += transmittanceDensity;
|
|
168494
|
+
float stepTransmittance = exp(-properties.density * properties.sigmaT * stepSize);
|
|
168495
|
+
float powder = cloudPowder(properties.density, cosAngle, cloudScattering.y);
|
|
168496
|
+
vec3 sunLuminance = cloudSunTransmittance(currentPositionPS, sunDirection, phase) * powder;
|
|
168497
|
+
float absorbed = transmittance - transmittance * stepTransmittance;
|
|
168498
|
+
scattering += sunLuminance * absorbed;
|
|
168499
|
+
ambient += properties.ambientOcclusion * absorbed;
|
|
168500
|
+
transmittance *= stepTransmittance;
|
|
168501
|
+
if (transmittance < 0.003) {
|
|
168502
|
+
transmittance = 0.0;
|
|
168503
|
+
break;
|
|
168504
|
+
}
|
|
168505
|
+
sequentialEmptySamples = 0;
|
|
168506
|
+
} else {
|
|
168507
|
+
sequentialEmptySamples++;
|
|
168508
|
+
}
|
|
168509
|
+
if (sequentialEmptySamples == EMPTY_STEPS_BEFORE_LARGE_STEPS) activeSampling = false;
|
|
168510
|
+
float relativeStepSize = mix(integrationNoise, 1.0, clamp(float(currentIndex), 0.0, 1.0));
|
|
168511
|
+
currentPositionWS += direction * stepSize * relativeStepSize;
|
|
168512
|
+
currentDistance += stepSize * relativeStepSize;
|
|
168513
|
+
} else {
|
|
168514
|
+
CloudProperties properties = cloudPropertiesAt(currentPositionPS, 1.0, 0.0, true, false);
|
|
168515
|
+
properties.density *= densityFade;
|
|
168516
|
+
if (properties.density < CLOUD_DENSITY_THRESHOLD) {
|
|
168517
|
+
currentPositionWS += direction * stepSize * 2.0;
|
|
168518
|
+
currentDistance += stepSize * 2.0;
|
|
168519
|
+
} else {
|
|
168520
|
+
currentPositionWS -= direction * stepSize;
|
|
168521
|
+
currentDistance -= stepSize;
|
|
168522
|
+
currentIndex -= 1;
|
|
168523
|
+
activeSampling = true;
|
|
168524
|
+
sequentialEmptySamples = 0;
|
|
168525
|
+
}
|
|
168526
|
+
}
|
|
168527
|
+
currentIndex++;
|
|
168528
|
+
}
|
|
168529
|
+
if (meanDistance == 0.0) return;
|
|
168530
|
+
meanDistance = min(meanDistance / meanDistanceDivider, maxRayLength);
|
|
168531
|
+
float relativeHeight = cloudNormalizedHeight(originWS - cloudPlanet.xyz + meanDistance * direction);
|
|
168532
|
+
vec3 ambientColor = max(vec3(0.0), mix(cloudAmbientBottom.rgb, cloudAmbientTop.rgb, relativeHeight));
|
|
168533
|
+
vec3 color = (cloudSunColor.rgb * scattering + ambientColor * ambient) * cloudCamera.w;
|
|
168534
|
+
#ifdef CLOUD_AERIAL
|
|
168535
|
+
if (cloudApCamera.w > 0.5) {
|
|
168536
|
+
vec3 apDirection = vec3(direction.x * cloudForward.w, direction.y, direction.z);
|
|
168537
|
+
vec3 apColor = vec3(0.0);
|
|
168538
|
+
vec3 apOpacity = vec3(0.0);
|
|
168539
|
+
fogAerialPerspective(vUV, cloudApCamera.xyz, apDirection, meanDistance, apColor, apOpacity);
|
|
168540
|
+
color = apColor * (1.0 - transmittance) + (vec3(1.0) - apOpacity) * color;
|
|
168541
|
+
}
|
|
168542
|
+
#endif
|
|
168543
|
+
gl_FragColor = vec4(color, transmittance);
|
|
168544
|
+
}
|
|
168545
|
+
`;
|
|
168546
|
+
}
|
|
168547
|
+
static GlslCompositeVertex() {
|
|
168548
|
+
return `precision highp float;
|
|
168549
|
+
attribute vec3 position;
|
|
168550
|
+
varying vec2 vUV;
|
|
168551
|
+
void main(void) {
|
|
168552
|
+
vUV = position.xy * 0.5 + 0.5;
|
|
168553
|
+
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
168554
|
+
}
|
|
168555
|
+
`;
|
|
168556
|
+
}
|
|
168557
|
+
static GlslCompositeFragment() {
|
|
168558
|
+
return `precision highp float;
|
|
168559
|
+
varying vec2 vUV;
|
|
168560
|
+
uniform sampler2D cloudLighting;
|
|
168561
|
+
uniform float cloudDisplayEncoded;
|
|
168562
|
+
const float CLOUD_DISPLAY_GAMMA = 2.2;
|
|
168563
|
+
const vec3 CLOUD_LUMINANCE = vec3(0.2126, 0.7152, 0.0722);
|
|
168564
|
+
void main(void) {
|
|
168565
|
+
vec4 cloud = texture2D(cloudLighting, vUV);
|
|
168566
|
+
vec3 scattering = cloud.rgb;
|
|
168567
|
+
float throughput = cloud.a;
|
|
168568
|
+
if (cloudDisplayEncoded > 0.5) {
|
|
168569
|
+
float encodeExponent = 1.0 / CLOUD_DISPLAY_GAMMA;
|
|
168570
|
+
float scatteringLuminance = clamp(dot(scattering, CLOUD_LUMINANCE), 0.0, 1.0);
|
|
168571
|
+
throughput = pow(min(cloud.a + scatteringLuminance, 1.0), encodeExponent) - pow(scatteringLuminance, encodeExponent);
|
|
168572
|
+
scattering = pow(clamp(scattering, vec3(0.0), vec3(1.0)), vec3(encodeExponent));
|
|
168573
|
+
}
|
|
168574
|
+
gl_FragColor = vec4(scattering, 1.0 - throughput);
|
|
168575
|
+
}
|
|
168576
|
+
`;
|
|
168577
|
+
}
|
|
168578
|
+
static GlslTemporal() {
|
|
168579
|
+
const shaders = VolumetricCloudsShaders;
|
|
168580
|
+
return "precision highp float;\nvarying vec2 vUV;\nuniform sampler2D cloudCurrent;\nuniform sampler2D cloudHistory;\nuniform sampler2D sceneDepth;\n"
|
|
168581
|
+
+ shaders.TemporalUniforms.map((name) => (name.indexOf("ViewProj") >= 0) ? "uniform mat4 " + name + ";" : "uniform vec4 " + name + ";").join("\n") + "\n"
|
|
168582
|
+
+ shaders.GlslGeometry() + `
|
|
168583
|
+
float cloudReprojectionDistance(vec3 direction) {
|
|
168584
|
+
float distanceToCloud = 0.0;
|
|
168585
|
+
float exitDistance = 0.0;
|
|
168586
|
+
#ifdef CLOUD_LOCAL
|
|
168587
|
+
if (!cloudIntersectVolume(cloudEye.xyz - cloudPlanet.xyz, direction, distanceToCloud, exitDistance)) distanceToCloud = exitDistance;
|
|
168588
|
+
#else
|
|
168589
|
+
cloudRange(vec3(0.0), direction, distanceToCloud, exitDistance);
|
|
168590
|
+
#endif
|
|
168591
|
+
distanceToCloud = max(distanceToCloud, 1.0);
|
|
168592
|
+
if (cloudCamera.y > 0.5) {
|
|
168593
|
+
float viewDepth = textureLod(sceneDepth, vUV, 0.0).r;
|
|
168594
|
+
if (viewDepth < cloudCamera.x * 0.999) distanceToCloud = min(distanceToCloud, viewDepth / max(dot(direction, cloudForward.xyz), 0.0001));
|
|
168595
|
+
}
|
|
168596
|
+
return distanceToCloud;
|
|
168597
|
+
}
|
|
168598
|
+
void main(void) {
|
|
168599
|
+
vec4 current = textureLod(cloudCurrent, vUV, 0.0);
|
|
168600
|
+
gl_FragColor = current;
|
|
168601
|
+
if (cloudCamera.z < 0.5 || current.a >= 1.0) return;
|
|
168602
|
+
vec3 boxMin = current.rgb;
|
|
168603
|
+
vec3 boxMax = current.rgb;
|
|
168604
|
+
vec3 neighbour = textureLod(cloudCurrent, vUV + vec2(0.0, -cloudTexel.y), 0.0).rgb;
|
|
168605
|
+
boxMin = min(boxMin, neighbour);
|
|
168606
|
+
boxMax = max(boxMax, neighbour);
|
|
168607
|
+
neighbour = textureLod(cloudCurrent, vUV + vec2(-cloudTexel.x, 0.0), 0.0).rgb;
|
|
168608
|
+
boxMin = min(boxMin, neighbour);
|
|
168609
|
+
boxMax = max(boxMax, neighbour);
|
|
168610
|
+
neighbour = textureLod(cloudCurrent, vUV + vec2(cloudTexel.x, 0.0), 0.0).rgb;
|
|
168611
|
+
boxMin = min(boxMin, neighbour);
|
|
168612
|
+
boxMax = max(boxMax, neighbour);
|
|
168613
|
+
neighbour = textureLod(cloudCurrent, vUV + vec2(0.0, cloudTexel.y), 0.0).rgb;
|
|
168614
|
+
boxMin = min(boxMin, neighbour);
|
|
168615
|
+
boxMax = max(boxMax, neighbour);
|
|
168616
|
+
vec4 farPoint = cloudInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
|
|
168617
|
+
vec3 direction = normalize(farPoint.xyz / farPoint.w - cloudEye.xyz);
|
|
168618
|
+
vec3 worldPosition = cloudEye.xyz + direction * cloudReprojectionDistance(direction);
|
|
168619
|
+
vec4 previousClip = cloudPrevViewProj * vec4(worldPosition, 1.0);
|
|
168620
|
+
vec2 previousUV = previousClip.xy / previousClip.w * 0.5 + 0.5;
|
|
168621
|
+
vec2 velocity = previousUV - vUV;
|
|
168622
|
+
if (previousUV.x > 1.0 || previousUV.x < 0.0 || previousUV.y > 1.0 || previousUV.y < 0.0) return;
|
|
168623
|
+
vec3 previous = clamp(textureLod(cloudHistory, previousUV, 0.0).rgb, boxMin, boxMax);
|
|
168624
|
+
float accumulation = cloudCamera.w;
|
|
168625
|
+
float intensity = clamp(min(accumulation - abs(velocity.x) * accumulation, accumulation - abs(velocity.y) * accumulation), 0.0, 1.0);
|
|
168626
|
+
gl_FragColor = vec4(mix(current.rgb, previous, intensity), current.a);
|
|
168627
|
+
}
|
|
168628
|
+
`;
|
|
168629
|
+
}
|
|
168630
|
+
static WgslMarchUniforms() {
|
|
168631
|
+
return VolumetricCloudsShaders.MarchUniforms.map((name) => (name === "cloudInvViewProj") ? "uniform " + name + ": mat4x4f;" : "uniform " + name + ": vec4f;").join("\n") + "\n";
|
|
168632
|
+
}
|
|
168633
|
+
static WgslConstants() {
|
|
168634
|
+
return `const CLOUD_DENSITY_THRESHOLD: f32 = 0.001;
|
|
168635
|
+
const EMPTY_STEPS_BEFORE_LARGE_STEPS: i32 = 8;
|
|
168636
|
+
const FORWARD_ECCENTRICITY: f32 = 0.7;
|
|
168637
|
+
const BACKWARD_ECCENTRICITY: f32 = 0.7;
|
|
168638
|
+
const MIN_EROSION_DISTANCE: f32 = 3000.0;
|
|
168639
|
+
const MAX_EROSION_DISTANCE: f32 = 100000.0;
|
|
168640
|
+
const NOISE_NORMALIZATION: f32 = 100000.0;
|
|
168641
|
+
const MAX_SKYBOX_DISTANCE: f32 = 200000.0;
|
|
168642
|
+
const LIGHT_STEP_MAXIMAL_SIZE: f32 = 1000.0;
|
|
168643
|
+
const CLOUD_PI: f32 = 3.14159265359;
|
|
168644
|
+
const MAX_PRIMARY_STEPS: i32 = 1024;
|
|
168645
|
+
const MAX_LIGHT_STEPS: i32 = 16;
|
|
168646
|
+
`;
|
|
168647
|
+
}
|
|
168648
|
+
static WgslGeometry() {
|
|
168649
|
+
return `struct CloudRange {
|
|
168650
|
+
hit: bool,
|
|
168651
|
+
entryDistance: f32,
|
|
168652
|
+
exitDistance: f32,
|
|
168653
|
+
};
|
|
168654
|
+
fn cloudIntersectSphere(sphereRadius: f32, cosChi: f32, radialDistance: f32) -> vec2f {
|
|
168655
|
+
let ratio: f32 = sphereRadius / radialDistance;
|
|
168656
|
+
let d: f32 = ratio * ratio - clamp(1.0 - cosChi * cosChi, 0.0, 1.0);
|
|
168657
|
+
if (d < 0.0) { return vec2f(d); }
|
|
168658
|
+
return radialDistance * vec2f(-cosChi - sqrt(d), -cosChi + sqrt(d));
|
|
168659
|
+
}
|
|
168660
|
+
fn cloudHorizonCosine(radialDistance: f32) -> f32 {
|
|
168661
|
+
let sinHorizon: f32 = uniforms.cloudPlanet.w / radialDistance;
|
|
168662
|
+
return -sqrt(clamp(1.0 - sinHorizon * sinHorizon, 0.0, 1.0));
|
|
168663
|
+
}
|
|
168664
|
+
fn cloudIntersectVolume(originPS: vec3f, direction: vec3f) -> CloudRange {
|
|
168665
|
+
let radialDistance: f32 = length(originPS);
|
|
168666
|
+
let cosChi: f32 = dot(originPS, direction) / radialDistance;
|
|
168667
|
+
let inner: vec2f = cloudIntersectSphere(uniforms.cloudLayer.x, cosChi, radialDistance);
|
|
168668
|
+
let outer: vec2f = cloudIntersectSphere(uniforms.cloudLayer.y, cosChi, radialDistance);
|
|
168669
|
+
if (inner.x < 0.0 && inner.y >= 0.0) {
|
|
168670
|
+
return CloudRange(cosChi >= cloudHorizonCosine(radialDistance), inner.y, outer.y);
|
|
168671
|
+
}
|
|
168672
|
+
return CloudRange(outer.y >= 0.0, max(outer.x, 0.0), select(outer.y, inner.x, inner.x >= 0.0));
|
|
168673
|
+
}
|
|
168674
|
+
fn cloudRaySphere(startWS: vec3f, direction: vec3f, radius: f32) -> vec2f {
|
|
168675
|
+
let startPS: vec3f = startWS + vec3f(0.0, uniforms.cloudPlanet.w, 0.0);
|
|
168676
|
+
let a: f32 = dot(direction, direction);
|
|
168677
|
+
let b: f32 = 2.0 * dot(direction, startPS);
|
|
168678
|
+
let c: f32 = dot(startPS, startPS) - radius * radius;
|
|
168679
|
+
let d: f32 = b * b - 4.0 * a * c;
|
|
168680
|
+
var result: vec2f = vec2f(0.0);
|
|
168681
|
+
if (d >= 0.0) {
|
|
168682
|
+
let q: f32 = -0.5 * (b + select(-1.0, 1.0, b >= 0.0) * sqrt(d));
|
|
168683
|
+
result = vec2f(c / q, q / a);
|
|
168684
|
+
if (result.x < 0.0) { result.x = result.y; }
|
|
168685
|
+
}
|
|
168686
|
+
return result;
|
|
168687
|
+
}
|
|
168688
|
+
fn cloudRange(originWS: vec3f, direction: vec3f) -> CloudRange {
|
|
168689
|
+
#ifdef CLOUD_LOCAL
|
|
168690
|
+
return cloudIntersectVolume(originWS - uniforms.cloudPlanet.xyz, direction);
|
|
168691
|
+
#else
|
|
168692
|
+
let inner: vec2f = cloudRaySphere(originWS, direction, uniforms.cloudLayer.x);
|
|
168693
|
+
let outer: vec2f = cloudRaySphere(originWS, direction, uniforms.cloudLayer.y);
|
|
168694
|
+
return CloudRange(true, inner.x, outer.x);
|
|
168695
|
+
#endif
|
|
168696
|
+
}
|
|
168697
|
+
`;
|
|
168698
|
+
}
|
|
168699
|
+
static WgslDensity() {
|
|
168700
|
+
return `struct CloudProperties {
|
|
168701
|
+
density: f32,
|
|
168702
|
+
ambientOcclusion: f32,
|
|
168703
|
+
height: f32,
|
|
168704
|
+
sigmaT: f32,
|
|
168705
|
+
};
|
|
168706
|
+
fn cloudRemap(x: f32, a: f32, b: f32, c: f32, d: f32) -> f32 {
|
|
168707
|
+
return (((x - a) / (b - a)) * (d - c)) + c;
|
|
168708
|
+
}
|
|
168709
|
+
fn cloudNormalizedHeight(positionPS: vec3f) -> f32 {
|
|
168710
|
+
return clamp((length(positionPS) - uniforms.cloudLayer.x) / (uniforms.cloudLayer.y - uniforms.cloudLayer.x), 0.0, 1.0);
|
|
168711
|
+
}
|
|
168712
|
+
fn cloudAnimateShape(positionPS: vec3f) -> vec3f {
|
|
168713
|
+
var position: vec3f = positionPS;
|
|
168714
|
+
position.y = position.y + position.x / 3.0 + position.z / 7.0;
|
|
168715
|
+
return position + vec3f(uniforms.cloudWind.z, 0.0, uniforms.cloudWind.w) * uniforms.cloudWindSpeed.x + vec3f(0.0, uniforms.cloudWindSpeed.z, 0.0);
|
|
168716
|
+
}
|
|
168717
|
+
fn cloudAnimateErosion(positionPS: vec3f) -> vec3f {
|
|
168718
|
+
return positionPS + vec3f(uniforms.cloudWind.z, 0.0, uniforms.cloudWind.w) * uniforms.cloudWindSpeed.y + vec3f(0.0, uniforms.cloudWindSpeed.w, 0.0);
|
|
168719
|
+
}
|
|
168720
|
+
fn cloudCoverageHash(cell: vec2f) -> f32 {
|
|
168721
|
+
var p: vec3f = fract(vec3f(cell.x, cell.y, cell.x) * 0.1031);
|
|
168722
|
+
p = p + vec3f(dot(p, p.yzx + vec3f(33.33)));
|
|
168723
|
+
return fract((p.x + p.y) * p.z);
|
|
168724
|
+
}
|
|
168725
|
+
fn cloudCoverageNoise(position: vec2f) -> f32 {
|
|
168726
|
+
let cell: vec2f = floor(position);
|
|
168727
|
+
var blend: vec2f = fract(position);
|
|
168728
|
+
blend = blend * blend * (vec2f(3.0) - 2.0 * blend);
|
|
168729
|
+
let bottom: f32 = mix(cloudCoverageHash(cell), cloudCoverageHash(cell + vec2f(1.0, 0.0)), blend.x);
|
|
168730
|
+
let top: f32 = mix(cloudCoverageHash(cell + vec2f(0.0, 1.0)), cloudCoverageHash(cell + vec2f(1.0, 1.0)), blend.x);
|
|
168731
|
+
return mix(bottom, top, blend.y);
|
|
168732
|
+
}
|
|
168733
|
+
fn cloudCoverageAt(positionPS: vec3f) -> vec2f {
|
|
168734
|
+
var coverage: f32 = 0.9;
|
|
168735
|
+
var shapeFill: f32 = 0.0;
|
|
168736
|
+
if (uniforms.cloudCoverage.x != 0.0) {
|
|
168737
|
+
let coordinates: vec2f = (positionPS.xz + uniforms.cloudWind.zw * uniforms.cloudWindSpeed.x + uniforms.cloudCoverageOffset.xy) * uniforms.cloudCoverage.y;
|
|
168738
|
+
let coverageNoise: f32 = cloudCoverageNoise(coordinates) * 0.7 + cloudCoverageNoise(coordinates * 2.03 + vec2f(17.13, 43.71)) * 0.3;
|
|
168739
|
+
let threshold: f32 = mix(-0.15, 1.15, uniforms.cloudCoverage.z);
|
|
168740
|
+
let mask: f32 = smoothstep(threshold - 0.12, threshold + 0.12, coverageNoise);
|
|
168741
|
+
if (uniforms.cloudCoverage.x > 0.0) {
|
|
168742
|
+
coverage = coverage * mix(1.0, mask, uniforms.cloudCoverage.x);
|
|
168743
|
+
} else {
|
|
168744
|
+
shapeFill = clamp(-uniforms.cloudCoverage.x, 0.0, 1.0) * (1.0 - mask);
|
|
168745
|
+
coverage = mix(coverage, 1.0, shapeFill);
|
|
168746
|
+
}
|
|
168747
|
+
}
|
|
168748
|
+
return vec2f(coverage, shapeFill);
|
|
168749
|
+
}
|
|
168750
|
+
fn cloudPropertiesAt(samplePositionPS: vec3f, noiseMip: f32, erosionMip: f32, cheapVersion: bool, lightSampling: bool) -> CloudProperties {
|
|
168751
|
+
var properties: CloudProperties = CloudProperties(0.0, 0.0, 0.0, 0.0);
|
|
168752
|
+
var positionPS: vec3f = samplePositionPS;
|
|
168753
|
+
#ifndef CLOUD_LOCAL
|
|
168754
|
+
if (positionPS.y < uniforms.cloudPlanet.w) { return properties; }
|
|
168755
|
+
#endif
|
|
168756
|
+
properties.ambientOcclusion = 1.0;
|
|
168757
|
+
properties.height = cloudNormalizedHeight(positionPS);
|
|
168758
|
+
#ifndef CLOUD_LOCAL
|
|
168759
|
+
positionPS = vec3f(positionPS.x + uniforms.cloudEye.x, positionPS.y, positionPS.z + uniforms.cloudEye.z);
|
|
168760
|
+
#endif
|
|
168761
|
+
var baseCoordinates: vec3f = (cloudAnimateShape(positionPS).xzy / NOISE_NORMALIZATION) * uniforms.cloudShape.x - uniforms.cloudShapeOffset.xyz;
|
|
168762
|
+
baseCoordinates = baseCoordinates + properties.height * vec3f(uniforms.cloudWind.x, uniforms.cloudWind.y, 0.0) * uniforms.cloudShapeOffset.w;
|
|
168763
|
+
var lowFrequencyNoise: f32 = textureSampleLevel(worleyNoise, worleyNoiseSampler, baseCoordinates, noiseMip).r;
|
|
168764
|
+
let coverage: vec2f = cloudCoverageAt(positionPS);
|
|
168765
|
+
if (coverage.x <= CLOUD_DENSITY_THRESHOLD) { return properties; }
|
|
168766
|
+
let curves: vec3f = textureSampleLevel(cloudCurves, cloudCurvesSampler, vec2f(properties.height, 0.5), 0.0).xyz;
|
|
168767
|
+
let shapeFactor: f32 = mix(0.1, 1.0, uniforms.cloudShape.y * (1.0 - coverage.y)) * curves.y;
|
|
168768
|
+
let erosionFactor: f32 = uniforms.cloudShape.w * curves.y;
|
|
168769
|
+
let microDetailFactor: f32 = uniforms.cloudDetail.z * curves.y;
|
|
168770
|
+
lowFrequencyNoise = mix(1.0, lowFrequencyNoise, shapeFactor);
|
|
168771
|
+
var baseCloud: f32 = 1.0 - curves.x * coverage.x * (1.0 - shapeFactor);
|
|
168772
|
+
baseCloud = clamp(cloudRemap(lowFrequencyNoise, baseCloud, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x * coverage.x;
|
|
168773
|
+
properties.sigmaT = 0.04;
|
|
168774
|
+
let occlusionBlend: f32 = clamp(1.0 - max(erosionFactor, shapeFactor) * 0.5, 0.0, 1.0);
|
|
168775
|
+
properties.ambientOcclusion = mix(1.0, curves.z, occlusionBlend);
|
|
168776
|
+
if (!cheapVersion) {
|
|
168777
|
+
let erosionCoordinates: vec3f = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * uniforms.cloudShape.z;
|
|
168778
|
+
var erosionValue: f32 = 1.0 - textureSampleLevel(erosionNoise, erosionNoiseSampler, erosionCoordinates, erosionMip).r;
|
|
168779
|
+
erosionValue = mix(0.0, erosionValue, erosionFactor * 0.75 * coverage.x);
|
|
168780
|
+
properties.ambientOcclusion = clamp(properties.ambientOcclusion - sqrt(erosionValue * uniforms.cloudDetail.x), 0.0, 1.0);
|
|
168781
|
+
baseCloud = cloudRemap(baseCloud, erosionValue, 1.0, 0.0, 1.0);
|
|
168782
|
+
#ifdef CLOUD_MICRO_EROSION
|
|
168783
|
+
let fineCoordinates: vec3f = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * uniforms.cloudDetail.y;
|
|
168784
|
+
var fineValue: f32 = 1.0 - textureSampleLevel(erosionNoise, erosionNoiseSampler, fineCoordinates, erosionMip).r;
|
|
168785
|
+
fineValue = mix(0.0, fineValue, microDetailFactor * 0.5 * coverage.x);
|
|
168786
|
+
baseCloud = cloudRemap(baseCloud, fineValue, 1.0, 0.0, 1.0);
|
|
168787
|
+
#endif
|
|
168788
|
+
}
|
|
168789
|
+
if (lightSampling) {
|
|
168790
|
+
baseCloud = baseCloud - erosionFactor * 0.1;
|
|
168791
|
+
#ifdef CLOUD_MICRO_EROSION
|
|
168792
|
+
baseCloud = baseCloud - microDetailFactor * 0.15;
|
|
168793
|
+
#endif
|
|
168794
|
+
}
|
|
168795
|
+
properties.density = max(0.0, baseCloud) * uniforms.cloudDetail.w;
|
|
168796
|
+
return properties;
|
|
168797
|
+
}
|
|
168798
|
+
`;
|
|
168799
|
+
}
|
|
168800
|
+
static WgslLighting() {
|
|
168801
|
+
return `fn cloudPhase(anisotropy: f32, cosTheta: f32) -> f32 {
|
|
168802
|
+
let denominator: f32 = 1.0 + anisotropy * anisotropy - 2.0 * anisotropy * cosTheta;
|
|
168803
|
+
let falloff: f32 = inverseSqrt(max(denominator, 1e-6));
|
|
168804
|
+
return (1.0 / (4.0 * CLOUD_PI)) * (1.0 - anisotropy * anisotropy) * falloff * falloff * falloff;
|
|
168805
|
+
}
|
|
168806
|
+
fn cloudPowder(density: f32, cosAngle: f32, intensity: f32) -> f32 {
|
|
168807
|
+
let powder: f32 = clamp((1.0 - exp(-density * 4.0)) * 2.0, 0.0, 1.0);
|
|
168808
|
+
return mix(1.0, mix(1.0, powder, smoothstep(0.5, -0.5, cosAngle)), intensity);
|
|
168809
|
+
}
|
|
168810
|
+
fn cloudSunTransmittance(positionPS: vec3f, sunDirection: vec3f, phase: vec2f) -> vec3f {
|
|
168811
|
+
let radialDistance: f32 = length(positionPS);
|
|
168812
|
+
let cosChi: f32 = dot(positionPS, sunDirection) / radialDistance;
|
|
168813
|
+
var transmittance: vec3f = vec3f(0.0);
|
|
168814
|
+
if (cosChi < cloudHorizonCosine(radialDistance)) { return transmittance; }
|
|
168815
|
+
let lightSteps: f32 = uniforms.cloudSteps.y;
|
|
168816
|
+
let totalLightDistance: f32 = clamp(cloudIntersectSphere(uniforms.cloudLayer.y, cosChi, radialDistance).y, 0.0, lightSteps * LIGHT_STEP_MAXIMAL_SIZE) + 5.0;
|
|
168817
|
+
let intervalSize: f32 = totalLightDistance / lightSteps;
|
|
168818
|
+
var opticalDepth: f32 = 0.0;
|
|
168819
|
+
for (var j: i32 = 0; j < MAX_LIGHT_STEPS; j++) {
|
|
168820
|
+
if (f32(j) >= lightSteps) { break; }
|
|
168821
|
+
let distanceAlongLight: f32 = intervalSize * (0.25 + f32(j));
|
|
168822
|
+
let lightProperties: CloudProperties = cloudPropertiesAt(positionPS + sunDirection * distanceAlongLight, 3.0 * f32(j) / lightSteps, 0.0, true, true);
|
|
168823
|
+
opticalDepth = opticalDepth + lightProperties.density * lightProperties.sigmaT;
|
|
168824
|
+
}
|
|
168825
|
+
let extinction: vec3f = intervalSize * opticalDepth * uniforms.cloudTint.rgb;
|
|
168826
|
+
transmittance = transmittance + exp(-extinction) * phase.x;
|
|
168827
|
+
transmittance = transmittance + exp(-extinction * uniforms.cloudScattering.x) * (phase.y * uniforms.cloudScattering.x);
|
|
168828
|
+
return transmittance;
|
|
168829
|
+
}
|
|
168830
|
+
`;
|
|
168831
|
+
}
|
|
168832
|
+
static WgslAerial() {
|
|
168833
|
+
const skyShaders = HdrpPbSkyShaders;
|
|
168834
|
+
const fogPass = HdrpFogPass;
|
|
168835
|
+
if (skyShaders == null || fogPass == null)
|
|
168836
|
+
return "";
|
|
168837
|
+
return "#ifdef CLOUD_AERIAL\nvar apLutSampler: sampler;\nvar apLut: texture_2d_array<f32>;\nuniform cloudApCamera: vec4f;\n"
|
|
168838
|
+
+ skyShaders.WgslUniforms() + skyShaders.WgslCommon() + fogPass.AerialPerspectiveWgsl() + "#endif\n";
|
|
168839
|
+
}
|
|
168840
|
+
static WgslMarch() {
|
|
168841
|
+
const shaders = VolumetricCloudsShaders;
|
|
168842
|
+
return "varying vUV: vec2f;\nvar worleyNoiseSampler: sampler;\nvar worleyNoise: texture_3d<f32>;\nvar erosionNoiseSampler: sampler;\nvar erosionNoise: texture_3d<f32>;\nvar cloudCurvesSampler: sampler;\nvar cloudCurves: texture_2d<f32>;\nvar sceneDepthSampler: sampler;\nvar sceneDepth: texture_2d<f32>;\n"
|
|
168843
|
+
+ shaders.WgslMarchUniforms() + shaders.WgslConstants() + shaders.WgslGeometry() + shaders.WgslDensity() + shaders.WgslLighting() + shaders.WgslAerial() + `
|
|
168844
|
+
fn cloudJenkins(value: u32) -> u32 {
|
|
168845
|
+
var x: u32 = value;
|
|
168846
|
+
x = x + (x << 10u);
|
|
168847
|
+
x = x ^ (x >> 6u);
|
|
168848
|
+
x = x + (x << 3u);
|
|
168849
|
+
x = x ^ (x >> 11u);
|
|
168850
|
+
x = x + (x << 15u);
|
|
168851
|
+
return x;
|
|
168852
|
+
}
|
|
168853
|
+
fn cloudRandom(pixel: vec2f, seed: f32) -> f32 {
|
|
168854
|
+
let v: vec3u = vec3u(u32(pixel.x), u32(pixel.y), u32(seed));
|
|
168855
|
+
let hash: u32 = cloudJenkins(v.x ^ cloudJenkins(v.y ^ cloudJenkins(v.z)));
|
|
168856
|
+
return bitcast<f32>((hash & 0x007FFFFFu) | 0x3F800000u) - 1.0;
|
|
168857
|
+
}
|
|
168858
|
+
@fragment
|
|
168859
|
+
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
168860
|
+
fragmentOutputs.color = vec4f(0.0, 0.0, 0.0, 1.0);
|
|
168861
|
+
let uv: vec2f = fragmentInputs.vUV;
|
|
168862
|
+
let farPoint: vec4f = uniforms.cloudInvViewProj * vec4f(uv * 2.0 - vec2f(1.0), 1.0, 1.0);
|
|
168863
|
+
let direction: vec3f = normalize(farPoint.xyz / farPoint.w - uniforms.cloudEye.xyz);
|
|
168864
|
+
var maxRayLength: f32 = MAX_SKYBOX_DISTANCE;
|
|
168865
|
+
let viewDepth: f32 = textureSampleLevel(sceneDepth, sceneDepthSampler, uv, 0.0).r;
|
|
168866
|
+
if (uniforms.cloudCamera.y > 0.5 && viewDepth < uniforms.cloudCamera.x * 0.999) {
|
|
168867
|
+
#ifdef CLOUD_LOCAL
|
|
168868
|
+
maxRayLength = viewDepth / max(dot(direction, uniforms.cloudForward.xyz), 0.0001);
|
|
168869
|
+
#else
|
|
168870
|
+
return fragmentOutputs;
|
|
168871
|
+
#endif
|
|
168872
|
+
}
|
|
168873
|
+
#ifdef CLOUD_LOCAL
|
|
168874
|
+
let originWS: vec3f = uniforms.cloudEye.xyz;
|
|
168875
|
+
#else
|
|
168876
|
+
let originWS: vec3f = vec3f(0.0);
|
|
168877
|
+
#endif
|
|
168878
|
+
let marchRange: CloudRange = cloudRange(originWS, direction);
|
|
168879
|
+
if (!marchRange.hit || maxRayLength < marchRange.entryDistance) { return fragmentOutputs; }
|
|
168880
|
+
let rangeStart: f32 = marchRange.entryDistance;
|
|
168881
|
+
let integrationNoise: f32 = cloudRandom(fragmentInputs.position.xy, uniforms.cloudCamera.z);
|
|
168882
|
+
let primarySteps: f32 = uniforms.cloudLayer.w;
|
|
168883
|
+
var totalDistance: f32 = min(marchRange.exitDistance, maxRayLength) - rangeStart;
|
|
168884
|
+
var stepSize: f32 = min(totalDistance / primarySteps, uniforms.cloudLayer.z);
|
|
168885
|
+
totalDistance = stepSize * primarySteps;
|
|
168886
|
+
let layerThickness: f32 = max(1.0, uniforms.cloudLayer.y - uniforms.cloudLayer.x);
|
|
168887
|
+
let sampleMultiplier: f32 = min(max(1.0, totalDistance / layerThickness), max(1.0, uniforms.cloudSteps.x));
|
|
168888
|
+
let primaryStepCount: i32 = min(i32(ceil(primarySteps * sampleMultiplier)), MAX_PRIMARY_STEPS);
|
|
168889
|
+
stepSize = totalDistance / f32(max(1, primaryStepCount));
|
|
168890
|
+
let sunDirection: vec3f = uniforms.cloudSunDirection.xyz;
|
|
168891
|
+
let cosAngle: f32 = dot(direction, sunDirection);
|
|
168892
|
+
let phase: vec2f = vec2f(
|
|
168893
|
+
cloudPhase(FORWARD_ECCENTRICITY, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY, cosAngle),
|
|
168894
|
+
cloudPhase(FORWARD_ECCENTRICITY * uniforms.cloudScattering.x, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY * uniforms.cloudScattering.x, cosAngle));
|
|
168895
|
+
var scattering: vec3f = vec3f(0.0);
|
|
168896
|
+
var ambient: f32 = 0.0;
|
|
168897
|
+
var transmittance: f32 = 1.0;
|
|
168898
|
+
var meanDistance: f32 = 0.0;
|
|
168899
|
+
var meanDistanceDivider: f32 = 0.0;
|
|
168900
|
+
var currentDistance: f32 = integrationNoise;
|
|
168901
|
+
var currentPositionWS: vec3f = originWS + (rangeStart + currentDistance) * direction;
|
|
168902
|
+
var activeSampling: bool = true;
|
|
168903
|
+
var sequentialEmptySamples: i32 = 0;
|
|
168904
|
+
var currentIndex: i32 = 0;
|
|
168905
|
+
for (var iteration: i32 = 0; iteration < MAX_PRIMARY_STEPS * 2; iteration++) {
|
|
168906
|
+
if (currentIndex >= primaryStepCount || currentDistance >= totalDistance) { break; }
|
|
168907
|
+
let cameraDistance: f32 = rangeStart + currentDistance;
|
|
168908
|
+
let densityFade: f32 = clamp((cameraDistance - uniforms.cloudSteps.z) / (uniforms.cloudSteps.z + uniforms.cloudSteps.w), 0.0, 1.0);
|
|
168909
|
+
let erosionMip: f32 = mix(0.0, 4.0, clamp((cameraDistance - MIN_EROSION_DISTANCE) / (MAX_EROSION_DISTANCE - MIN_EROSION_DISTANCE), 0.0, 1.0));
|
|
168910
|
+
let currentPositionPS: vec3f = currentPositionWS - uniforms.cloudPlanet.xyz;
|
|
168911
|
+
if (activeSampling) {
|
|
168912
|
+
var properties: CloudProperties = cloudPropertiesAt(currentPositionPS, 0.0, erosionMip, false, false);
|
|
168913
|
+
properties.density = properties.density * densityFade;
|
|
168914
|
+
if (properties.density > CLOUD_DENSITY_THRESHOLD) {
|
|
168915
|
+
let transmittanceDensity: f32 = transmittance * properties.density;
|
|
168916
|
+
meanDistance = meanDistance + cameraDistance * transmittanceDensity;
|
|
168917
|
+
meanDistanceDivider = meanDistanceDivider + transmittanceDensity;
|
|
168918
|
+
let stepTransmittance: f32 = exp(-properties.density * properties.sigmaT * stepSize);
|
|
168919
|
+
let powder: f32 = cloudPowder(properties.density, cosAngle, uniforms.cloudScattering.y);
|
|
168920
|
+
let sunLuminance: vec3f = cloudSunTransmittance(currentPositionPS, sunDirection, phase) * powder;
|
|
168921
|
+
let absorbed: f32 = transmittance - transmittance * stepTransmittance;
|
|
168922
|
+
scattering = scattering + sunLuminance * absorbed;
|
|
168923
|
+
ambient = ambient + properties.ambientOcclusion * absorbed;
|
|
168924
|
+
transmittance = transmittance * stepTransmittance;
|
|
168925
|
+
if (transmittance < 0.003) {
|
|
168926
|
+
transmittance = 0.0;
|
|
168927
|
+
break;
|
|
168928
|
+
}
|
|
168929
|
+
sequentialEmptySamples = 0;
|
|
168930
|
+
} else {
|
|
168931
|
+
sequentialEmptySamples = sequentialEmptySamples + 1;
|
|
168932
|
+
}
|
|
168933
|
+
if (sequentialEmptySamples == EMPTY_STEPS_BEFORE_LARGE_STEPS) { activeSampling = false; }
|
|
168934
|
+
let relativeStepSize: f32 = mix(integrationNoise, 1.0, clamp(f32(currentIndex), 0.0, 1.0));
|
|
168935
|
+
currentPositionWS = currentPositionWS + direction * stepSize * relativeStepSize;
|
|
168936
|
+
currentDistance = currentDistance + stepSize * relativeStepSize;
|
|
168937
|
+
} else {
|
|
168938
|
+
var properties: CloudProperties = cloudPropertiesAt(currentPositionPS, 1.0, 0.0, true, false);
|
|
168939
|
+
properties.density = properties.density * densityFade;
|
|
168940
|
+
if (properties.density < CLOUD_DENSITY_THRESHOLD) {
|
|
168941
|
+
currentPositionWS = currentPositionWS + direction * stepSize * 2.0;
|
|
168942
|
+
currentDistance = currentDistance + stepSize * 2.0;
|
|
168943
|
+
} else {
|
|
168944
|
+
currentPositionWS = currentPositionWS - direction * stepSize;
|
|
168945
|
+
currentDistance = currentDistance - stepSize;
|
|
168946
|
+
currentIndex = currentIndex - 1;
|
|
168947
|
+
activeSampling = true;
|
|
168948
|
+
sequentialEmptySamples = 0;
|
|
168949
|
+
}
|
|
168950
|
+
}
|
|
168951
|
+
currentIndex = currentIndex + 1;
|
|
168952
|
+
}
|
|
168953
|
+
if (meanDistance == 0.0) { return fragmentOutputs; }
|
|
168954
|
+
meanDistance = min(meanDistance / meanDistanceDivider, maxRayLength);
|
|
168955
|
+
let relativeHeight: f32 = cloudNormalizedHeight(originWS - uniforms.cloudPlanet.xyz + meanDistance * direction);
|
|
168956
|
+
let ambientColor: vec3f = max(vec3f(0.0), mix(uniforms.cloudAmbientBottom.rgb, uniforms.cloudAmbientTop.rgb, relativeHeight));
|
|
168957
|
+
var color: vec3f = (uniforms.cloudSunColor.rgb * scattering + ambientColor * ambient) * uniforms.cloudCamera.w;
|
|
168958
|
+
#ifdef CLOUD_AERIAL
|
|
168959
|
+
if (uniforms.cloudApCamera.w > 0.5) {
|
|
168960
|
+
let apDirection: vec3f = vec3f(direction.x * uniforms.cloudForward.w, direction.y, direction.z);
|
|
168961
|
+
let aerial: FogAerial = fogAerialPerspective(uv, uniforms.cloudApCamera.xyz, apDirection, meanDistance);
|
|
168962
|
+
color = aerial.color * (1.0 - transmittance) + (vec3f(1.0) - aerial.opacity) * color;
|
|
168963
|
+
}
|
|
168964
|
+
#endif
|
|
168965
|
+
fragmentOutputs.color = vec4f(color, transmittance);
|
|
168966
|
+
}
|
|
168967
|
+
`;
|
|
168968
|
+
}
|
|
168969
|
+
static WgslCompositeVertex() {
|
|
168970
|
+
return `attribute position: vec3f;
|
|
168971
|
+
varying vUV: vec2f;
|
|
168972
|
+
@vertex
|
|
168973
|
+
fn main(input: VertexInputs) -> FragmentInputs {
|
|
168974
|
+
vertexOutputs.vUV = vertexInputs.position.xy * 0.5 + vec2f(0.5);
|
|
168975
|
+
vertexOutputs.position = vec4f(vertexInputs.position.xy, 0.0, 1.0);
|
|
168976
|
+
}
|
|
168977
|
+
`;
|
|
168978
|
+
}
|
|
168979
|
+
static WgslCompositeFragment() {
|
|
168980
|
+
return `varying vUV: vec2f;
|
|
168981
|
+
var cloudLightingSampler: sampler;
|
|
168982
|
+
var cloudLighting: texture_2d<f32>;
|
|
168983
|
+
uniform cloudDisplayEncoded: f32;
|
|
168984
|
+
const CLOUD_DISPLAY_GAMMA: f32 = 2.2;
|
|
168985
|
+
const CLOUD_LUMINANCE: vec3f = vec3f(0.2126, 0.7152, 0.0722);
|
|
168986
|
+
@fragment
|
|
168987
|
+
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
168988
|
+
let cloud: vec4f = textureSample(cloudLighting, cloudLightingSampler, fragmentInputs.vUV);
|
|
168989
|
+
var scattering: vec3f = cloud.rgb;
|
|
168990
|
+
var throughput: f32 = cloud.a;
|
|
168991
|
+
if (uniforms.cloudDisplayEncoded > 0.5) {
|
|
168992
|
+
let encodeExponent: f32 = 1.0 / CLOUD_DISPLAY_GAMMA;
|
|
168993
|
+
let scatteringLuminance: f32 = clamp(dot(scattering, CLOUD_LUMINANCE), 0.0, 1.0);
|
|
168994
|
+
throughput = pow(min(cloud.a + scatteringLuminance, 1.0), encodeExponent) - pow(scatteringLuminance, encodeExponent);
|
|
168995
|
+
scattering = pow(clamp(scattering, vec3f(0.0), vec3f(1.0)), vec3f(encodeExponent));
|
|
168996
|
+
}
|
|
168997
|
+
fragmentOutputs.color = vec4f(scattering, 1.0 - throughput);
|
|
168998
|
+
}
|
|
168999
|
+
`;
|
|
169000
|
+
}
|
|
169001
|
+
static WgslTemporal() {
|
|
169002
|
+
const shaders = VolumetricCloudsShaders;
|
|
169003
|
+
return "varying vUV: vec2f;\nvar cloudCurrentSampler: sampler;\nvar cloudCurrent: texture_2d<f32>;\nvar cloudHistorySampler: sampler;\nvar cloudHistory: texture_2d<f32>;\nvar sceneDepthSampler: sampler;\nvar sceneDepth: texture_2d<f32>;\n"
|
|
169004
|
+
+ shaders.TemporalUniforms.map((name) => (name.indexOf("ViewProj") >= 0) ? "uniform " + name + ": mat4x4f;" : "uniform " + name + ": vec4f;").join("\n") + "\n"
|
|
169005
|
+
+ shaders.WgslGeometry() + `
|
|
169006
|
+
fn cloudReprojectionDistance(direction: vec3f, viewDepth: f32) -> f32 {
|
|
169007
|
+
#ifdef CLOUD_LOCAL
|
|
169008
|
+
let layerRange: CloudRange = cloudIntersectVolume(uniforms.cloudEye.xyz - uniforms.cloudPlanet.xyz, direction);
|
|
169009
|
+
var distanceToCloud: f32 = select(layerRange.exitDistance, layerRange.entryDistance, layerRange.hit);
|
|
169010
|
+
#else
|
|
169011
|
+
let layerRange: CloudRange = cloudRange(vec3f(0.0), direction);
|
|
169012
|
+
var distanceToCloud: f32 = layerRange.entryDistance;
|
|
169013
|
+
#endif
|
|
169014
|
+
distanceToCloud = max(distanceToCloud, 1.0);
|
|
169015
|
+
if (uniforms.cloudCamera.y > 0.5 && viewDepth < uniforms.cloudCamera.x * 0.999) {
|
|
169016
|
+
distanceToCloud = min(distanceToCloud, viewDepth / max(dot(direction, uniforms.cloudForward.xyz), 0.0001));
|
|
169017
|
+
}
|
|
169018
|
+
return distanceToCloud;
|
|
169019
|
+
}
|
|
169020
|
+
@fragment
|
|
169021
|
+
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
169022
|
+
let uv: vec2f = fragmentInputs.vUV;
|
|
169023
|
+
let current: vec4f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv, 0.0);
|
|
169024
|
+
let viewDepth: f32 = textureSampleLevel(sceneDepth, sceneDepthSampler, uv, 0.0).r;
|
|
169025
|
+
var north: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(0.0, -uniforms.cloudTexel.y), 0.0).rgb;
|
|
169026
|
+
var west: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(-uniforms.cloudTexel.x, 0.0), 0.0).rgb;
|
|
169027
|
+
var east: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(uniforms.cloudTexel.x, 0.0), 0.0).rgb;
|
|
169028
|
+
var south: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(0.0, uniforms.cloudTexel.y), 0.0).rgb;
|
|
169029
|
+
fragmentOutputs.color = current;
|
|
169030
|
+
if (uniforms.cloudCamera.z < 0.5 || current.a >= 1.0) { return fragmentOutputs; }
|
|
169031
|
+
let boxMin: vec3f = min(min(min(current.rgb, north), min(west, east)), south);
|
|
169032
|
+
let boxMax: vec3f = max(max(max(current.rgb, north), max(west, east)), south);
|
|
169033
|
+
let farPoint: vec4f = uniforms.cloudInvViewProj * vec4f(uv * 2.0 - vec2f(1.0), 1.0, 1.0);
|
|
169034
|
+
let direction: vec3f = normalize(farPoint.xyz / farPoint.w - uniforms.cloudEye.xyz);
|
|
169035
|
+
let worldPosition: vec3f = uniforms.cloudEye.xyz + direction * cloudReprojectionDistance(direction, viewDepth);
|
|
169036
|
+
let previousClip: vec4f = uniforms.cloudPrevViewProj * vec4f(worldPosition, 1.0);
|
|
169037
|
+
let previousUV: vec2f = previousClip.xy / previousClip.w * 0.5 + vec2f(0.5);
|
|
169038
|
+
let velocity: vec2f = previousUV - uv;
|
|
169039
|
+
let history: vec3f = textureSampleLevel(cloudHistory, cloudHistorySampler, clamp(previousUV, vec2f(0.0), vec2f(1.0)), 0.0).rgb;
|
|
169040
|
+
if (previousUV.x > 1.0 || previousUV.x < 0.0 || previousUV.y > 1.0 || previousUV.y < 0.0) { return fragmentOutputs; }
|
|
169041
|
+
let previous: vec3f = clamp(history, boxMin, boxMax);
|
|
169042
|
+
let accumulation: f32 = uniforms.cloudCamera.w;
|
|
169043
|
+
let intensity: f32 = clamp(min(accumulation - abs(velocity.x) * accumulation, accumulation - abs(velocity.y) * accumulation), 0.0, 1.0);
|
|
169044
|
+
fragmentOutputs.color = vec4f(mix(current.rgb, previous, intensity), current.a);
|
|
169045
|
+
}
|
|
169046
|
+
`;
|
|
169047
|
+
}
|
|
169048
|
+
}
|
|
169049
|
+
VolumetricCloudsShaders.MarchName = "tkCloudMarch";
|
|
169050
|
+
VolumetricCloudsShaders.CompositeName = "tkCloudComposite";
|
|
169051
|
+
VolumetricCloudsShaders.TemporalName = "tkCloudTemporal";
|
|
169052
|
+
VolumetricCloudsShaders.TemporalUniforms = ["cloudInvViewProj", "cloudPrevViewProj", "cloudEye", "cloudForward", "cloudCamera", "cloudPlanet", "cloudLayer", "cloudTexel"];
|
|
169053
|
+
VolumetricCloudsShaders.TemporalSamplers = ["cloudCurrent", "cloudHistory", "sceneDepth"];
|
|
169054
|
+
VolumetricCloudsShaders.MarchUniforms = [
|
|
169055
|
+
"cloudInvViewProj", "cloudEye", "cloudForward", "cloudCamera", "cloudPlanet", "cloudLayer", "cloudSteps", "cloudShape",
|
|
169056
|
+
"cloudDetail", "cloudShapeOffset", "cloudWind", "cloudWindSpeed", "cloudCoverage", "cloudCoverageOffset", "cloudScattering",
|
|
169057
|
+
"cloudTint", "cloudSunDirection", "cloudSunColor", "cloudAmbientTop", "cloudAmbientBottom"
|
|
169058
|
+
];
|
|
169059
|
+
VolumetricCloudsShaders.MarchSamplers = ["worleyNoise", "erosionNoise", "cloudCurves", "sceneDepth"];
|
|
169060
|
+
VolumetricCloudsShaders.AerialSampler = "apLut";
|
|
169061
|
+
VolumetricCloudsShaders.AerialCameraUniform = "cloudApCamera";
|
|
169062
|
+
VolumetricCloudsShaders.registered = false;
|
|
166557
169063
|
export class WebVideoPlayer extends ScriptComponent {
|
|
166558
169064
|
getVideoMaterial() { return this.m_videoMaterial; }
|
|
166559
169065
|
getVideoTexture() { return this.m_videoTexture; }
|
|
@@ -166908,6 +169414,7 @@ SceneManager.RegisterClass("TOOLKIT.HdrpWaterDecalSurface", HdrpWaterDecalSurfac
|
|
|
166908
169414
|
SceneManager.RegisterClass("TOOLKIT.LensFlareComponentSRP", LensFlareComponentSRP);
|
|
166909
169415
|
SceneManager.RegisterClass("TOOLKIT.LegacyLensFlare", LegacyLensFlare);
|
|
166910
169416
|
SceneManager.RegisterClass("TOOLKIT.LightProbeNetwork", LightProbeNetwork);
|
|
169417
|
+
SceneManager.RegisterClass("TOOLKIT.MeshWaterSurface", MeshWaterSurface);
|
|
166911
169418
|
SceneManager.RegisterClass("TOOLKIT.NavigationAgent", NavigationAgent);
|
|
166912
169419
|
Object.defineProperty(PlayerRef, "None", { value: new PlayerRef(0), writable: false });
|
|
166913
169420
|
Object.defineProperty(PlayerRef, "MasterClient", { value: new PlayerRef(-1), writable: false });
|
|
@@ -166939,11 +169446,14 @@ SceneManager.RegisterClass("TOOLKIT.ShurikenParticles", ShurikenParticles);
|
|
|
166939
169446
|
SceneManager.RegisterClass("TOOLKIT.SkinTextureArray", SkinTextureArray);
|
|
166940
169447
|
SceneManager.RegisterClass(SplineAnimate.ClassName, SplineAnimate);
|
|
166941
169448
|
SceneManager.RegisterClass(SplineContainer.ClassName, SplineContainer);
|
|
169449
|
+
const TERRAIN_EDGE_TOLERANCE_METERS = 0.01;
|
|
166942
169450
|
SceneManager.RegisterClass("TOOLKIT.TerrainBuilder", TerrainBuilder);
|
|
166943
169451
|
SceneManager.RegisterClass("TOOLKIT.TreeBranchMaterial", TreeBranchMaterial);
|
|
166944
169452
|
SceneManager.RegisterClass("TOOLKIT.GrassStandardMaterial", GrassStandardMaterial);
|
|
166945
169453
|
SceneManager.RegisterClass("TOOLKIT.GrassBillboardMaterial", GrassBillboardMaterial);
|
|
166946
169454
|
SceneManager.RegisterClass("TOOLKIT.TerrainGrassPlugin", TerrainGrassPlugin);
|
|
169455
|
+
const TREE_INITIAL_CAPACITY = 64;
|
|
169456
|
+
const TREE_GROWTH_FACTOR = 1.5;
|
|
166947
169457
|
SceneManager.RegisterClass("TOOLKIT.TextureAtlasSkin", TextureAtlasSkin);
|
|
166948
169458
|
SceneManager.RegisterClass(ThirdPersonController.ClassName, ThirdPersonController);
|
|
166949
169459
|
SceneManager.RegisterClass("BABYLON.GUI.UnitySlider", UnitySlider);
|
|
@@ -166953,4 +169463,36 @@ SceneManager.RegisterClass("TOOLKIT.UserInterface", UserInterface);
|
|
|
166953
169463
|
SceneManager.RegisterClass("TOOLKIT.VisualEffect", VisualEffect);
|
|
166954
169464
|
const VFX_EPSILON = 1e-5;
|
|
166955
169465
|
const VFX_FLT_MIN = 1.175494351e-38;
|
|
169466
|
+
const CLOUD_ALPHA_INDEX = -1000000;
|
|
169467
|
+
const CLOUD_TIER_WINDOW_FRAMES = 120;
|
|
169468
|
+
const CLOUD_TIER_BUDGET_MS = 33;
|
|
169469
|
+
const CLOUD_TIER_MINIMUM_SAVING = 0.1;
|
|
169470
|
+
const CLOUD_MAX_STEP_STRETCH = 4;
|
|
169471
|
+
const CLOUD_CAMERA_CUT_METERS = 50;
|
|
169472
|
+
const CLOUD_CURVE_SAMPLES = 64;
|
|
169473
|
+
const CLOUD_EARTH_RADIUS_METERS = 6378100;
|
|
169474
|
+
const CLOUD_MINIMUM_CURVATURE_SCALE = 0.025;
|
|
169475
|
+
const CLOUD_WIND_KMH_TO_MS = -0.277778;
|
|
169476
|
+
const CLOUD_WIND_WRAP_METERS = 100000;
|
|
169477
|
+
const CLOUD_MAX_STEP_DIVISOR = 8;
|
|
169478
|
+
const CLOUD_PHONE_WIDTH_CSS_PX = 800;
|
|
169479
|
+
const CLOUD_WEAK_GPU_TEXTURE_SIZE = 8192;
|
|
169480
|
+
const CLOUD_READY_POLL_MS = 16;
|
|
169481
|
+
const CLOUD_MINIMUM_ACCUMULATION = 0.01;
|
|
169482
|
+
const CLOUD_AUTO_FADE_START_FRACTION = 0.2;
|
|
169483
|
+
const CLOUD_AUTO_FADE_DISTANCE_FRACTION = 0.3;
|
|
169484
|
+
const CLOUD_FADE_MODE_MANUAL = 1;
|
|
169485
|
+
const CLOUD_DENSITY_SCALE = 2;
|
|
169486
|
+
const CLOUD_ALTITUDE_DISTORTION_SCALE = 0.25;
|
|
169487
|
+
const CLOUD_MULTI_SCATTERING_SCALE = 0.95;
|
|
169488
|
+
const CLOUD_SCATTERING_TINT_SCALE = 0.75;
|
|
169489
|
+
const CLOUD_MINIMUM_COVERAGE_PATTERN_METERS = 100;
|
|
169490
|
+
const CLOUD_DEFAULT_COVERAGE_PATTERN_METERS = 10000;
|
|
169491
|
+
const CLOUD_DEFAULT_COVERAGE_GAP = 0.45;
|
|
169492
|
+
const CLOUD_DEFAULT_SETTINGS = { resolutionScale: 0.5, primarySteps: 32, longRayMultiplier: 1, lightSteps: 2 };
|
|
169493
|
+
const CLOUD_TIER_CAPS = [
|
|
169494
|
+
{ resolutionScale: Infinity, primarySteps: 64, longRayMultiplier: 2, lightSteps: Infinity },
|
|
169495
|
+
{ resolutionScale: 0.35, primarySteps: 64, longRayMultiplier: 1, lightSteps: 2 },
|
|
169496
|
+
{ resolutionScale: 0.25, primarySteps: 64, longRayMultiplier: 1, lightSteps: 1 }
|
|
169497
|
+
];
|
|
166956
169498
|
SceneManager.RegisterClass("TOOLKIT.WebVideoPlayer", WebVideoPlayer);
|