@babylonjs-toolkit/next 9.34.10 → 9.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +80 -14
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +80 -14
- package/lib/leaderboardaroundpage.d.ts +5 -0
- package/lib/leaderboardaroundpage.d.ts.map +1 -0
- package/lib/leaderboardaroundpage.js +1 -0
- package/lib/leaderboardcallresult.d.ts +13 -0
- package/lib/leaderboardcallresult.d.ts.map +1 -0
- package/lib/leaderboardcallresult.js +1 -0
- package/lib/leaderboardclient.d.ts +95 -0
- package/lib/leaderboardclient.d.ts.map +1 -0
- package/lib/leaderboardclient.js +448 -0
- package/lib/leaderboardclientoptions.d.ts +12 -0
- package/lib/leaderboardclientoptions.d.ts.map +1 -0
- package/lib/leaderboardclientoptions.js +1 -0
- package/lib/leaderboardentriespage.d.ts +10 -0
- package/lib/leaderboardentriespage.d.ts.map +1 -0
- package/lib/leaderboardentriespage.js +1 -0
- package/lib/leaderboardentryview.d.ts +14 -0
- package/lib/leaderboardentryview.d.ts.map +1 -0
- package/lib/leaderboardentryview.js +1 -0
- package/lib/leaderboarderrorbody.d.ts +6 -0
- package/lib/leaderboarderrorbody.d.ts.map +1 -0
- package/lib/leaderboarderrorbody.js +1 -0
- package/lib/leaderboardfetch.d.ts +2 -0
- package/lib/leaderboardfetch.d.ts.map +1 -0
- package/lib/leaderboardfetch.js +1 -0
- package/lib/leaderboardkeeppolicy.d.ts +2 -0
- package/lib/leaderboardkeeppolicy.d.ts.map +1 -0
- package/lib/leaderboardkeeppolicy.js +1 -0
- package/lib/leaderboardmetadata.d.ts +5 -0
- package/lib/leaderboardmetadata.d.ts.map +1 -0
- package/lib/leaderboardmetadata.js +1 -0
- package/lib/leaderboardmetadatavalue.d.ts +4 -0
- package/lib/leaderboardmetadatavalue.d.ts.map +1 -0
- package/lib/leaderboardmetadatavalue.js +1 -0
- package/lib/leaderboardplayersession.d.ts +6 -0
- package/lib/leaderboardplayersession.d.ts.map +1 -0
- package/lib/leaderboardplayersession.js +1 -0
- package/lib/leaderboardplayerview.d.ts +7 -0
- package/lib/leaderboardplayerview.d.ts.map +1 -0
- package/lib/leaderboardplayerview.js +1 -0
- package/lib/leaderboardpostattempt.d.ts +6 -0
- package/lib/leaderboardpostattempt.d.ts.map +1 -0
- package/lib/leaderboardpostattempt.js +1 -0
- package/lib/leaderboardpublicboardview.d.ts +17 -0
- package/lib/leaderboardpublicboardview.d.ts.map +1 -0
- package/lib/leaderboardpublicboardview.js +1 -0
- package/lib/leaderboardrangequery.d.ts +7 -0
- package/lib/leaderboardrangequery.d.ts.map +1 -0
- package/lib/leaderboardrangequery.js +1 -0
- package/lib/leaderboardrefusalreason.d.ts +2 -0
- package/lib/leaderboardrefusalreason.d.ts.map +1 -0
- package/lib/leaderboardrefusalreason.js +1 -0
- package/lib/leaderboardscoreformat.d.ts +2 -0
- package/lib/leaderboardscoreformat.d.ts.map +1 -0
- package/lib/leaderboardscoreformat.js +1 -0
- package/lib/leaderboardscoreresult.d.ts +18 -0
- package/lib/leaderboardscoreresult.d.ts.map +1 -0
- package/lib/leaderboardscoreresult.js +1 -0
- package/lib/leaderboardscoresubmission.d.ts +8 -0
- package/lib/leaderboardscoresubmission.d.ts.map +1 -0
- package/lib/leaderboardscoresubmission.js +1 -0
- package/lib/leaderboardseasonview.d.ts +7 -0
- package/lib/leaderboardseasonview.d.ts.map +1 -0
- package/lib/leaderboardseasonview.js +1 -0
- package/lib/leaderboardsortorder.d.ts +2 -0
- package/lib/leaderboardsortorder.d.ts.map +1 -0
- package/lib/leaderboardsortorder.js +1 -0
- package/lib/leaderboardstorage.d.ts +6 -0
- package/lib/leaderboardstorage.d.ts.map +1 -0
- package/lib/leaderboardstorage.js +1 -0
- package/lib/leaderboardstoredplayer.d.ts +5 -0
- package/lib/leaderboardstoredplayer.d.ts.map +1 -0
- package/lib/leaderboardstoredplayer.js +1 -0
- package/lib/leaderboardsubmission.d.ts +8 -0
- package/lib/leaderboardsubmission.d.ts.map +1 -0
- package/lib/leaderboardsubmission.js +1 -0
- package/lib/leaderboardsubmissionrequest.d.ts +12 -0
- package/lib/leaderboardsubmissionrequest.d.ts.map +1 -0
- package/lib/leaderboardsubmissionrequest.js +1 -0
- package/lib/leaderboardsubmissionresponse.d.ts +7 -0
- package/lib/leaderboardsubmissionresponse.d.ts.map +1 -0
- package/lib/leaderboardsubmissionresponse.js +1 -0
- package/lib/leaderboardsubmitresult.d.ts +15 -0
- package/lib/leaderboardsubmitresult.d.ts.map +1 -0
- package/lib/leaderboardsubmitresult.js +1 -0
- package/lib/leaderboardtarget.d.ts +6 -0
- package/lib/leaderboardtarget.d.ts.map +1 -0
- package/lib/leaderboardtarget.js +1 -0
- package/lib/leaderboardtargetentry.d.ts +9 -0
- package/lib/leaderboardtargetentry.d.ts.map +1 -0
- package/lib/leaderboardtargetentry.js +1 -0
- package/lib/leaderboardtrustlevel.d.ts +2 -0
- package/lib/leaderboardtrustlevel.d.ts.map +1 -0
- package/lib/leaderboardtrustlevel.js +1 -0
- package/lib/leaderboardvariantdimension.d.ts +6 -0
- package/lib/leaderboardvariantdimension.d.ts.map +1 -0
- package/lib/leaderboardvariantdimension.js +1 -0
- package/lib/leaderboardwriteoutcome.d.ts +2 -0
- package/lib/leaderboardwriteoutcome.d.ts.map +1 -0
- package/lib/leaderboardwriteoutcome.js +1 -0
- package/lib/project.d.ts +16 -732
- package/lib/project.d.ts.map +1 -1
- package/lib/project.js +76 -3226
- package/lib/scenemanager.d.ts +424 -6
- package/lib/scenemanager.d.ts.map +1 -1
- package/lib/scenemanager.js +2633 -258
- package/package.json +17 -13
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.
|
|
146
|
+
static get Version() { return "9.35.0 - R1"; }
|
|
147
147
|
static get Copyright() { return "All rights reserved (c) 2024 Mackey Kinard"; }
|
|
148
148
|
static IsPrecompilingShaders() {
|
|
149
149
|
return SceneManager.PrecompileShaders !== false;
|
|
@@ -9198,7 +9198,6 @@ export class CVTOOLS_unity_metadata {
|
|
|
9198
9198
|
return;
|
|
9199
9199
|
const levels = [];
|
|
9200
9200
|
const rendererCounts = [];
|
|
9201
|
-
let multiRenderer = false;
|
|
9202
9201
|
let totalMeshes = 0;
|
|
9203
9202
|
for (let index = 0; index < levelCount; index++) {
|
|
9204
9203
|
const entry = lodentries[index];
|
|
@@ -9212,8 +9211,6 @@ export class CVTOOLS_unity_metadata {
|
|
|
9212
9211
|
meshes.push(lodnode);
|
|
9213
9212
|
}
|
|
9214
9213
|
});
|
|
9215
|
-
if (meshes.length > 1)
|
|
9216
|
-
multiRenderer = true;
|
|
9217
9214
|
totalMeshes += meshes.length;
|
|
9218
9215
|
rendererCounts.push(meshes.length);
|
|
9219
9216
|
levels.push({ distance: thresholds[index], meshes: meshes });
|
|
@@ -9221,14 +9218,15 @@ export class CVTOOLS_unity_metadata {
|
|
|
9221
9218
|
if (totalMeshes === 0)
|
|
9222
9219
|
return;
|
|
9223
9220
|
const fadeMode = UnityLodGroups.ParseFadeMode(metadata.fademode);
|
|
9224
|
-
const
|
|
9221
|
+
const requiresSwitcher = UnityLodGroups.RequiresSwitcher(levels.map((level) => level.meshes), fadeMode);
|
|
9222
|
+
const nativeCandidate = (useDistances === false && requiresSwitcher === false
|
|
9225
9223
|
&& levels[0].meshes.length === 1 && levels[0].meshes[0] instanceof Mesh && !(levels[0].meshes[0] instanceof InstancedMesh));
|
|
9226
9224
|
if (nativeCandidate === true && this._setupNativeCoverageLevelOfDetail(scene, transform, levels, lodBias, metadata) === true) {
|
|
9227
9225
|
}
|
|
9228
9226
|
else if (useDistances === false) {
|
|
9229
9227
|
this._setupLevelOfDetailSwitcher(scene, transform, levels, rendererCounts, UnityLodGroups.MODE_COVERAGE, lodBias, metadata);
|
|
9230
9228
|
}
|
|
9231
|
-
else if (
|
|
9229
|
+
else if (requiresSwitcher === true) {
|
|
9232
9230
|
this._setupLevelOfDetailSwitcher(scene, transform, levels, rendererCounts, UnityLodGroups.MODE_DISTANCE, lodBias, metadata);
|
|
9233
9231
|
}
|
|
9234
9232
|
else {
|
|
@@ -9735,10 +9733,14 @@ export class CVTOOLS_unity_metadata {
|
|
|
9735
9733
|
return { mesh: skyboxMesh, texture: skyboxTexture };
|
|
9736
9734
|
};
|
|
9737
9735
|
const pbSkyClass = HdrpPhysicallyBasedSky;
|
|
9738
|
-
const
|
|
9736
|
+
const physicalSky = PhysicalSkyBlock.SkyOf(metadata);
|
|
9737
|
+
const hdrpSkyType = (physicalSky != null && typeof physicalSky.type === "string") ? physicalSky.type.toLowerCase() : null;
|
|
9739
9738
|
const liveSky = (pbSkyClass != null && hdrpSkyType === "physicallybased")
|
|
9740
9739
|
? pbSkyClass.TryCreate(babylonScene, metadata, root, { rendergroup: rendergroup, bakedSky: hasBakedSkyFaces ? (() => buildBakedSky().mesh) : null })
|
|
9741
9740
|
: null;
|
|
9741
|
+
const volumetricClouds = VolumetricClouds;
|
|
9742
|
+
if (volumetricClouds != null)
|
|
9743
|
+
volumetricClouds.TryCreate(babylonScene, metadata, root);
|
|
9742
9744
|
const graphSky = (liveSky == null && hdrpSkyType !== "none" && typeof skybox.customMaterial === "string" && skybox.bags != null && ShaderGraphSky != null)
|
|
9743
9745
|
? ShaderGraphSky.CreateGraphSkybox(babylonScene, skybox.bags, { size: skysize, rendergroup: rendergroup, skyfog: skyfog, tags: skytags, environment: (skybox.environment == null && babylonScene.environmentTexture == null) })
|
|
9744
9746
|
: null;
|
|
@@ -9845,8 +9847,10 @@ export class CVTOOLS_unity_metadata {
|
|
|
9845
9847
|
const hasDiffuseIbl = Array.isArray(environment.sh) && environment.sh.length === 27;
|
|
9846
9848
|
const useUnitySH = (mode === 0 && hasDiffuseIbl === true);
|
|
9847
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));
|
|
9848
9852
|
if (useUnitySH)
|
|
9849
|
-
this.generateSphericalHarmonics(babylonScene, environment.sh,
|
|
9853
|
+
this.generateSphericalHarmonics(babylonScene, environment.sh, ambientScale, hasEnvironmentFile ? environmentLevel : null);
|
|
9850
9854
|
if (envinfo != null && envinfo !== "") {
|
|
9851
9855
|
try {
|
|
9852
9856
|
const root = (this._rootUrl != null) ? this._rootUrl : "/";
|
|
@@ -9954,6 +9958,8 @@ export class CVTOOLS_unity_metadata {
|
|
|
9954
9958
|
}
|
|
9955
9959
|
if (metadata.shaderglobals != null)
|
|
9956
9960
|
ShaderGlobals.LoadSceneGlobals(babylonScene, metadata.shaderglobals);
|
|
9961
|
+
ShaderGlobals.LoadSceneDrivers(babylonScene, metadata.shaderglobaldrivers);
|
|
9962
|
+
ShaderGraphRuntime.RecordExportReport(babylonScene, metadata.exportreport);
|
|
9957
9963
|
if (metadata.lights2d != null && Light2DTexture != null)
|
|
9958
9964
|
Light2DTexture.Load(babylonScene, metadata.lights2d);
|
|
9959
9965
|
if (metadata.renderfeatures != null && ShaderGraphPass != null)
|
|
@@ -15431,6 +15437,8 @@ export class LightingConversions {
|
|
|
15431
15437
|
static UnityLinearLightColor(light) {
|
|
15432
15438
|
if (light == null || light.diffuse == null)
|
|
15433
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();
|
|
15434
15442
|
const rider = (light.metadata != null && light.metadata.toolkit != null) ? light.metadata.toolkit.colorTemperature : null;
|
|
15435
15443
|
const d = light.diffuse;
|
|
15436
15444
|
const channel = (value, factor) => {
|
|
@@ -15443,6 +15451,18 @@ export class LightingConversions {
|
|
|
15443
15451
|
const tb = (rider != null) ? rider.b : 1.0;
|
|
15444
15452
|
return new Color3(channel(d.r, tr), channel(d.g, tg), channel(d.b, tb));
|
|
15445
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
|
+
}
|
|
15446
15466
|
static SetShadowFade(scene, fade) {
|
|
15447
15467
|
if (scene == null || !Array.isArray(fade) || fade.length < 2)
|
|
15448
15468
|
return;
|
|
@@ -15898,6 +15918,10 @@ export class LightingConversions {
|
|
|
15898
15918
|
return Math.max(0.0, reflection);
|
|
15899
15919
|
return Math.min(levelMax, Math.max(0.0, reflection));
|
|
15900
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
|
+
}
|
|
15901
15925
|
static WarnBakedSkyWithoutEnvironment(skybox) {
|
|
15902
15926
|
if (skybox == null || skybox.bakedsky !== true || skybox.environment != null)
|
|
15903
15927
|
return false;
|
|
@@ -17610,6 +17634,7 @@ export class MetadataParser {
|
|
|
17610
17634
|
babylonLight.metadata.toolkit.hdrpSky = component.hdrpsky;
|
|
17611
17635
|
babylonLight.metadata.toolkit.intensityFactor = intensityFactor;
|
|
17612
17636
|
babylonLight.metadata.toolkit.colorTemperature = (component.colortemperature != null) ? Utilities.ParseColor3(component.colortemperature, Color3.White()) : null;
|
|
17637
|
+
LightingConversions.LinearizeLightColor(babylonLight, scene);
|
|
17613
17638
|
if (component.cookie != null)
|
|
17614
17639
|
UnityLightCookies.Apply(scene, babylonLight, entity, component.cookie);
|
|
17615
17640
|
babylonLight.shadowEnabled = false;
|
|
@@ -18098,6 +18123,10 @@ export class OpaqueSceneColor {
|
|
|
18098
18123
|
}
|
|
18099
18124
|
return all;
|
|
18100
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
|
+
}
|
|
18101
18130
|
static QueueSamples(subMeshes, materials) {
|
|
18102
18131
|
if (subMeshes == null || materials == null || materials.size === 0)
|
|
18103
18132
|
return false;
|
|
@@ -18168,7 +18197,7 @@ export class OpaqueSceneColor {
|
|
|
18168
18197
|
if (!copier.isReady())
|
|
18169
18198
|
return;
|
|
18170
18199
|
engine.unBindFramebuffer(current, true);
|
|
18171
|
-
copier.copy(current.texture, destination);
|
|
18200
|
+
copier.copy(current.texture, destination, OpaqueSceneColor.CopyConversionFor(scene.imageProcessingConfiguration));
|
|
18172
18201
|
if (target.__tkMips === true && target.getInternalTexture() != null && typeof engine.generateMipmaps === "function")
|
|
18173
18202
|
engine.generateMipmaps(target.getInternalTexture());
|
|
18174
18203
|
engine.bindFramebuffer(current, undefined, undefined, undefined, true);
|
|
@@ -18179,6 +18208,27 @@ export class OpaqueSceneColor {
|
|
|
18179
18208
|
OpaqueSceneColor.Ratio = null;
|
|
18180
18209
|
OpaqueSceneColor.StoreKey = "__tkOpaqueSceneColor";
|
|
18181
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;
|
|
18182
18232
|
export class FixedStepClock {
|
|
18183
18233
|
constructor(fixedDeltaTime = FixedStepClock.DEFAULT_FIXED_DELTA_TIME, maximumDeltaTime = FixedStepClock.DEFAULT_MAXIMUM_DELTA_TIME) {
|
|
18184
18234
|
this.accumulatedTime = 0;
|
|
@@ -19447,7 +19497,9 @@ export class SceneShaderWarmup {
|
|
|
19447
19497
|
SceneShaderWarmup.SkipFrustumClipping(scene, frustumState);
|
|
19448
19498
|
const drawHidden = drawWholeScene && ownPipelines && options.includeHiddenMeshes === true && SceneShaderWarmup.DrawHiddenMeshesOnReveal === true;
|
|
19449
19499
|
const shownHiddenMeshes = drawHidden ? SceneShaderWarmup.ShowHiddenMeshesForDraws(scene) : [];
|
|
19450
|
-
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);
|
|
19451
19503
|
const shadowListWraps = SceneShaderWarmup.KeepHiddenCastersInShadowMaps(scene, shownHiddenMeshes);
|
|
19452
19504
|
const scaleVariantDraw = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.AlternateScaleVariants(scene) : null;
|
|
19453
19505
|
const materialVariantDraw = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.AlternateMaterialVariants(scene) : null;
|
|
@@ -19458,10 +19510,11 @@ export class SceneShaderWarmup {
|
|
|
19458
19510
|
const stageFraction = (requested > startRequested) ? (compiled - startCompiled) / (requested - startRequested) : 0;
|
|
19459
19511
|
const pipelineStep = { step: "pipelines", ready: compiled - startCompiled, total: requested - startRequested };
|
|
19460
19512
|
report(effects.ready + compiled - startCompiled, effects.total + requested - startRequested + 1, effectShare + (1 - effectShare) * stageFraction * 0.99, pipelineStep);
|
|
19461
|
-
}).then((timedOut) => {
|
|
19513
|
+
}, settleFrames).then((timedOut) => {
|
|
19462
19514
|
SceneShaderWarmup.RestoreFrustumClipping(scene, frustumState);
|
|
19463
19515
|
SceneShaderWarmup.RestoreShadowCasterLists(shadowListWraps);
|
|
19464
|
-
SceneShaderWarmup.
|
|
19516
|
+
SceneShaderWarmup.RestoreFullShadowMaps(fullShadowDraws);
|
|
19517
|
+
SceneShaderWarmup.RestoreNativeLodLevels(scene, lodCycle);
|
|
19465
19518
|
SceneShaderWarmup.RestoreHiddenMeshes(scene, shownHiddenMeshes);
|
|
19466
19519
|
SceneShaderWarmup.RestoreScaleVariants(scene, scaleVariantDraw);
|
|
19467
19520
|
SceneShaderWarmup.RestoreMaterialVariants(scene, materialVariantDraw);
|
|
@@ -21181,13 +21234,15 @@ export class SceneShaderWarmup {
|
|
|
21181
21234
|
wrap.shadowMap.getCustomRenderList = wrap.customList;
|
|
21182
21235
|
}
|
|
21183
21236
|
}
|
|
21184
|
-
static
|
|
21237
|
+
static DrawShadowMapsFully(scene) {
|
|
21185
21238
|
const draws = [];
|
|
21186
21239
|
for (const shadowMap of SceneShaderWarmup.CollectShadowMaps(scene)) {
|
|
21187
21240
|
const renderGate = shadowMap;
|
|
21188
|
-
|
|
21241
|
+
const isGated = renderGate._tkSkipInvisible === true;
|
|
21242
|
+
if (!isGated && !SceneShaderWarmup.HasCasterCullingList(shadowMap))
|
|
21189
21243
|
continue;
|
|
21190
|
-
|
|
21244
|
+
if (isGated)
|
|
21245
|
+
renderGate._tkPrewarmDrawAll = true;
|
|
21191
21246
|
draws.push({ shadowMap: shadowMap, customList: shadowMap.getCustomRenderList });
|
|
21192
21247
|
shadowMap.getCustomRenderList = null;
|
|
21193
21248
|
try {
|
|
@@ -21197,7 +21252,11 @@ export class SceneShaderWarmup {
|
|
|
21197
21252
|
}
|
|
21198
21253
|
return draws;
|
|
21199
21254
|
}
|
|
21200
|
-
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) {
|
|
21201
21260
|
for (const draw of draws) {
|
|
21202
21261
|
draw.shadowMap._tkPrewarmDrawAll = false;
|
|
21203
21262
|
if (draw.shadowMap.getCustomRenderList == null)
|
|
@@ -21429,6 +21488,53 @@ export class SceneShaderWarmup {
|
|
|
21429
21488
|
draw.frameObserver = null;
|
|
21430
21489
|
SceneShaderWarmup.SetScaleVariantForced(draw.entries, false);
|
|
21431
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
|
+
}
|
|
21432
21538
|
static SetScaleVariantForced(entries, forced) {
|
|
21433
21539
|
const prototypeUpdate = AbstractMesh.prototype._updateNonUniformScalingState;
|
|
21434
21540
|
for (const entry of entries) {
|
|
@@ -21572,8 +21678,8 @@ export class SceneShaderWarmup {
|
|
|
21572
21678
|
}
|
|
21573
21679
|
return phases;
|
|
21574
21680
|
}
|
|
21575
|
-
static WaitForPipelinesPerCamera(scene, pipelines, deadline, phases, onProgress) {
|
|
21576
|
-
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);
|
|
21577
21683
|
}
|
|
21578
21684
|
static RunCameraPhasesAsync(scene, phases, noPhaseRun, runStep, isTimedOut) {
|
|
21579
21685
|
const runPhase = (phaseIndex, previous) => {
|
|
@@ -21591,7 +21697,7 @@ export class SceneShaderWarmup {
|
|
|
21591
21697
|
};
|
|
21592
21698
|
return runPhase(0, noPhaseRun);
|
|
21593
21699
|
}
|
|
21594
|
-
static WaitForPipelines(scene, pipelines, deadline, onProgress) {
|
|
21700
|
+
static WaitForPipelines(scene, pipelines, deadline, onProgress, settleFrames = SceneShaderWarmup.PIPELINE_SETTLE_FRAMES) {
|
|
21595
21701
|
return new Promise((resolve) => {
|
|
21596
21702
|
let quietFrames = 0;
|
|
21597
21703
|
let lastRequested = -1;
|
|
@@ -21622,7 +21728,7 @@ export class SceneShaderWarmup {
|
|
|
21622
21728
|
quietFrames = quiet ? quietFrames + 1 : 0;
|
|
21623
21729
|
if (onProgress != null)
|
|
21624
21730
|
onProgress(pipelines.compiled, pipelines.requested);
|
|
21625
|
-
if (quietFrames >=
|
|
21731
|
+
if (quietFrames >= settleFrames)
|
|
21626
21732
|
finish(false);
|
|
21627
21733
|
});
|
|
21628
21734
|
timer = setTimeout(() => finish(true), Math.max(0, deadline - SceneShaderWarmup.NowMs()));
|
|
@@ -22179,6 +22285,7 @@ SceneShaderWarmup.BuildScenePipelinesOnReveal = true;
|
|
|
22179
22285
|
SceneShaderWarmup.DrawHiddenMeshesOnReveal = true;
|
|
22180
22286
|
SceneShaderWarmup.WarmEmptyThinInstancesOnReveal = true;
|
|
22181
22287
|
SceneShaderWarmup.WarmScaleVariantsOnReveal = true;
|
|
22288
|
+
SceneShaderWarmup.WarmNativeLodLevelsOnReveal = true;
|
|
22182
22289
|
SceneShaderWarmup.OPTIONAL_UNIT_GRACE_MS = 3000;
|
|
22183
22290
|
SceneShaderWarmup.GIVEN_UP_NAMES_SHOWN = 10;
|
|
22184
22291
|
SceneShaderWarmup.LateCompileReporting = null;
|
|
@@ -23815,6 +23922,27 @@ export class ShaderGlobals {
|
|
|
23815
23922
|
ShaderGlobals._keywords.add(k);
|
|
23816
23923
|
ShaderGlobals._version++;
|
|
23817
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
|
+
}
|
|
23818
23946
|
static GetClock(scene) {
|
|
23819
23947
|
if (scene == null)
|
|
23820
23948
|
return { time: 0, deltaTime: 0, smoothDeltaTime: 0, frame: 0 };
|
|
@@ -23849,6 +23977,7 @@ export class ShaderGlobals {
|
|
|
23849
23977
|
return clock;
|
|
23850
23978
|
}
|
|
23851
23979
|
}
|
|
23980
|
+
ShaderGlobals.DriverComponentNames = ["x", "y", "z", "w"];
|
|
23852
23981
|
ShaderGlobals._version = 0;
|
|
23853
23982
|
ShaderGlobals._floats = new Map();
|
|
23854
23983
|
ShaderGlobals._vectors = new Map();
|
|
@@ -25073,6 +25202,23 @@ export class ShaderGraphRuntime {
|
|
|
25073
25202
|
loadTexture(textures[k], (texture) => { clone.setTextureValue(k, texture); });
|
|
25074
25203
|
return clone;
|
|
25075
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
|
+
}
|
|
25076
25222
|
static FlushSceneSummary(scene) {
|
|
25077
25223
|
const record = ShaderGraphRuntime._records.get(scene != null ? scene.uniqueId : -1);
|
|
25078
25224
|
if (record == null || record.scene !== scene)
|
|
@@ -25092,6 +25238,8 @@ export class ShaderGraphRuntime {
|
|
|
25092
25238
|
const report = (ShaderGraphRuntime.ReportDeviations != null) ? ShaderGraphRuntime.ReportDeviations
|
|
25093
25239
|
: (SceneManager != null && typeof SceneManager.IsDebugMode === "function" ? SceneManager.IsDebugMode() : true);
|
|
25094
25240
|
const n = record.materials.size, m = record.lastResorts.size;
|
|
25241
|
+
if (report && record.exportReport != null)
|
|
25242
|
+
ShaderGraphRuntime.PrintExportReport(record);
|
|
25095
25243
|
if (report && (n > 0 || m > 0)) {
|
|
25096
25244
|
console.groupCollapsed("Shader Graph: " + n + " materials with deviations, " + m + " last resort" + (record.flushed ? " (built after the scene summary)" : ""));
|
|
25097
25245
|
record.materials.forEach((entry, name) => {
|
|
@@ -29818,8 +29966,9 @@ export class UnityLodGroups {
|
|
|
29818
29966
|
static ExportedSize(root, metadata) {
|
|
29819
29967
|
if (root == null || metadata == null || !(metadata.lodsize > 0))
|
|
29820
29968
|
return null;
|
|
29821
|
-
const
|
|
29822
|
-
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();
|
|
29823
29972
|
const scale = new Vector3();
|
|
29824
29973
|
root.computeWorldMatrix(true).decompose(scale, null, null);
|
|
29825
29974
|
const largest = Math.max(Math.abs(scale.x), Math.abs(scale.y), Math.abs(scale.z));
|
|
@@ -29830,6 +29979,35 @@ export class UnityLodGroups {
|
|
|
29830
29979
|
const useDistances = !hasCoverages && (distances != null && distances.length > 0);
|
|
29831
29980
|
return { useDistances: useDistances, thresholds: useDistances ? distances : (hasCoverages ? coverages : null) };
|
|
29832
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
|
+
}
|
|
29833
30011
|
static ParseFadeMode(mode) {
|
|
29834
30012
|
const m = (mode != null) ? String(mode).toLowerCase() : "";
|
|
29835
30013
|
if (m === "crossfade")
|
|
@@ -29992,21 +30170,25 @@ export class UnityLodGroups {
|
|
|
29992
30170
|
addNativeGroup(master, details, coverages, worldSize) {
|
|
29993
30171
|
if (!(master instanceof Mesh) || master instanceof InstancedMesh || coverages == null)
|
|
29994
30172
|
return false;
|
|
29995
|
-
|
|
29996
|
-
|
|
30173
|
+
if (master.instances.length > 0)
|
|
30174
|
+
return false;
|
|
30175
|
+
const levelCount = Math.min(coverages.length, details.length + 1);
|
|
30176
|
+
if (levelCount <= 0)
|
|
29997
30177
|
return false;
|
|
29998
|
-
for (let k = 0; k <
|
|
29999
|
-
const
|
|
30000
|
-
if (!(
|
|
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)
|
|
30001
30181
|
return false;
|
|
30002
30182
|
}
|
|
30003
|
-
|
|
30183
|
+
if (UnityLodGroups.LevelsShareTransform(master, details.slice(0, levelCount - 1)) === false)
|
|
30184
|
+
return false;
|
|
30185
|
+
for (let k = 1; k < levelCount; k++)
|
|
30004
30186
|
master.addLODLevel(1e6 + k, details[k - 1]);
|
|
30005
|
-
master.addLODLevel(1e6 +
|
|
30187
|
+
master.addLODLevel(1e6 + levelCount, null);
|
|
30006
30188
|
const all = master.getLODLevels();
|
|
30007
30189
|
const entries = [];
|
|
30008
|
-
for (let i = 0; i <
|
|
30009
|
-
const mesh = (i <
|
|
30190
|
+
for (let i = 0; i < levelCount; i++) {
|
|
30191
|
+
const mesh = (i < levelCount - 1) ? details[i] : null;
|
|
30010
30192
|
let found = null;
|
|
30011
30193
|
for (const level of all) {
|
|
30012
30194
|
if (level.mesh === mesh && entries.indexOf(level) === -1) {
|
|
@@ -30016,7 +30198,7 @@ export class UnityLodGroups {
|
|
|
30016
30198
|
}
|
|
30017
30199
|
entries.push(found);
|
|
30018
30200
|
}
|
|
30019
|
-
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 });
|
|
30020
30202
|
this._nativeKey[0] = NaN;
|
|
30021
30203
|
return true;
|
|
30022
30204
|
}
|
|
@@ -30266,6 +30448,9 @@ UnityLodGroups.FADE_NONE = 0;
|
|
|
30266
30448
|
UnityLodGroups.FADE_CROSSFADE = 1;
|
|
30267
30449
|
UnityLodGroups.FADE_SPEEDTREE = 2;
|
|
30268
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];
|
|
30269
30454
|
UnityLodGroups.DeferLoadFadeClones = true;
|
|
30270
30455
|
UnityLodGroups.FadeCloneSliceMs = 8;
|
|
30271
30456
|
UnityLodGroups._cloneQueues = new Map();
|
|
@@ -69897,16 +70082,36 @@ export class HdrpFogPass {
|
|
|
69897
70082
|
return HdrpFogPass.Uniforms.concat((S != null && Array.isArray(S.AtmosphereUniforms)) ? S.AtmosphereUniforms : [], ["scale"]);
|
|
69898
70083
|
}
|
|
69899
70084
|
static FogBlock(scene) {
|
|
69900
|
-
|
|
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);
|
|
69901
70091
|
return (block != null && block.fog != null && typeof block.fog === "object") ? block.fog : null;
|
|
69902
70092
|
}
|
|
69903
70093
|
static SkyOf(scene) {
|
|
69904
70094
|
const live = (HdrpPhysicallyBasedSky != null) ? HdrpPhysicallyBasedSky.Get(scene) : null;
|
|
69905
70095
|
if (live != null)
|
|
69906
70096
|
return live;
|
|
69907
|
-
|
|
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);
|
|
69908
70103
|
return (block != null) ? block.sky : null;
|
|
69909
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
|
+
}
|
|
69910
70115
|
static Frame(scene, fog) {
|
|
69911
70116
|
const s = scene;
|
|
69912
70117
|
const renderId = scene.getRenderId();
|
|
@@ -70204,6 +70409,113 @@ fn hdrpFogAmbient(dir: vec3f) -> vec3f {
|
|
|
70204
70409
|
+ uniforms.fogAmb4.rgb * (d.x * d.y) + uniforms.fogAmb5.rgb * (d.y * d.z) + uniforms.fogAmb6.rgb * (3.0 * d.z * d.z - 1.0)
|
|
70205
70410
|
+ uniforms.fogAmb7.rgb * (d.x * d.z) + uniforms.fogAmb8.rgb * (d.x * d.x - d.y * d.y);
|
|
70206
70411
|
}
|
|
70412
|
+
`;
|
|
70413
|
+
}
|
|
70414
|
+
static AerialPerspectiveGlsl() {
|
|
70415
|
+
return `
|
|
70416
|
+
vec3 fogRescaledChapman(vec2 z, vec2 Z, float c) {
|
|
70417
|
+
float sinTheta = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
|
|
70418
|
+
vec2 ch = vec2(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
|
|
70419
|
+
if (c < 0.0) {
|
|
70420
|
+
vec2 z0 = z * sinTheta;
|
|
70421
|
+
ch = vec2(2.0 * pbChapmanHorizontal(z0.x) * exp(Z.x - z0.x), 2.0 * pbChapmanHorizontal(z0.y) * exp(Z.y - z0.y)) - ch;
|
|
70422
|
+
}
|
|
70423
|
+
return vec3(ch, 0.0);
|
|
70424
|
+
}
|
|
70425
|
+
vec3 fogApOpticalDepth(vec3 O, vec3 V, float t) {
|
|
70426
|
+
float R = pbAtmo0.x;
|
|
70427
|
+
vec2 n = vec2(pbAtmo1.x, pbAtmo1.z);
|
|
70428
|
+
vec2 H = vec2(pbAtmo1.y, pbAtmo1.w);
|
|
70429
|
+
float r = length(O);
|
|
70430
|
+
vec3 N = O / r;
|
|
70431
|
+
float cosChi = dot(N, V);
|
|
70432
|
+
vec2 Z = R * n;
|
|
70433
|
+
vec3 P1 = O + t * V;
|
|
70434
|
+
float r1 = length(P1);
|
|
70435
|
+
float cosChi1 = dot(P1, V) / r1;
|
|
70436
|
+
float cosChi0 = (cosChi1 >= 0.0) ? cosChi : -cosChi;
|
|
70437
|
+
vec2 ch0 = fogRescaledChapman(r * n, Z, cosChi0).xy;
|
|
70438
|
+
vec2 z1 = r1 * n;
|
|
70439
|
+
vec2 ch1 = vec2(pbChapmanUpper(z1.x, abs(cosChi1)) * exp(Z.x - z1.x), pbChapmanUpper(z1.y, abs(cosChi1)) * exp(Z.y - z1.y));
|
|
70440
|
+
vec2 od = abs(ch0 - ch1) * H;
|
|
70441
|
+
float ozone = pbOzoneOpticalDepth(r, cosChi, t);
|
|
70442
|
+
return od.x * pbAirExt.xyz + od.y * pbAirExt.w + ozone * pbOzone0.xyz;
|
|
70443
|
+
}
|
|
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);
|
|
70461
|
+
}
|
|
70462
|
+
`;
|
|
70463
|
+
}
|
|
70464
|
+
static AerialPerspectiveWgsl() {
|
|
70465
|
+
return `
|
|
70466
|
+
fn fogRescaledChapman(z: vec2f, Z: vec2f, c: f32) -> vec2f {
|
|
70467
|
+
let sinTheta: f32 = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
|
|
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));
|
|
70469
|
+
if (c < 0.0) {
|
|
70470
|
+
let z0: vec2f = z * sinTheta;
|
|
70471
|
+
ch = vec2f(2.0 * pbChapmanHorizontal(z0.x) * exp(Z.x - z0.x), 2.0 * pbChapmanHorizontal(z0.y) * exp(Z.y - z0.y)) - ch;
|
|
70472
|
+
}
|
|
70473
|
+
return ch;
|
|
70474
|
+
}
|
|
70475
|
+
fn fogApOpticalDepth(O: vec3f, V: vec3f, t: f32) -> vec3f {
|
|
70476
|
+
let R: f32 = uniforms.pbAtmo0.x;
|
|
70477
|
+
let n: vec2f = vec2f(uniforms.pbAtmo1.x, uniforms.pbAtmo1.z);
|
|
70478
|
+
let H: vec2f = vec2f(uniforms.pbAtmo1.y, uniforms.pbAtmo1.w);
|
|
70479
|
+
let r: f32 = length(O);
|
|
70480
|
+
let N: vec3f = O / r;
|
|
70481
|
+
let cosChi: f32 = dot(N, V);
|
|
70482
|
+
let Z: vec2f = R * n;
|
|
70483
|
+
let P1: vec3f = O + t * V;
|
|
70484
|
+
let r1: f32 = length(P1);
|
|
70485
|
+
let cosChi1: f32 = dot(P1, V) / r1;
|
|
70486
|
+
let cosChi0: f32 = select(-cosChi, cosChi, cosChi1 >= 0.0);
|
|
70487
|
+
let ch0: vec2f = fogRescaledChapman(r * n, Z, cosChi0);
|
|
70488
|
+
let z1: vec2f = r1 * n;
|
|
70489
|
+
let ch1: vec2f = vec2f(pbChapmanUpper(z1.x, abs(cosChi1)) * exp(Z.x - z1.x), pbChapmanUpper(z1.y, abs(cosChi1)) * exp(Z.y - z1.y));
|
|
70490
|
+
let od: vec2f = abs(ch0 - ch1) * H;
|
|
70491
|
+
let ozone: f32 = pbOzoneOpticalDepth(r, cosChi, t);
|
|
70492
|
+
return od.x * uniforms.pbAirExt.xyz + od.y * uniforms.pbAirExt.w + ozone * uniforms.pbOzone0.xyz;
|
|
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
|
+
}
|
|
70207
70519
|
`;
|
|
70208
70520
|
}
|
|
70209
70521
|
static GlslHelpers(prefix) {
|
|
@@ -70251,6 +70563,7 @@ fn ${prefix}PhaseHG(g: f32, c: f32) -> f32 {
|
|
|
70251
70563
|
precision highp sampler2DArray;
|
|
70252
70564
|
#endif
|
|
70253
70565
|
precision highp float;
|
|
70566
|
+
#define FOG_DISPLAY_GAMMA 2.2
|
|
70254
70567
|
varying vec2 vUV;
|
|
70255
70568
|
uniform sampler2D textureSampler;
|
|
70256
70569
|
uniform sampler2D depthSampler;
|
|
@@ -70270,40 +70583,14 @@ ${HdrpFogPass.AmbientDeclarations(false)}uniform vec4 fogEye;
|
|
|
70270
70583
|
uniform vec4 fogForward;
|
|
70271
70584
|
uniform vec4 fogViewport;
|
|
70272
70585
|
uniform vec4 fogInput;
|
|
70586
|
+
uniform vec4 fogChain;
|
|
70273
70587
|
uniform mat4 fogInvViewProj;
|
|
70274
70588
|
uniform mat4 fogEnvMatrix;
|
|
70275
70589
|
#ifdef HDRP_FOG_AP
|
|
70276
70590
|
uniform sampler2DArray apLut;
|
|
70277
70591
|
uniform vec4 fogApCamera;
|
|
70278
70592
|
${ap}
|
|
70279
|
-
|
|
70280
|
-
float sinTheta = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
|
|
70281
|
-
vec2 ch = vec2(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
|
|
70282
|
-
if (c < 0.0) {
|
|
70283
|
-
vec2 z0 = z * sinTheta;
|
|
70284
|
-
ch = vec2(2.0 * pbChapmanHorizontal(z0.x) * exp(Z.x - z0.x), 2.0 * pbChapmanHorizontal(z0.y) * exp(Z.y - z0.y)) - ch;
|
|
70285
|
-
}
|
|
70286
|
-
return vec3(ch, 0.0);
|
|
70287
|
-
}
|
|
70288
|
-
vec3 fogApOpticalDepth(vec3 O, vec3 V, float t) {
|
|
70289
|
-
float R = pbAtmo0.x;
|
|
70290
|
-
vec2 n = vec2(pbAtmo1.x, pbAtmo1.z);
|
|
70291
|
-
vec2 H = vec2(pbAtmo1.y, pbAtmo1.w);
|
|
70292
|
-
float r = length(O);
|
|
70293
|
-
vec3 N = O / r;
|
|
70294
|
-
float cosChi = dot(N, V);
|
|
70295
|
-
vec2 Z = R * n;
|
|
70296
|
-
vec3 P1 = O + t * V;
|
|
70297
|
-
float r1 = length(P1);
|
|
70298
|
-
float cosChi1 = dot(P1, V) / r1;
|
|
70299
|
-
float cosChi0 = (cosChi1 >= 0.0) ? cosChi : -cosChi;
|
|
70300
|
-
vec2 ch0 = fogRescaledChapman(r * n, Z, cosChi0).xy;
|
|
70301
|
-
vec2 z1 = r1 * n;
|
|
70302
|
-
vec2 ch1 = vec2(pbChapmanUpper(z1.x, abs(cosChi1)) * exp(Z.x - z1.x), pbChapmanUpper(z1.y, abs(cosChi1)) * exp(Z.y - z1.y));
|
|
70303
|
-
vec2 od = abs(ch0 - ch1) * H;
|
|
70304
|
-
float ozone = pbOzoneOpticalDepth(r, cosChi, t);
|
|
70305
|
-
return od.x * pbAirExt.xyz + od.y * pbAirExt.w + ozone * pbOzone0.xyz;
|
|
70306
|
-
}
|
|
70593
|
+
${HdrpFogPass.AerialPerspectiveGlsl()}
|
|
70307
70594
|
#endif
|
|
70308
70595
|
${HdrpFogPass.AmbientGlsl()}${HdrpFogPass.GlslHelpers("hdrpFog")}${HdrpFogPass.LocalGlsl()}
|
|
70309
70596
|
vec3 hdrpFogColor(vec3 dir, float dist) {
|
|
@@ -70356,29 +70643,28 @@ void main(void) {
|
|
|
70356
70643
|
opacity = vec3(1.0 - trCamera);
|
|
70357
70644
|
}
|
|
70358
70645
|
#ifdef HDRP_FOG_AP
|
|
70359
|
-
|
|
70360
|
-
|
|
70361
|
-
|
|
70362
|
-
|
|
70363
|
-
|
|
70364
|
-
float off = 1.0 / 128.0;
|
|
70365
|
-
float s = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
|
|
70366
|
-
float layer = clamp(s * 64.0 - 0.5, 0.0, 63.0);
|
|
70367
|
-
float l0 = floor(layer);
|
|
70368
|
-
float l1 = min(l0 + 1.0, 63.0);
|
|
70369
|
-
vec3 apColor = mix(textureLod(apLut, vec3(vUV, l0), 0.0).rgb, textureLod(apLut, vec3(vUV, l1), 0.0).rgb, layer - l0);
|
|
70370
|
-
vec3 apOpacity = vec3(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
|
|
70371
|
-
apOpacity = pbDesaturate(apOpacity, pbZenith.w) * pbArt.x;
|
|
70372
|
-
float chi = acos(clamp(cosChi, -1.0, 1.0));
|
|
70373
|
-
float hor = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
|
|
70374
|
-
float nrm = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
|
|
70375
|
-
float te = exp(pbArt.y * nrm) * pbArt.z - pbArt.z;
|
|
70376
|
-
apColor *= mix(pbHorizon.xyz, pbZenith.xyz, te);
|
|
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);
|
|
70377
70651
|
color = color + (vec3(1.0) - opacity) * apColor;
|
|
70378
70652
|
opacity = opacity + (vec3(1.0) - opacity) * apOpacity;
|
|
70379
70653
|
}
|
|
70380
70654
|
#endif
|
|
70381
|
-
|
|
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);
|
|
70382
70668
|
}
|
|
70383
70669
|
`;
|
|
70384
70670
|
}
|
|
@@ -70386,6 +70672,7 @@ void main(void) {
|
|
|
70386
70672
|
const S = HdrpPbSkyShaders;
|
|
70387
70673
|
const ap = (S != null) ? (S.WgslUniforms() + S.WgslCommon()) : "";
|
|
70388
70674
|
return `varying vUV: vec2f;
|
|
70675
|
+
const FOG_DISPLAY_GAMMA: f32 = 2.2;
|
|
70389
70676
|
var textureSamplerSampler: sampler;
|
|
70390
70677
|
var textureSampler: texture_2d<f32>;
|
|
70391
70678
|
var depthSamplerSampler: sampler;
|
|
@@ -70407,6 +70694,7 @@ ${HdrpFogPass.AmbientDeclarations(true)}uniform fogEye: vec4f;
|
|
|
70407
70694
|
uniform fogForward: vec4f;
|
|
70408
70695
|
uniform fogViewport: vec4f;
|
|
70409
70696
|
uniform fogInput: vec4f;
|
|
70697
|
+
uniform fogChain: vec4f;
|
|
70410
70698
|
uniform fogInvViewProj: mat4x4f;
|
|
70411
70699
|
uniform fogEnvMatrix: mat4x4f;
|
|
70412
70700
|
#ifdef HDRP_FOG_AP
|
|
@@ -70414,34 +70702,7 @@ var apLutSampler: sampler;
|
|
|
70414
70702
|
var apLut: texture_2d_array<f32>;
|
|
70415
70703
|
uniform fogApCamera: vec4f;
|
|
70416
70704
|
${ap}
|
|
70417
|
-
|
|
70418
|
-
let sinTheta: f32 = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
|
|
70419
|
-
var ch: vec2f = vec2f(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
|
|
70420
|
-
if (c < 0.0) {
|
|
70421
|
-
let z0: vec2f = z * sinTheta;
|
|
70422
|
-
ch = vec2f(2.0 * pbChapmanHorizontal(z0.x) * exp(Z.x - z0.x), 2.0 * pbChapmanHorizontal(z0.y) * exp(Z.y - z0.y)) - ch;
|
|
70423
|
-
}
|
|
70424
|
-
return ch;
|
|
70425
|
-
}
|
|
70426
|
-
fn fogApOpticalDepth(O: vec3f, V: vec3f, t: f32) -> vec3f {
|
|
70427
|
-
let R: f32 = uniforms.pbAtmo0.x;
|
|
70428
|
-
let n: vec2f = vec2f(uniforms.pbAtmo1.x, uniforms.pbAtmo1.z);
|
|
70429
|
-
let H: vec2f = vec2f(uniforms.pbAtmo1.y, uniforms.pbAtmo1.w);
|
|
70430
|
-
let r: f32 = length(O);
|
|
70431
|
-
let N: vec3f = O / r;
|
|
70432
|
-
let cosChi: f32 = dot(N, V);
|
|
70433
|
-
let Z: vec2f = R * n;
|
|
70434
|
-
let P1: vec3f = O + t * V;
|
|
70435
|
-
let r1: f32 = length(P1);
|
|
70436
|
-
let cosChi1: f32 = dot(P1, V) / r1;
|
|
70437
|
-
let cosChi0: f32 = select(-cosChi, cosChi, cosChi1 >= 0.0);
|
|
70438
|
-
let ch0: vec2f = fogRescaledChapman(r * n, Z, cosChi0);
|
|
70439
|
-
let z1: vec2f = r1 * n;
|
|
70440
|
-
let ch1: vec2f = vec2f(pbChapmanUpper(z1.x, abs(cosChi1)) * exp(Z.x - z1.x), pbChapmanUpper(z1.y, abs(cosChi1)) * exp(Z.y - z1.y));
|
|
70441
|
-
let od: vec2f = abs(ch0 - ch1) * H;
|
|
70442
|
-
let ozone: f32 = pbOzoneOpticalDepth(r, cosChi, t);
|
|
70443
|
-
return od.x * uniforms.pbAirExt.xyz + od.y * uniforms.pbAirExt.w + ozone * uniforms.pbOzone0.xyz;
|
|
70444
|
-
}
|
|
70705
|
+
${HdrpFogPass.AerialPerspectiveWgsl()}
|
|
70445
70706
|
#endif
|
|
70446
70707
|
${HdrpFogPass.AmbientWgsl()}${HdrpFogPass.WgslHelpers("hdrpFog")}${HdrpFogPass.LocalWgsl()}
|
|
70447
70708
|
fn hdrpFogColor(dir: vec3f, dist: f32) -> vec3f {
|
|
@@ -70497,31 +70758,29 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
70497
70758
|
opacity = vec3f(1.0 - trCamera);
|
|
70498
70759
|
}
|
|
70499
70760
|
#ifdef HDRP_FOG_AP
|
|
70500
|
-
|
|
70501
|
-
|
|
70502
|
-
let
|
|
70503
|
-
let
|
|
70504
|
-
let
|
|
70505
|
-
let off: f32 = 1.0 / 128.0;
|
|
70506
|
-
let s: f32 = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
|
|
70507
|
-
let layer: f32 = clamp(s * 64.0 - 0.5, 0.0, 63.0);
|
|
70508
|
-
let l0: f32 = floor(layer);
|
|
70509
|
-
let l1: f32 = min(l0 + 1.0, 63.0);
|
|
70510
|
-
let c0: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l0), 0.0).rgb;
|
|
70511
|
-
let c1: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l1), 0.0).rgb;
|
|
70512
|
-
var apColor: vec3f = mix(c0, c1, vec3f(layer - l0));
|
|
70513
|
-
var apOpacity: vec3f = vec3f(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
|
|
70514
|
-
apOpacity = pbDesaturate(apOpacity, uniforms.pbZenith.w) * uniforms.pbArt.x;
|
|
70515
|
-
let chi: f32 = acos(clamp(cosChi, -1.0, 1.0));
|
|
70516
|
-
let hor: f32 = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
|
|
70517
|
-
let nrm: f32 = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
|
|
70518
|
-
let te: f32 = exp(uniforms.pbArt.y * nrm) * uniforms.pbArt.z - uniforms.pbArt.z;
|
|
70519
|
-
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;
|
|
70520
70766
|
color = color + (vec3f(1.0) - opacity) * apColor;
|
|
70521
70767
|
opacity = opacity + (vec3f(1.0) - opacity) * apOpacity;
|
|
70522
70768
|
}
|
|
70523
70769
|
#endif
|
|
70524
|
-
|
|
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);
|
|
70525
70784
|
}
|
|
70526
70785
|
`;
|
|
70527
70786
|
}
|
|
@@ -70602,6 +70861,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
70602
70861
|
effect.setFloat4("fogE", frame.e[0], frame.e[1], frame.e[2], frame.e[3]);
|
|
70603
70862
|
effect.setFloat4("fogSunDir", frame.sunDir[0], frame.sunDir[1], frame.sunDir[2], frame.sunDir[3]);
|
|
70604
70863
|
effect.setFloat4("fogSun", frame.sun[0], frame.sun[1], frame.sun[2], frame.sun[3]);
|
|
70864
|
+
HdrpFogPass.BindChain(effect, scene, state.fog);
|
|
70605
70865
|
effect.setFloat4("fogEnv", frame.env[0], frame.env[1], frame.env[2], frame.env[3]);
|
|
70606
70866
|
effect.setFloat4("fogSky", frame.sky[0], frame.sky[1], frame.sky[2], frame.sky[3]);
|
|
70607
70867
|
for (let i = 0; i < HdrpFogPass.AmbientVectors; i++)
|
|
@@ -70633,7 +70893,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
70633
70893
|
}
|
|
70634
70894
|
}
|
|
70635
70895
|
HdrpFogPass.ShaderName = "toolkitHdrpFog";
|
|
70636
|
-
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"];
|
|
70637
70897
|
HdrpFogPass.Samplers = ["depthSampler", "fogEnvSampler", "apLut"];
|
|
70638
70898
|
HdrpFogPass.AmbientVectors = 9;
|
|
70639
70899
|
HdrpFogPass.registered = false;
|
|
@@ -71258,6 +71518,8 @@ export class HdrpPbSkyShaders {
|
|
|
71258
71518
|
#define PB_TWO_PI 6.28318530718
|
|
71259
71519
|
#define PB_HALF_PI 1.57079632679
|
|
71260
71520
|
#define PB_INV_PI 0.31830988618
|
|
71521
|
+
#define PB_DISPLAY_GAMMA 0.45454545454
|
|
71522
|
+
#define PB_NO_BODY_HIT 3.0e38
|
|
71261
71523
|
#define PB_FLT_MIN 1.175494351e-38
|
|
71262
71524
|
#define PB_FLT_EPS 5.960464478e-8
|
|
71263
71525
|
float pbSat(float x) { return clamp(x, 0.0, 1.0); }
|
|
@@ -71687,6 +71949,10 @@ vec2 pbCloudLighting${X}(vec2 uv) {
|
|
|
71687
71949
|
` + HdrpPbSkyShaders.GlslCloudUniforms() + `#ifdef PB_CLOUDS
|
|
71688
71950
|
uniform sampler2D cloudTex;
|
|
71689
71951
|
#endif
|
|
71952
|
+
#ifdef PBSKY_SPACE_EMISSION
|
|
71953
|
+
uniform samplerCube spaceCube;
|
|
71954
|
+
uniform vec4 pbSpace;
|
|
71955
|
+
#endif
|
|
71690
71956
|
` + HdrpPbSkyShaders.GlslCommon() + `
|
|
71691
71957
|
vec2 pbMapSkyView(float cosChi, vec3 V) {
|
|
71692
71958
|
float coord = pbFastACos(cosChi) / PB_HALF_PI;
|
|
@@ -71781,6 +72047,12 @@ vec3 pbRenderSky(vec3 d) {
|
|
|
71781
72047
|
}
|
|
71782
72048
|
}
|
|
71783
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
|
|
71784
72056
|
vec3 skyColor = vec3(0.0);
|
|
71785
72057
|
vec3 skyOpacity = vec3(0.0);
|
|
71786
72058
|
if (lookAbove) {
|
|
@@ -71803,7 +72075,12 @@ vec3 pbRenderSky(vec3 d) {
|
|
|
71803
72075
|
}
|
|
71804
72076
|
void main(void) {
|
|
71805
72077
|
vec3 d = normalize(vDir);
|
|
71806
|
-
|
|
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);
|
|
71807
72084
|
}
|
|
71808
72085
|
`;
|
|
71809
72086
|
}
|
|
@@ -71830,6 +72107,8 @@ const PB_PI: f32 = 3.14159265359;
|
|
|
71830
72107
|
const PB_TWO_PI: f32 = 6.28318530718;
|
|
71831
72108
|
const PB_HALF_PI: f32 = 1.57079632679;
|
|
71832
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;
|
|
71833
72112
|
const PB_FLT_MIN: f32 = 1.175494351e-38;
|
|
71834
72113
|
const PB_FLT_EPS: f32 = 5.960464478e-8;
|
|
71835
72114
|
fn pbSat(x: f32) -> f32 { return clamp(x, 0.0, 1.0); }
|
|
@@ -72272,6 +72551,11 @@ fn pbCloudLighting${X}(uv: vec2f) -> vec2f {
|
|
|
72272
72551
|
var cloudTexSampler: sampler;
|
|
72273
72552
|
var cloudTex: texture_2d<f32>;
|
|
72274
72553
|
#endif
|
|
72554
|
+
#ifdef PBSKY_SPACE_EMISSION
|
|
72555
|
+
var spaceCubeSampler: sampler;
|
|
72556
|
+
var spaceCube: texture_cube<f32>;
|
|
72557
|
+
uniform pbSpace: vec4f;
|
|
72558
|
+
#endif
|
|
72275
72559
|
` + HdrpPbSkyShaders.WgslCommon() + `
|
|
72276
72560
|
fn pbMapSkyView(cosChi: f32, V: vec3f) -> vec2f {
|
|
72277
72561
|
let coord: f32 = pbFastACos(cosChi) / PB_HALF_PI;
|
|
@@ -72367,6 +72651,12 @@ fn pbRenderSky(d: vec3f) -> vec3f {
|
|
|
72367
72651
|
}
|
|
72368
72652
|
}
|
|
72369
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
|
|
72370
72660
|
var skyColor: vec3f = vec3f(0.0);
|
|
72371
72661
|
var skyOpacity: vec3f = vec3f(0.0);
|
|
72372
72662
|
if (lookAbove) {
|
|
@@ -72389,8 +72679,13 @@ fn pbRenderSky(d: vec3f) -> vec3f {
|
|
|
72389
72679
|
}
|
|
72390
72680
|
@fragment
|
|
72391
72681
|
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
72392
|
-
|
|
72393
|
-
|
|
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);
|
|
72394
72689
|
}
|
|
72395
72690
|
`;
|
|
72396
72691
|
}
|
|
@@ -72643,17 +72938,16 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72643
72938
|
return (sky != null && sky.disposed !== true) ? sky : null;
|
|
72644
72939
|
}
|
|
72645
72940
|
static Decide(metadata, engine) {
|
|
72646
|
-
|
|
72647
|
-
|
|
72648
|
-
const sky = metadata.hdrp.sky;
|
|
72941
|
+
const sky = PhysicalSkyBlock.SkyOf(metadata);
|
|
72942
|
+
const isUrp = PhysicalSkyBlock.UrpBlock(metadata) != null && !HdrpRendering.IsParityExport(metadata);
|
|
72649
72943
|
if (sky == null)
|
|
72650
|
-
return { live: false, key: null, reason: "no
|
|
72944
|
+
return { live: false, key: null, reason: "no physical sky block" };
|
|
72651
72945
|
const type = (typeof sky.type === "string") ? sky.type.toLowerCase() : "";
|
|
72652
72946
|
if (type !== "physicallybased")
|
|
72653
72947
|
return { live: false, key: null, reason: "sky type " + (type || "unknown") };
|
|
72654
72948
|
if (HdrpPhysicallyBasedSky.IsSupported(engine) === false)
|
|
72655
72949
|
return { live: false, key: "hdrp:pbsky:unsupported", reason: "no WebGL2 / WebGPU half-float render target" };
|
|
72656
|
-
if (sky.volumetricclouds === true)
|
|
72950
|
+
if (isUrp === false && sky.volumetricclouds === true)
|
|
72657
72951
|
return { live: false, key: "hdrp:pbsky:volumetricclouds", reason: "an active VolumetricClouds is not ported (the bake carries it)" };
|
|
72658
72952
|
return { live: true, key: null, reason: "live" };
|
|
72659
72953
|
}
|
|
@@ -72665,11 +72959,12 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72665
72959
|
if (decision.key != null)
|
|
72666
72960
|
LightingConversions.WarnOnce(decision.key, "HDRP PhysicallyBasedSky: " + decision.reason + " - the baked sky is used");
|
|
72667
72961
|
if (decision.key != null)
|
|
72668
|
-
HdrpPhysicallyBasedSky.AttachBakedSkySunAttenuation(scene, metadata
|
|
72962
|
+
HdrpPhysicallyBasedSky.AttachBakedSkySunAttenuation(scene, PhysicalSkyBlock.SkyOf(metadata));
|
|
72669
72963
|
return null;
|
|
72670
72964
|
}
|
|
72671
72965
|
try {
|
|
72672
|
-
|
|
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 });
|
|
72673
72968
|
}
|
|
72674
72969
|
catch (e) {
|
|
72675
72970
|
LightingConversions.WarnOnce("hdrp:pbsky:create", "HDRP PhysicallyBasedSky could not be created (" + ((e && e.message) ? e.message : e) + ") - the baked sky is used");
|
|
@@ -72755,6 +73050,13 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72755
73050
|
}
|
|
72756
73051
|
return precomputationHash !== state.lastPrecompute || lightHash !== state.lastLight || state.requested === true;
|
|
72757
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
|
+
}
|
|
72758
73060
|
static SelectAmbientSh(skyAmbientMode, readback, exported) {
|
|
72759
73061
|
if (skyAmbientMode === 1)
|
|
72760
73062
|
return (readback != null) ? readback : exported;
|
|
@@ -72843,6 +73145,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72843
73145
|
this.filtering = null;
|
|
72844
73146
|
this.pendingFilter = false;
|
|
72845
73147
|
this.capturing = false;
|
|
73148
|
+
this.probeDrawsSky = false;
|
|
72846
73149
|
this.shReading = false;
|
|
72847
73150
|
this.readbackHarmonics = null;
|
|
72848
73151
|
this.exportedHarmonics = null;
|
|
@@ -72857,6 +73160,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72857
73160
|
this.startTime = Date.now();
|
|
72858
73161
|
this.scene = scene;
|
|
72859
73162
|
this.sky = sky || {};
|
|
73163
|
+
this.sunIntensityScale = (typeof this.sky.sunintensityscale === "number") ? this.sky.sunintensityscale : 1;
|
|
72860
73164
|
this.options = options;
|
|
72861
73165
|
this.fixedLights = lights;
|
|
72862
73166
|
this.coefficients = HdrpPbSkyMath.Coefficients(this.sky);
|
|
@@ -72907,16 +73211,23 @@ export class HdrpPhysicallyBasedSky {
|
|
|
72907
73211
|
defines.push("#define PB_CLOUDS");
|
|
72908
73212
|
if (layers.length >= 2)
|
|
72909
73213
|
defines.push("#define PB_CLOUDS2");
|
|
73214
|
+
this.spaceCube = this.loadSpaceEmission();
|
|
73215
|
+
if (this.spaceCube != null)
|
|
73216
|
+
defines.push("#define PBSKY_SPACE_EMISSION");
|
|
72910
73217
|
const material = new ShaderMaterial("HDRP PBSky Material", scene, { vertex: HdrpPbSkyShaders.SkyName, fragment: HdrpPbSkyShaders.SkyName }, {
|
|
72911
73218
|
attributes: ["position"],
|
|
72912
|
-
uniforms: ["world", "viewProjection"].concat(HdrpPbSkyShaders.AtmosphereUniforms, HdrpPbSkyShaders.CloudUniforms),
|
|
72913
|
-
samplers: ["skyViewLut", "cloudTex"],
|
|
73219
|
+
uniforms: ["world", "viewProjection", "pbSpace"].concat(HdrpPbSkyShaders.AtmosphereUniforms, HdrpPbSkyShaders.CloudUniforms),
|
|
73220
|
+
samplers: ["skyViewLut", "cloudTex", "spaceCube"],
|
|
72914
73221
|
defines: defines,
|
|
72915
73222
|
shaderLanguage: language
|
|
72916
73223
|
});
|
|
72917
73224
|
material.backFaceCulling = false;
|
|
72918
73225
|
material.disableDepthWrite = true;
|
|
72919
73226
|
material.setTexture("skyViewLut", this.skyViewLut);
|
|
73227
|
+
if (this.spaceCube != null) {
|
|
73228
|
+
material.setTexture("spaceCube", this.spaceCube);
|
|
73229
|
+
material.setVector4("pbSpace", this.spaceEmissionUniform());
|
|
73230
|
+
}
|
|
72920
73231
|
if (layers.length > 0) {
|
|
72921
73232
|
for (let i = 0; i < layers.length; i++)
|
|
72922
73233
|
this.cloudMaps.push(this.loadCloudMap(layers[i]));
|
|
@@ -73055,7 +73366,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73055
73366
|
let exposure = 1;
|
|
73056
73367
|
const body = new Array(HdrpPbSkyShaders.BodyVec4s * HdrpPbSkyShaders.MaxBodies * 4).fill(0);
|
|
73057
73368
|
const peSafe = (pe > 0) ? pe : 1;
|
|
73058
|
-
const lux = (l) => l.intensity / peSafe;
|
|
73369
|
+
const lux = (l) => (l.intensity * this.sunIntensityScale) / peSafe;
|
|
73059
73370
|
const sunFor = (self) => {
|
|
73060
73371
|
let best = null, max = 0;
|
|
73061
73372
|
for (const l of this.scene.lights)
|
|
@@ -73128,7 +73439,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73128
73439
|
U.pbHorizon = [horizon[0], horizon[1], horizon[2], (typeof p.colorsaturation === "number") ? p.colorsaturation : 1];
|
|
73129
73440
|
U.pbZenith = [zenith[0], zenith[1], zenith[2], (typeof p.alphasaturation === "number") ? p.alphasaturation : 1];
|
|
73130
73441
|
U.pbArt = [(typeof p.alphamultiplier === "number") ? p.alphamultiplier : 1, exp[0], exp[1], pe];
|
|
73131
|
-
U.pbCounts = [lightCount, lights.length, 1, 0];
|
|
73442
|
+
U.pbCounts = [lightCount, lights.length, 1, this.encodesForDisplay() ? 1 : 0];
|
|
73132
73443
|
U.pbBody = body;
|
|
73133
73444
|
this.writeCloudUniforms(lights);
|
|
73134
73445
|
}
|
|
@@ -73163,14 +73474,16 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73163
73474
|
}
|
|
73164
73475
|
}
|
|
73165
73476
|
cloudAmbient() {
|
|
73166
|
-
|
|
73477
|
+
return this.ambientAt(new Vector3(0, -1, 0));
|
|
73478
|
+
}
|
|
73479
|
+
ambientAt(direction) {
|
|
73167
73480
|
if (this.ambientPolynomial != null)
|
|
73168
|
-
return HdrpPbSkyMath.EvaluatePolynomial(this.ambientPolynomial,
|
|
73481
|
+
return HdrpPbSkyMath.EvaluatePolynomial(this.ambientPolynomial, direction);
|
|
73169
73482
|
if (this.exportedHarmonics == null)
|
|
73170
73483
|
this.exportedHarmonics = this.parseExportedAmbient();
|
|
73171
73484
|
if (this.exportedHarmonics == null)
|
|
73172
73485
|
return new Color3(0, 0, 0);
|
|
73173
|
-
return HdrpPbSkyMath.EvaluatePolynomial(LightingConversions.PolynomialFromPreScaled(this.exportedHarmonics, 1),
|
|
73486
|
+
return HdrpPbSkyMath.EvaluatePolynomial(LightingConversions.PolynomialFromPreScaled(this.exportedHarmonics, 1), direction);
|
|
73174
73487
|
}
|
|
73175
73488
|
cloudLayers() {
|
|
73176
73489
|
const cl = this.sky.cloudlayer;
|
|
@@ -73178,6 +73491,22 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73178
73491
|
return [];
|
|
73179
73492
|
return cl.layerlist.filter((l) => l != null && l.cloudmap != null && typeof l.cloudmap.url === "string").slice(0, 2);
|
|
73180
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
|
+
}
|
|
73181
73510
|
loadCloudMap(layer) {
|
|
73182
73511
|
const url = ((typeof this.options.root === "string") ? this.options.root : "") + layer.cloudmap.url;
|
|
73183
73512
|
const tex = new Texture(url, this.scene, true, true, Texture.BILINEAR_SAMPLINGMODE);
|
|
@@ -73209,7 +73538,10 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73209
73538
|
this.applyMaterialUniforms();
|
|
73210
73539
|
return;
|
|
73211
73540
|
}
|
|
73212
|
-
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;
|
|
73213
73545
|
}
|
|
73214
73546
|
applyMaterialUniforms() {
|
|
73215
73547
|
const m = this.material;
|
|
@@ -73238,7 +73570,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73238
73570
|
const p = this.sky.params || {};
|
|
73239
73571
|
if (p.renderingmode === 1)
|
|
73240
73572
|
LightingConversions.WarnOnce("hdrp:pbsky:material", "HDRP PhysicallyBasedSky: the Material rendering mode (a Shader Graph sky) renders the default live atmosphere");
|
|
73241
|
-
if (p.hasgroundtextures === true || p.hasspaceemission === true)
|
|
73573
|
+
if (p.hasgroundtextures === true || (p.hasspaceemission === true && typeof p.spaceemissionurl !== "string"))
|
|
73242
73574
|
LightingConversions.WarnOnce("hdrp:pbsky:textures", "HDRP PhysicallyBasedSky: ground / space emission textures are not rendered");
|
|
73243
73575
|
for (const layer of this.cloudLayers())
|
|
73244
73576
|
if (typeof layer.distortion === "number" && layer.distortion !== 0)
|
|
@@ -73271,20 +73603,28 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73271
73603
|
scene.customRenderTargets.push(probe.cubeTexture);
|
|
73272
73604
|
const p = this.sky.params || {};
|
|
73273
73605
|
probe.cubeTexture.onBeforeRenderObservable.add(() => {
|
|
73274
|
-
|
|
73275
|
-
|
|
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);
|
|
73276
73608
|
const art = this.uniforms.pbArt, counts = this.uniforms.pbCounts;
|
|
73277
73609
|
if (art != null)
|
|
73278
73610
|
this.material.setVector4("pbArt", new Vector4(art[0], art[1], art[2], 1));
|
|
73279
73611
|
if (counts != null)
|
|
73280
|
-
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));
|
|
73281
73613
|
});
|
|
73282
73614
|
probe.cubeTexture.onAfterRenderObservable.add((faceIndex) => {
|
|
73283
|
-
if (
|
|
73615
|
+
if (typeof faceIndex === "number" && faceIndex < 5)
|
|
73284
73616
|
return;
|
|
73285
|
-
this.capturing = false;
|
|
73286
73617
|
this.applyMaterialUniforms();
|
|
73287
|
-
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
|
+
}
|
|
73288
73628
|
});
|
|
73289
73629
|
this.exportedHarmonics = this.parseExportedAmbient();
|
|
73290
73630
|
}
|
|
@@ -73487,6 +73827,8 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73487
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" });
|
|
73488
73828
|
this.apTexture = new BaseTexture(scene, this.apTarget.texture);
|
|
73489
73829
|
this.apTexture.name = "HDRP PBSky Aerial Perspective";
|
|
73830
|
+
this.apTexture.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
|
|
73831
|
+
this.apTexture.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
|
|
73490
73832
|
LightingConversions.WarnOnce("hdrp:pbsky:aerialshadow", "HDRP PhysicallyBasedSky: the aerial perspective has no shadowed in-scatter (the sun shadow map is not sampled)");
|
|
73491
73833
|
}
|
|
73492
73834
|
if (!this.apPass.isReady())
|
|
@@ -73575,7 +73917,7 @@ export class HdrpPhysicallyBasedSky {
|
|
|
73575
73917
|
catch (e) { }
|
|
73576
73918
|
for (const light of this.attenuated)
|
|
73577
73919
|
HdrpPhysicallyBasedSky.RestoreLight(light);
|
|
73578
|
-
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);
|
|
73579
73921
|
for (const d of disposables) {
|
|
73580
73922
|
try {
|
|
73581
73923
|
if (d != null)
|
|
@@ -73594,6 +73936,9 @@ HdrpPhysicallyBasedSky.AerialPerspectiveSize = [32, 32, 64];
|
|
|
73594
73936
|
HdrpPhysicallyBasedSky.AerialMaxDistance = 128000;
|
|
73595
73937
|
HdrpPhysicallyBasedSky.MeshName = "HDRP Physically Based Sky";
|
|
73596
73938
|
HdrpPhysicallyBasedSky.OnEnvironmentUpdated = new Observable();
|
|
73939
|
+
HdrpPhysicallyBasedSky.ProbeRenderKept = 0;
|
|
73940
|
+
HdrpPhysicallyBasedSky.ProbeCaptureAccepted = 1;
|
|
73941
|
+
HdrpPhysicallyBasedSky.ProbeCaptureRetry = 2;
|
|
73597
73942
|
HdrpPhysicallyBasedSky.RealtimeMinFrames = 9;
|
|
73598
73943
|
HdrpPhysicallyBasedSky.ShProjectionFaceSize = 64;
|
|
73599
73944
|
HdrpPhysicallyBasedSky.CaptureFaceCount = 6;
|
|
@@ -91394,6 +91739,7 @@ export class LightProbeNetwork extends ScriptComponent {
|
|
|
91394
91739
|
}
|
|
91395
91740
|
if (this._dynCount === 0)
|
|
91396
91741
|
return;
|
|
91742
|
+
this.dropDisposedEntries();
|
|
91397
91743
|
this._walker.maxWalkSteps = LightProbeNetwork.MaxWalkSteps;
|
|
91398
91744
|
for (let i = 0; i < this._dynCount; i++)
|
|
91399
91745
|
this.sampleDynamic(i);
|
|
@@ -91556,10 +91902,10 @@ export class LightProbeNetwork extends ScriptComponent {
|
|
|
91556
91902
|
const meshState = LightProbeNetwork.MeshState(mesh);
|
|
91557
91903
|
meshState._tkLightProbeOwner = this;
|
|
91558
91904
|
if (mesh.onDisposeObservable != null && meshState._tkLightProbeDisposeObserver == null) {
|
|
91559
|
-
meshState._tkLightProbeDisposeObserver = mesh.onDisposeObservable.
|
|
91905
|
+
meshState._tkLightProbeDisposeObserver = mesh.onDisposeObservable.add(() => {
|
|
91560
91906
|
meshState._tkLightProbeDisposeObserver = null;
|
|
91561
|
-
this.
|
|
91562
|
-
});
|
|
91907
|
+
this.detachDisposed(mesh);
|
|
91908
|
+
}, undefined, true, undefined, true);
|
|
91563
91909
|
}
|
|
91564
91910
|
return true;
|
|
91565
91911
|
}
|
|
@@ -91615,6 +91961,16 @@ export class LightProbeNetwork extends ScriptComponent {
|
|
|
91615
91961
|
return;
|
|
91616
91962
|
LightProbeNetwork.SetRider(mesh, toolkitMetadata, null);
|
|
91617
91963
|
toolkitMetadata.lightProbeOcclusion = null;
|
|
91964
|
+
toolkitMetadata.lightProbeSlot = -1;
|
|
91965
|
+
this.releaseMesh(mesh, registryIndex);
|
|
91966
|
+
}
|
|
91967
|
+
detachDisposed(mesh) {
|
|
91968
|
+
this.detach(mesh);
|
|
91969
|
+
if (LightProbeNetwork.MeshState(mesh)._tkLightProbeOwner !== this)
|
|
91970
|
+
return;
|
|
91971
|
+
this.releaseMesh(mesh, this._dynMeshes.indexOf(mesh));
|
|
91972
|
+
}
|
|
91973
|
+
releaseMesh(mesh, registryIndex) {
|
|
91618
91974
|
if (this._noEnvironment === true)
|
|
91619
91975
|
LightProbeAmbient.ExcludeFromAmbient(this.scene, mesh, false);
|
|
91620
91976
|
for (let lightIndex = 0; lightIndex < this._bakedLights.length; lightIndex++) {
|
|
@@ -91623,26 +91979,11 @@ export class LightProbeNetwork extends ScriptComponent {
|
|
|
91623
91979
|
if (excludedIndex >= 0)
|
|
91624
91980
|
excluded.splice(excludedIndex, 1);
|
|
91625
91981
|
}
|
|
91626
|
-
if (registryIndex >= 0)
|
|
91627
|
-
|
|
91628
|
-
|
|
91629
|
-
const moved = this._dynMeshes[lastIndex];
|
|
91630
|
-
this._dynMeshes[registryIndex] = moved;
|
|
91631
|
-
this._dynTet[registryIndex] = this._dynTet[lastIndex];
|
|
91632
|
-
this._dynHasAnchor[registryIndex] = this._dynHasAnchor[lastIndex];
|
|
91633
|
-
for (let axis = 0; axis < 3; axis++) {
|
|
91634
|
-
this._dynLast[registryIndex * 3 + axis] = this._dynLast[lastIndex * 3 + axis];
|
|
91635
|
-
this._dynAnchor[registryIndex * 3 + axis] = this._dynAnchor[lastIndex * 3 + axis];
|
|
91636
|
-
}
|
|
91637
|
-
moved.metadata.toolkit.lightProbeSlot = registryIndex;
|
|
91638
|
-
}
|
|
91639
|
-
this._dynMeshes[lastIndex] = null;
|
|
91640
|
-
this._dynCount--;
|
|
91641
|
-
}
|
|
91642
|
-
else {
|
|
91982
|
+
if (registryIndex >= 0)
|
|
91983
|
+
this.removeDynamicEntry(registryIndex);
|
|
91984
|
+
else
|
|
91643
91985
|
this._staticCount--;
|
|
91644
|
-
|
|
91645
|
-
toolkitMetadata.lightProbeSlot = -1;
|
|
91986
|
+
const meshState = LightProbeNetwork.MeshState(mesh);
|
|
91646
91987
|
meshState._tkLightProbeOwner = null;
|
|
91647
91988
|
if (meshState._tkLightProbeDisposeObserver != null) {
|
|
91648
91989
|
if (mesh.onDisposeObservable != null)
|
|
@@ -91650,6 +91991,33 @@ export class LightProbeNetwork extends ScriptComponent {
|
|
|
91650
91991
|
meshState._tkLightProbeDisposeObserver = null;
|
|
91651
91992
|
}
|
|
91652
91993
|
}
|
|
91994
|
+
removeDynamicEntry(registryIndex) {
|
|
91995
|
+
const lastIndex = this._dynCount - 1;
|
|
91996
|
+
if (registryIndex !== lastIndex) {
|
|
91997
|
+
const moved = this._dynMeshes[lastIndex];
|
|
91998
|
+
this._dynMeshes[registryIndex] = moved;
|
|
91999
|
+
this._dynTet[registryIndex] = this._dynTet[lastIndex];
|
|
92000
|
+
this._dynHasAnchor[registryIndex] = this._dynHasAnchor[lastIndex];
|
|
92001
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
92002
|
+
this._dynLast[registryIndex * 3 + axis] = this._dynLast[lastIndex * 3 + axis];
|
|
92003
|
+
this._dynAnchor[registryIndex * 3 + axis] = this._dynAnchor[lastIndex * 3 + axis];
|
|
92004
|
+
}
|
|
92005
|
+
const movedToolkit = (moved.metadata != null) ? moved.metadata.toolkit : null;
|
|
92006
|
+
if (movedToolkit != null)
|
|
92007
|
+
movedToolkit.lightProbeSlot = registryIndex;
|
|
92008
|
+
}
|
|
92009
|
+
this._dynMeshes[lastIndex] = null;
|
|
92010
|
+
this._dynCount--;
|
|
92011
|
+
}
|
|
92012
|
+
dropDisposedEntries() {
|
|
92013
|
+
for (let i = this._dynCount - 1; i >= 0; i--) {
|
|
92014
|
+
const mesh = this._dynMeshes[i];
|
|
92015
|
+
const hasToolkitMetadata = (mesh.metadata != null && mesh.metadata.toolkit != null);
|
|
92016
|
+
if (hasToolkitMetadata && !mesh.isDisposed())
|
|
92017
|
+
continue;
|
|
92018
|
+
this.releaseMesh(mesh, i);
|
|
92019
|
+
}
|
|
92020
|
+
}
|
|
91653
92021
|
sampleMeshNow(mesh) {
|
|
91654
92022
|
const tk = (mesh != null && mesh.metadata != null) ? mesh.metadata.toolkit : null;
|
|
91655
92023
|
if (tk == null || tk.lightProbeSH == null)
|
|
@@ -91904,6 +92272,238 @@ LightProbeNetwork.DebugProbeSize = 0.1;
|
|
|
91904
92272
|
LightProbeNetwork.InspectorSection = true;
|
|
91905
92273
|
LightProbeNetwork.OnNetworkChangedObservable = new Observable();
|
|
91906
92274
|
LightProbeNetwork.Sections = ["positions", "probesh", "tetraindices", "tetraadjacency", "renderersh", "rendererocclusion", "rendereranchor"];
|
|
92275
|
+
export class MeshWaterSurface extends ScriptComponent {
|
|
92276
|
+
constructor(transform, scene, properties = {}, alias = "TOOLKIT.MeshWaterSurface") {
|
|
92277
|
+
super(transform, scene, properties, alias);
|
|
92278
|
+
this.waterProperties = null;
|
|
92279
|
+
this.isUnderwater = false;
|
|
92280
|
+
this.fogColorChannels = [0, 0, 0];
|
|
92281
|
+
this.surfaceFogColor = new Color3(0, 0, 0);
|
|
92282
|
+
this.surfaceFogDensity = 0;
|
|
92283
|
+
this.surfaceFogStart = 0;
|
|
92284
|
+
this.surfaceFogEnd = 0;
|
|
92285
|
+
this.shownNodes = [];
|
|
92286
|
+
this.shownParticles = [];
|
|
92287
|
+
this.volumeNodes = [];
|
|
92288
|
+
this.flippedNodes = [];
|
|
92289
|
+
this.followers = [];
|
|
92290
|
+
this.followPosition = new Vector3(0, 0, 0);
|
|
92291
|
+
}
|
|
92292
|
+
static Get(scene) {
|
|
92293
|
+
if (scene == null)
|
|
92294
|
+
return null;
|
|
92295
|
+
return MeshWaterSurface.Registry.get(scene.uniqueId) ?? null;
|
|
92296
|
+
}
|
|
92297
|
+
awake() {
|
|
92298
|
+
this.waterProperties = this.getProperties();
|
|
92299
|
+
if (MeshWaterSurface.Registry.has(this.scene.uniqueId)) {
|
|
92300
|
+
Tools.Warn("MeshWaterSurface: '" + this.transform.name + "' replaces the scene's first water surface");
|
|
92301
|
+
}
|
|
92302
|
+
MeshWaterSurface.Registry.set(this.scene.uniqueId, this);
|
|
92303
|
+
}
|
|
92304
|
+
ready() {
|
|
92305
|
+
const properties = this.getProperties();
|
|
92306
|
+
if (properties.underwater == null)
|
|
92307
|
+
return;
|
|
92308
|
+
for (const node of this.findNodes(properties.underwater.shownodes)) {
|
|
92309
|
+
for (const component of SceneManager.GetComponents(node) ?? [])
|
|
92310
|
+
ScriptComponent.AwakeInstance(component);
|
|
92311
|
+
}
|
|
92312
|
+
}
|
|
92313
|
+
start() {
|
|
92314
|
+
const underwater = this.waterProperties.underwater;
|
|
92315
|
+
if (underwater == null)
|
|
92316
|
+
return;
|
|
92317
|
+
this.shownNodes = this.findNodes(underwater.shownodes);
|
|
92318
|
+
this.shownParticles = this.findParticles(this.shownNodes);
|
|
92319
|
+
this.volumeNodes = this.findNodes(underwater.volumenodes);
|
|
92320
|
+
this.flippedNodes = this.findNodes(underwater.flipnodes).map((node) => this.prepareFlip(node));
|
|
92321
|
+
this.followers = this.prepareFollowers(underwater.follownodes);
|
|
92322
|
+
this.update();
|
|
92323
|
+
}
|
|
92324
|
+
update() {
|
|
92325
|
+
if (this.waterProperties == null || this.waterProperties.underwater == null)
|
|
92326
|
+
return;
|
|
92327
|
+
const camera = this.scene.activeCamera;
|
|
92328
|
+
if (camera == null)
|
|
92329
|
+
return;
|
|
92330
|
+
const isCameraUnder = this.isCameraUnderwater(camera);
|
|
92331
|
+
if (isCameraUnder !== this.isUnderwater) {
|
|
92332
|
+
this.isUnderwater = isCameraUnder;
|
|
92333
|
+
if (isCameraUnder)
|
|
92334
|
+
this.enterUnderwater();
|
|
92335
|
+
else
|
|
92336
|
+
this.leaveUnderwater();
|
|
92337
|
+
}
|
|
92338
|
+
if (!isCameraUnder)
|
|
92339
|
+
return;
|
|
92340
|
+
this.updateFog(this.waterProperties.underwater.sealevel - camera.globalPosition.y);
|
|
92341
|
+
this.updateFollowers(camera);
|
|
92342
|
+
}
|
|
92343
|
+
destroy() {
|
|
92344
|
+
if (this.isUnderwater)
|
|
92345
|
+
this.leaveUnderwater();
|
|
92346
|
+
this.isUnderwater = false;
|
|
92347
|
+
if (MeshWaterSurface.Registry.get(this.scene.uniqueId) === this)
|
|
92348
|
+
MeshWaterSurface.Registry.delete(this.scene.uniqueId);
|
|
92349
|
+
}
|
|
92350
|
+
getSeaLevel() {
|
|
92351
|
+
return this.waterProperties.sealevel;
|
|
92352
|
+
}
|
|
92353
|
+
getSurfaceHeight() {
|
|
92354
|
+
const properties = this.waterProperties;
|
|
92355
|
+
const clockSeconds = ShaderGlobals.GetClock(this.scene).time;
|
|
92356
|
+
return properties.sealevel + Math.sin(clockSeconds * properties.wavespeed) * properties.waveamplitude;
|
|
92357
|
+
}
|
|
92358
|
+
getCameraHeight(camera) {
|
|
92359
|
+
if (camera == null)
|
|
92360
|
+
return 0;
|
|
92361
|
+
return camera.globalPosition.y - this.getSurfaceHeight();
|
|
92362
|
+
}
|
|
92363
|
+
isCameraUnderwater(camera) {
|
|
92364
|
+
if (camera == null)
|
|
92365
|
+
return false;
|
|
92366
|
+
const underwater = this.waterProperties.underwater;
|
|
92367
|
+
const crossingHeight = (underwater != null) ? underwater.sealevel : this.getSurfaceHeight();
|
|
92368
|
+
return camera.globalPosition.y <= crossingHeight;
|
|
92369
|
+
}
|
|
92370
|
+
ProjectPointOnWaterSurface(searchParameters) {
|
|
92371
|
+
if (searchParameters == null || searchParameters.targetPositionWS == null)
|
|
92372
|
+
return null;
|
|
92373
|
+
const target = searchParameters.targetPositionWS;
|
|
92374
|
+
const targetX = Array.isArray(target) ? target[0] : target.x;
|
|
92375
|
+
const targetZ = Array.isArray(target) ? target[2] : target.z;
|
|
92376
|
+
const projectedPosition = new Vector3(targetX, this.getSurfaceHeight(), targetZ);
|
|
92377
|
+
return {
|
|
92378
|
+
projectedPositionWS: projectedPosition,
|
|
92379
|
+
normalWS: Vector3.Up(),
|
|
92380
|
+
candidateLocationWS: projectedPosition.clone(),
|
|
92381
|
+
currentDirectionWS: Vector3.Zero(),
|
|
92382
|
+
numIterations: 1,
|
|
92383
|
+
error: 0
|
|
92384
|
+
};
|
|
92385
|
+
}
|
|
92386
|
+
FindWaterSurfaceHeight(searchParameters) {
|
|
92387
|
+
return this.ProjectPointOnWaterSurface(searchParameters);
|
|
92388
|
+
}
|
|
92389
|
+
findNodes(nodeIds) {
|
|
92390
|
+
const nodes = [];
|
|
92391
|
+
for (const nodeId of nodeIds ?? []) {
|
|
92392
|
+
const node = SceneManager.GetTransformNodeByID(this.scene, nodeId);
|
|
92393
|
+
if (node != null)
|
|
92394
|
+
nodes.push(node);
|
|
92395
|
+
}
|
|
92396
|
+
return nodes;
|
|
92397
|
+
}
|
|
92398
|
+
findParticles(nodes) {
|
|
92399
|
+
const particles = [];
|
|
92400
|
+
for (const node of nodes) {
|
|
92401
|
+
const system = SceneManager.GetComponent(node, MeshWaterSurface.PARTICLES_CLASS);
|
|
92402
|
+
if (system != null)
|
|
92403
|
+
particles.push(system);
|
|
92404
|
+
}
|
|
92405
|
+
return particles;
|
|
92406
|
+
}
|
|
92407
|
+
prepareFlip(node) {
|
|
92408
|
+
if (node.isWorldMatrixFrozen)
|
|
92409
|
+
node.unfreezeWorldMatrix();
|
|
92410
|
+
if (node.rotationQuaternion == null)
|
|
92411
|
+
node.rotationQuaternion = Quaternion.FromEulerVector(node.rotation);
|
|
92412
|
+
const surfaceRotation = node.rotationQuaternion.clone();
|
|
92413
|
+
const flipRotation = Quaternion.FromEulerAngles(Math.PI, 0, 0);
|
|
92414
|
+
return { node: node, surfaceRotation: surfaceRotation, underwaterRotation: flipRotation.multiply(surfaceRotation) };
|
|
92415
|
+
}
|
|
92416
|
+
prepareFollowers(follownodes) {
|
|
92417
|
+
const followers = [];
|
|
92418
|
+
for (const follower of follownodes ?? []) {
|
|
92419
|
+
const node = SceneManager.GetTransformNodeByID(this.scene, follower.node);
|
|
92420
|
+
if (node == null)
|
|
92421
|
+
continue;
|
|
92422
|
+
if (follower.scale != null)
|
|
92423
|
+
node.scaling.set(follower.scale[0], follower.scale[1], follower.scale[2]);
|
|
92424
|
+
followers.push({ node: node, offset: Vector3.FromArray(follower.offset) });
|
|
92425
|
+
}
|
|
92426
|
+
return followers;
|
|
92427
|
+
}
|
|
92428
|
+
enterUnderwater() {
|
|
92429
|
+
const underwater = this.waterProperties.underwater;
|
|
92430
|
+
if (underwater.fogcolors.length > 0)
|
|
92431
|
+
this.writeUnderwaterFog(underwater);
|
|
92432
|
+
for (const node of this.shownNodes)
|
|
92433
|
+
node.setEnabled(true);
|
|
92434
|
+
for (const particles of this.shownParticles)
|
|
92435
|
+
particles.play();
|
|
92436
|
+
this.setVolumesEnabled(true);
|
|
92437
|
+
for (const flipped of this.flippedNodes)
|
|
92438
|
+
flipped.node.rotationQuaternion.copyFrom(flipped.underwaterRotation);
|
|
92439
|
+
}
|
|
92440
|
+
leaveUnderwater() {
|
|
92441
|
+
if (this.waterProperties.underwater.fogcolors.length > 0)
|
|
92442
|
+
this.restoreSurfaceFog();
|
|
92443
|
+
for (const particles of this.shownParticles)
|
|
92444
|
+
particles.stop(true, MeshWaterSurface.STOP_AND_CLEAR);
|
|
92445
|
+
for (const node of this.shownNodes)
|
|
92446
|
+
node.setEnabled(false);
|
|
92447
|
+
this.setVolumesEnabled(false);
|
|
92448
|
+
for (const flipped of this.flippedNodes)
|
|
92449
|
+
flipped.node.rotationQuaternion.copyFrom(flipped.surfaceRotation);
|
|
92450
|
+
}
|
|
92451
|
+
writeUnderwaterFog(underwater) {
|
|
92452
|
+
const scene = this.scene;
|
|
92453
|
+
this.surfaceFogColor.copyFrom(scene.fogColor);
|
|
92454
|
+
this.surfaceFogDensity = scene.fogDensity;
|
|
92455
|
+
this.surfaceFogStart = scene.fogStart;
|
|
92456
|
+
this.surfaceFogEnd = scene.fogEnd;
|
|
92457
|
+
const isExponentialSquared = scene.fogMode === Scene.FOGMODE_EXP2;
|
|
92458
|
+
const densityScale = isExponentialSquared ? SceneManager.FogExp2DensityScale : SceneManager.FogExpDensityScale;
|
|
92459
|
+
scene.fogDensity = underwater.fogdensity * densityScale;
|
|
92460
|
+
scene.fogStart = underwater.fogstart;
|
|
92461
|
+
scene.fogEnd = underwater.fogend * MeshWaterSurface.LINEAR_FOG_END_SCALE;
|
|
92462
|
+
}
|
|
92463
|
+
restoreSurfaceFog() {
|
|
92464
|
+
const scene = this.scene;
|
|
92465
|
+
scene.fogColor.copyFrom(this.surfaceFogColor);
|
|
92466
|
+
scene.fogDensity = this.surfaceFogDensity;
|
|
92467
|
+
scene.fogStart = this.surfaceFogStart;
|
|
92468
|
+
scene.fogEnd = this.surfaceFogEnd;
|
|
92469
|
+
}
|
|
92470
|
+
setVolumesEnabled(enabled) {
|
|
92471
|
+
for (const node of this.volumeNodes) {
|
|
92472
|
+
node.setEnabled(enabled);
|
|
92473
|
+
const volume = SceneManager.GetComponent(node, MeshWaterSurface.POST_PROCESSOR_CLASS);
|
|
92474
|
+
if (volume != null)
|
|
92475
|
+
volume.SetVolumeEnabled(enabled);
|
|
92476
|
+
}
|
|
92477
|
+
}
|
|
92478
|
+
updateFog(depthMeters) {
|
|
92479
|
+
const underwater = this.waterProperties.underwater;
|
|
92480
|
+
if (underwater.fogcolors.length === 0)
|
|
92481
|
+
return;
|
|
92482
|
+
const depthFraction = Scalar.Clamp(depthMeters / underwater.fogdepth, 0, 1);
|
|
92483
|
+
const keyPosition = depthFraction * (underwater.fogcolors.length - 1);
|
|
92484
|
+
const lowerKey = Math.floor(keyPosition);
|
|
92485
|
+
const upperKey = Math.min(lowerKey + 1, underwater.fogcolors.length - 1);
|
|
92486
|
+
const keyBlend = keyPosition - lowerKey;
|
|
92487
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
92488
|
+
const gradientValue = Scalar.Lerp(underwater.fogcolors[lowerKey][channel], underwater.fogcolors[upperKey][channel], keyBlend);
|
|
92489
|
+
const litValue = gradientValue * underwater.suncolor[channel];
|
|
92490
|
+
const addedValue = underwater.fogcolormultiplier[channel];
|
|
92491
|
+
this.fogColorChannels[channel] = (addedValue >= 0) ? Scalar.Clamp(litValue + addedValue, 0, 1) : litValue;
|
|
92492
|
+
}
|
|
92493
|
+
this.scene.fogColor.set(this.fogColorChannels[0], this.fogColorChannels[1], this.fogColorChannels[2]);
|
|
92494
|
+
}
|
|
92495
|
+
updateFollowers(camera) {
|
|
92496
|
+
for (const follower of this.followers) {
|
|
92497
|
+
camera.globalPosition.addToRef(follower.offset, this.followPosition);
|
|
92498
|
+
follower.node.setAbsolutePosition(this.followPosition);
|
|
92499
|
+
}
|
|
92500
|
+
}
|
|
92501
|
+
}
|
|
92502
|
+
MeshWaterSurface.POST_PROCESSOR_CLASS = "TOOLKIT.PostProcessor";
|
|
92503
|
+
MeshWaterSurface.PARTICLES_CLASS = "TOOLKIT.ShurikenParticles";
|
|
92504
|
+
MeshWaterSurface.STOP_AND_CLEAR = 1;
|
|
92505
|
+
MeshWaterSurface.LINEAR_FOG_END_SCALE = 2;
|
|
92506
|
+
MeshWaterSurface.Registry = new Map();
|
|
91907
92507
|
export class NavigationAgent extends ScriptComponent {
|
|
91908
92508
|
isReady() { return this.m_agentReady; }
|
|
91909
92509
|
isNavigating() { return (this.m_navStatus === NavMoveStatus.Moving); }
|
|
@@ -108703,6 +109303,7 @@ export class PostProcessor extends ScriptComponent {
|
|
|
108703
109303
|
const props = this.getProperties();
|
|
108704
109304
|
this.cameraOwner = PostProcessingContract.isCameraOwner(props);
|
|
108705
109305
|
this.volumeProperties = this.cameraOwner ? null : PostProcessingContract.parseVolume(props);
|
|
109306
|
+
this.volumeEnabled = !this.isSwitchedVolume();
|
|
108706
109307
|
this.volumeLayer = SceneManager.GetTransformLayer(this.transform);
|
|
108707
109308
|
PostProcessor.Registry.push(this);
|
|
108708
109309
|
if (PostProcessor.GlobalInstance == null)
|
|
@@ -108939,7 +109540,8 @@ export class PostProcessor extends ScriptComponent {
|
|
|
108939
109540
|
PostProcessor.warnOnce("cameras:" + selection.mode, selection.warning);
|
|
108940
109541
|
const hasLocal = entries.some(e => e.properties.isglobal === false && e.properties.bounds != null);
|
|
108941
109542
|
const hasCinemachine = volumes.some((volume) => volume.isCinemachineVolume());
|
|
108942
|
-
const
|
|
109543
|
+
const hasSwitched = volumes.some((volume) => volume.isSwitchedVolume());
|
|
109544
|
+
const liveBlend = hasLocal || hasCinemachine || hasSwitched;
|
|
108943
109545
|
const localLuts = liveBlend ? entries.map((e) => {
|
|
108944
109546
|
const lut = PostProcessingContract.normalise(e.properties).lut;
|
|
108945
109547
|
return (lut != null && lut.identity !== true && typeof lut.url === "string" && lut.url !== "") ? lut : null;
|
|
@@ -109034,6 +109636,9 @@ export class PostProcessor extends ScriptComponent {
|
|
|
109034
109636
|
isCinemachineVolume() {
|
|
109035
109637
|
return this.volumeProperties != null && this.volumeProperties.role === PostProcessingContract.CINEMACHINE_VOLUME_ROLE;
|
|
109036
109638
|
}
|
|
109639
|
+
isSwitchedVolume() {
|
|
109640
|
+
return this.volumeProperties != null && this.volumeProperties.role === PostProcessingContract.SWITCHED_VOLUME_ROLE;
|
|
109641
|
+
}
|
|
109037
109642
|
IsVolumeEnabled() {
|
|
109038
109643
|
if (this.volumeEnabled === false)
|
|
109039
109644
|
return false;
|
|
@@ -109648,7 +110253,7 @@ export class PostProcessor extends ScriptComponent {
|
|
|
109648
110253
|
applyHdrpFog(camera, textureType) {
|
|
109649
110254
|
if (camera == null || this.scene == null || HdrpRendering == null || HdrpFogPass == null)
|
|
109650
110255
|
return;
|
|
109651
|
-
if (HdrpRendering.IsParity(this.scene) !== true)
|
|
110256
|
+
if (HdrpRendering.IsParity(this.scene) !== true && PhysicalSkyBlock.UrpBlock(HdrpRendering.SceneMetadata(this.scene)) == null)
|
|
109652
110257
|
return;
|
|
109653
110258
|
const fog = HdrpFogPass.FogBlock(this.scene);
|
|
109654
110259
|
if (!HdrpFogMath.PassNeeded(fog, HdrpFogPass.SkyOf(this.scene)))
|
|
@@ -115080,6 +115685,7 @@ export class PostProcessingContract {
|
|
|
115080
115685
|
}
|
|
115081
115686
|
PostProcessingContract.VOLUME_TYPES = ["BuiltIn", "URP", "HDRP"];
|
|
115082
115687
|
PostProcessingContract.CINEMACHINE_VOLUME_ROLE = "cinemachine";
|
|
115688
|
+
PostProcessingContract.SWITCHED_VOLUME_ROLE = "switched";
|
|
115083
115689
|
PostProcessingContract.GRADING_MODES = ["LowDefinitionRange", "HighDefinitionRange", "External"];
|
|
115084
115690
|
PostProcessingContract.RENDERING_PATH_DEFERRED = "DeferredShading";
|
|
115085
115691
|
PostProcessingContract.RENDERING_PATH_FORWARD = "Forward";
|
|
@@ -130843,6 +131449,20 @@ export class RigidbodyPhysics extends ScriptComponent {
|
|
|
130843
131449
|
RigidbodyPhysics.ConfigRigidbodyPhysics(scene, entity, false, false, physics, mass, centerOfMass);
|
|
130844
131450
|
return body;
|
|
130845
131451
|
}
|
|
131452
|
+
static MarkCompoundChildForDisposal(entity) {
|
|
131453
|
+
if (RigidbodyPhysics.RendersItself(entity) === false) {
|
|
131454
|
+
Utilities.AddLoaderItemMarkedForDisposal(entity);
|
|
131455
|
+
return;
|
|
131456
|
+
}
|
|
131457
|
+
const colliderMesh = SceneManager.FindChildTransformNode(entity, (entity.name + ".Collider"), SearchType.ExactMatch, true);
|
|
131458
|
+
if (colliderMesh != null)
|
|
131459
|
+
Utilities.AddLoaderItemMarkedForDisposal(colliderMesh);
|
|
131460
|
+
}
|
|
131461
|
+
static RendersItself(node) {
|
|
131462
|
+
if (!(node instanceof AbstractMesh) || node.getTotalVertices() <= 0 || node.name.endsWith(".Collider"))
|
|
131463
|
+
return false;
|
|
131464
|
+
return node.metadata != null && node.metadata.toolkit != null && node.metadata.toolkit.renderer != null;
|
|
131465
|
+
}
|
|
130846
131466
|
static CreateCompoundPhysicsShapeAndBody(scene, root, entity, element, impostortype, staticfriction, dynamicfriction, restitution, fcombine, rcombine, sitems, item, center, complex, trigger, persist, layer, filter) {
|
|
130847
131467
|
let center_xx = 0;
|
|
130848
131468
|
let center_yy = 0;
|
|
@@ -130853,7 +131473,7 @@ export class RigidbodyPhysics extends ScriptComponent {
|
|
|
130853
131473
|
const rootScale = RigidbodyPhysics.GetColliderScale(root);
|
|
130854
131474
|
const entityScale = (entity === root) ? rootScale : RigidbodyPhysics.RoundColliderScale(entity.scaling);
|
|
130855
131475
|
if (persist === false && complex === true)
|
|
130856
|
-
|
|
131476
|
+
RigidbodyPhysics.MarkCompoundChildForDisposal(entity);
|
|
130857
131477
|
if (impostortype == RigidbodyPhysics.ConvexHullImpostor) {
|
|
130858
131478
|
let convexHullMesh = SceneManager.FindChildTransformNode(entity, (entity.name + ".Collider"), SearchType.ExactMatch, true);
|
|
130859
131479
|
if (convexHullMesh != null) {
|
|
@@ -133691,7 +134311,7 @@ export class ShurikenParticles extends ScriptComponent {
|
|
|
133691
134311
|
}
|
|
133692
134312
|
canBeginWarmupDraw() {
|
|
133693
134313
|
const particleSystem = this._prtSystem;
|
|
133694
|
-
if (particleSystem == null || particleSystem.isDisposed || this._prtState == null || this._prtProps == null
|
|
134314
|
+
if (particleSystem == null || particleSystem.isDisposed || this._prtState == null || this._prtProps == null)
|
|
133695
134315
|
return false;
|
|
133696
134316
|
if (this._prtWarming || this._prtPrewarmJob != null)
|
|
133697
134317
|
return false;
|
|
@@ -141523,6 +142143,7 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
141523
142143
|
if (heights == null)
|
|
141524
142144
|
throw new Error("heights block has no data");
|
|
141525
142145
|
hf = new TerrainHeightfield(props, heights);
|
|
142146
|
+
hf.setEdgeNeighbors(this.resolveEdgeNeighbors());
|
|
141526
142147
|
this._heightfield = hf;
|
|
141527
142148
|
}
|
|
141528
142149
|
catch (e) {
|
|
@@ -141715,6 +142336,45 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
141715
142336
|
this._isBuilt = true;
|
|
141716
142337
|
this.onBuiltObservable.notifyObservers(this);
|
|
141717
142338
|
}
|
|
142339
|
+
resolveEdgeNeighbors() {
|
|
142340
|
+
const contract = this._contract;
|
|
142341
|
+
const tiles = TerrainBuilder.GetTerrains(this.scene);
|
|
142342
|
+
const ids = contract.neighbors;
|
|
142343
|
+
const hasIds = ids != null && (ids.left != null || ids.right != null || ids.top != null || ids.bottom != null);
|
|
142344
|
+
const neighbors = {
|
|
142345
|
+
left: this.edgeNeighborFor(tiles, hasIds ? ids.left : null, hasIds, -1, 0),
|
|
142346
|
+
right: this.edgeNeighborFor(tiles, hasIds ? ids.right : null, hasIds, 1, 0),
|
|
142347
|
+
top: this.edgeNeighborFor(tiles, hasIds ? ids.top : null, hasIds, 0, 1),
|
|
142348
|
+
bottom: this.edgeNeighborFor(tiles, hasIds ? ids.bottom : null, hasIds, 0, -1),
|
|
142349
|
+
};
|
|
142350
|
+
const anyNeighbor = neighbors.left != null || neighbors.right != null || neighbors.top != null || neighbors.bottom != null;
|
|
142351
|
+
return anyNeighbor ? neighbors : null;
|
|
142352
|
+
}
|
|
142353
|
+
edgeNeighborFor(tiles, neighborId, useIds, offsetX, offsetZ) {
|
|
142354
|
+
for (const other of tiles) {
|
|
142355
|
+
if (other === this || !this.canShareEdgeWith(other))
|
|
142356
|
+
continue;
|
|
142357
|
+
const isNeighbor = useIds ? other.transform.id === neighborId : this.isAdjacentTile(other, offsetX, offsetZ);
|
|
142358
|
+
if (!isNeighbor)
|
|
142359
|
+
continue;
|
|
142360
|
+
const heights = TerrainContract.GetBlock(other.contract, "heights", "u16");
|
|
142361
|
+
return (heights != null) ? { heights: heights, heightScale: other.contract.size[1] } : null;
|
|
142362
|
+
}
|
|
142363
|
+
return null;
|
|
142364
|
+
}
|
|
142365
|
+
canShareEdgeWith(other) {
|
|
142366
|
+
const contract = this._contract, otherContract = other.contract;
|
|
142367
|
+
return otherContract.heightresolution === contract.heightresolution && otherContract.size[0] === contract.size[0] && otherContract.size[2] === contract.size[2];
|
|
142368
|
+
}
|
|
142369
|
+
isAdjacentTile(other, offsetX, offsetZ) {
|
|
142370
|
+
const contract = this._contract, otherContract = other.contract;
|
|
142371
|
+
if (contract.allowautoconnect !== true || otherContract.allowautoconnect !== true || otherContract.groupingid !== contract.groupingid)
|
|
142372
|
+
return false;
|
|
142373
|
+
this.transform.computeWorldMatrix(true);
|
|
142374
|
+
const offset = this.worldToUnityUV(other.transform.getAbsolutePosition());
|
|
142375
|
+
const toleranceU = TERRAIN_EDGE_TOLERANCE_METERS / contract.size[0], toleranceV = TERRAIN_EDGE_TOLERANCE_METERS / contract.size[2];
|
|
142376
|
+
return Math.abs(offset[0] - offsetX) <= toleranceU && Math.abs(offset[1] - offsetZ) <= toleranceV;
|
|
142377
|
+
}
|
|
141718
142378
|
applyTreesAndFoliageVisibility() {
|
|
141719
142379
|
if (this._trees != null)
|
|
141720
142380
|
this._trees.setVisible(this._drawTreesAndFoliage);
|
|
@@ -142299,12 +142959,21 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
142299
142959
|
worldToUnityUV(worldPosition) {
|
|
142300
142960
|
if (this._contract == null || worldPosition == null)
|
|
142301
142961
|
return null;
|
|
142302
|
-
|
|
142303
|
-
const local =
|
|
142962
|
+
this.transform.getWorldMatrix().invertToRef(TerrainBuilder.ScratchInverse);
|
|
142963
|
+
const local = TerrainBuilder.ScratchLocal;
|
|
142964
|
+
Vector3.TransformCoordinatesToRef(worldPosition, TerrainBuilder.ScratchInverse, local);
|
|
142304
142965
|
const size = this._contract.size;
|
|
142305
142966
|
const ux = TerrainMath.UnityToLocalX(local.x, this._contract.handedness);
|
|
142306
142967
|
return [ux / size[0], local.z / size[2]];
|
|
142307
142968
|
}
|
|
142969
|
+
containsWorldPosition(worldPosition, tolerance) {
|
|
142970
|
+
const uv = this.worldToUnityUV(worldPosition);
|
|
142971
|
+
if (uv == null)
|
|
142972
|
+
return false;
|
|
142973
|
+
const size = this._contract.size;
|
|
142974
|
+
const slackU = tolerance / size[0], slackV = tolerance / size[2];
|
|
142975
|
+
return uv[0] >= -slackU && uv[0] <= 1 + slackU && uv[1] >= -slackV && uv[1] <= 1 + slackV;
|
|
142976
|
+
}
|
|
142308
142977
|
SampleHeight(worldPosition) {
|
|
142309
142978
|
if (this._heightfield == null)
|
|
142310
142979
|
return 0;
|
|
@@ -142373,6 +143042,10 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
142373
143042
|
if (list[i]._heightfield != null && list[i].WorldToTerrainUV(worldPosition) != null)
|
|
142374
143043
|
return list[i];
|
|
142375
143044
|
}
|
|
143045
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
143046
|
+
if (list[i]._heightfield != null && list[i].containsWorldPosition(worldPosition, TERRAIN_EDGE_TOLERANCE_METERS))
|
|
143047
|
+
return list[i];
|
|
143048
|
+
}
|
|
142376
143049
|
return null;
|
|
142377
143050
|
}
|
|
142378
143051
|
static GetWorldHeightAt(scene, worldPosition) {
|
|
@@ -142390,6 +143063,8 @@ export class TerrainBuilder extends ScriptComponent {
|
|
|
142390
143063
|
}
|
|
142391
143064
|
}
|
|
142392
143065
|
TerrainBuilder._registry = new Map();
|
|
143066
|
+
TerrainBuilder.ScratchInverse = new Matrix();
|
|
143067
|
+
TerrainBuilder.ScratchLocal = new Vector3();
|
|
142393
143068
|
TerrainBuilder.OnLoadProgressObservable = new Observable();
|
|
142394
143069
|
TerrainBuilder.PRELOAD_TIMEOUT_MS = 120000;
|
|
142395
143070
|
TerrainBuilder.SETTLE_MS = 30000;
|
|
@@ -142810,7 +143485,7 @@ export class TerrainDetails {
|
|
|
142810
143485
|
}
|
|
142811
143486
|
const F = TerrainFoliagePlugin;
|
|
142812
143487
|
const layer = rt.data;
|
|
142813
|
-
const sway = layer.sway;
|
|
143488
|
+
const sway = F.GraphOwnsMotion(source) ? null : layer.sway;
|
|
142814
143489
|
const urp = ((this._owner.contract != null && this._owner.contract.flavour) || "urp") === "urp";
|
|
142815
143490
|
const modes = F.MODE_FADE | ((sway != null) ? F.MODE_SWAY : 0) | ((layer.rendermode === "Grass") ? F.MODE_GRASSWAVE : 0) | (urp ? F.MODE_URPSPECULAR : 0);
|
|
142816
143491
|
const swayKey = (sway != null) ? (sway.amount + "," + sway.speed + "," + sway.wavelength) : "-";
|
|
@@ -143087,19 +143762,12 @@ export class TerrainDetails {
|
|
|
143087
143762
|
rt.mainMatrices = matrices;
|
|
143088
143763
|
rt.mainColors = colors;
|
|
143089
143764
|
rt.main.thinInstanceSetBuffer("matrix", matrices, 16, false);
|
|
143090
|
-
|
|
143765
|
+
TerrainTrees.ResizePreviousMatrices(rt.main, matrices);
|
|
143091
143766
|
if (colors != null)
|
|
143092
143767
|
rt.main.thinInstanceSetBuffer(TerrainDetails.TintBuffer(rt), colors, 4, false);
|
|
143093
143768
|
if (ShaderGraphRuntime != null)
|
|
143094
143769
|
ShaderGraphRuntime.EnsureThinInstanceBuffers(rt.main);
|
|
143095
143770
|
}
|
|
143096
|
-
static ResizePreviousMatrices(mesh, matrices) {
|
|
143097
|
-
const storage = mesh._thinInstanceDataStorage;
|
|
143098
|
-
if (storage == null || storage.previousMatrixBuffer == null || storage.previousMatrixData != null)
|
|
143099
|
-
return;
|
|
143100
|
-
storage.previousMatrixBuffer.dispose();
|
|
143101
|
-
storage.previousMatrixBuffer = mesh._thinInstanceCreateMatrixBuffer("previousWorld", matrices, false);
|
|
143102
|
-
}
|
|
143103
143771
|
static TintBuffer(rt) { return (rt.grass != null) ? "tkGrassTint" : "color"; }
|
|
143104
143772
|
chunkDistance(c, x, z, size) {
|
|
143105
143773
|
const x0 = c.cx * this._cw, x1 = Math.min((c.cx + 1) * this._cw, size[0]);
|
|
@@ -143693,6 +144361,9 @@ export class TerrainFoliagePlugin extends MaterialPluginBase {
|
|
|
143693
144361
|
const shader = m.getShaderName();
|
|
143694
144362
|
return shader != null && shader !== "UniversalShaderMaterial" && shader !== "CustomShaderMaterial";
|
|
143695
144363
|
}
|
|
144364
|
+
static GraphOwnsMotion(material) {
|
|
144365
|
+
return TerrainFoliagePlugin.IsGraphMaterial(material) && material.__speedTreeWind == null;
|
|
144366
|
+
}
|
|
143696
144367
|
getClassName() {
|
|
143697
144368
|
return "TerrainFoliagePlugin";
|
|
143698
144369
|
}
|
|
@@ -144865,6 +145536,7 @@ export class TerrainHeightfield {
|
|
|
144865
145536
|
this._errors = null;
|
|
144866
145537
|
this._minH = null;
|
|
144867
145538
|
this._maxH = null;
|
|
145539
|
+
this.edgeNeighbors = null;
|
|
144868
145540
|
this.R = contract.heightresolution;
|
|
144869
145541
|
this.size = [contract.size[0], contract.size[1], contract.size[2]];
|
|
144870
145542
|
this.handedness = (contract.handedness != null) ? contract.handedness : 1;
|
|
@@ -144893,9 +145565,12 @@ export class TerrainHeightfield {
|
|
|
144893
145565
|
return this.heights[z * this.R + x] / 65535 * this.size[1];
|
|
144894
145566
|
}
|
|
144895
145567
|
normalAt(u, v, out) {
|
|
144896
|
-
TerrainMath.SampleNormal(this.heights, this.R, this.size, u, v, out);
|
|
145568
|
+
TerrainMath.SampleNormal(this.heights, this.R, this.size, u, v, out, this.edgeNeighbors);
|
|
144897
145569
|
out[0] *= (this.handedness === 0 ? -1 : 1);
|
|
144898
145570
|
}
|
|
145571
|
+
setEdgeNeighbors(neighbors) {
|
|
145572
|
+
this.edgeNeighbors = neighbors;
|
|
145573
|
+
}
|
|
144899
145574
|
setHoles(pixels, width) {
|
|
144900
145575
|
const n = this.R - 1;
|
|
144901
145576
|
const height = (width > 0) ? Math.floor(pixels.length / 4 / width) : 0;
|
|
@@ -145174,15 +145849,31 @@ export class TerrainMath {
|
|
|
145174
145849
|
const b = h01 + (h11 - h01) * tx;
|
|
145175
145850
|
return TerrainMath.DecodeHeight(a + (b - a) * tz, sizeY);
|
|
145176
145851
|
}
|
|
145177
|
-
static SampleNormal(h, R, size, u, v, out) {
|
|
145852
|
+
static SampleNormal(h, R, size, u, v, out, neighbors = null) {
|
|
145178
145853
|
const i = Math.round(TerrainMath.Clamp01(u) * (R - 1));
|
|
145179
145854
|
const k = Math.round(TerrainMath.Clamp01(v) * (R - 1));
|
|
145180
145855
|
const iL = Math.max(i - 1, 0), iR = Math.min(i + 1, R - 1);
|
|
145181
145856
|
const kD = Math.max(k - 1, 0), kU = Math.min(k + 1, R - 1);
|
|
145182
145857
|
const cellX = size[0] / (R - 1), cellZ = size[2] / (R - 1);
|
|
145183
145858
|
const sy = size[1];
|
|
145184
|
-
|
|
145185
|
-
|
|
145859
|
+
let dhdx = (iR > iL) ? (TerrainMath.DecodeHeight(h[k * R + iR], sy) - TerrainMath.DecodeHeight(h[k * R + iL], sy)) / ((iR - iL) * cellX) : 0;
|
|
145860
|
+
let dhdz = (kU > kD) ? (TerrainMath.DecodeHeight(h[kU * R + i], sy) - TerrainMath.DecodeHeight(h[kD * R + i], sy)) / ((kU - kD) * cellZ) : 0;
|
|
145861
|
+
if (i === 0 && neighbors?.left != null) {
|
|
145862
|
+
const outsideHeight = TerrainMath.DecodeHeight(neighbors.left.heights[k * R + (R - 2)], neighbors.left.heightScale);
|
|
145863
|
+
dhdx = (TerrainMath.DecodeHeight(h[k * R + 1], sy) - outsideHeight) / (2 * cellX);
|
|
145864
|
+
}
|
|
145865
|
+
else if (i === R - 1 && neighbors?.right != null) {
|
|
145866
|
+
const outsideHeight = TerrainMath.DecodeHeight(neighbors.right.heights[k * R + 1], neighbors.right.heightScale);
|
|
145867
|
+
dhdx = (outsideHeight - TerrainMath.DecodeHeight(h[k * R + (R - 2)], sy)) / (2 * cellX);
|
|
145868
|
+
}
|
|
145869
|
+
if (k === 0 && neighbors?.bottom != null) {
|
|
145870
|
+
const outsideHeight = TerrainMath.DecodeHeight(neighbors.bottom.heights[(R - 2) * R + i], neighbors.bottom.heightScale);
|
|
145871
|
+
dhdz = (TerrainMath.DecodeHeight(h[1 * R + i], sy) - outsideHeight) / (2 * cellZ);
|
|
145872
|
+
}
|
|
145873
|
+
else if (k === R - 1 && neighbors?.top != null) {
|
|
145874
|
+
const outsideHeight = TerrainMath.DecodeHeight(neighbors.top.heights[1 * R + i], neighbors.top.heightScale);
|
|
145875
|
+
dhdz = (outsideHeight - TerrainMath.DecodeHeight(h[(R - 2) * R + i], sy)) / (2 * cellZ);
|
|
145876
|
+
}
|
|
145186
145877
|
const nx = -dhdx, ny = 1, nz = -dhdz;
|
|
145187
145878
|
const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
145188
145879
|
out[0] = nx / len;
|
|
@@ -146441,6 +147132,7 @@ export class TerrainTrees {
|
|
|
146441
147132
|
this._colliderShapes = [];
|
|
146442
147133
|
this._distance = 2000;
|
|
146443
147134
|
this._built = false;
|
|
147135
|
+
this._warnedUniqueHost = false;
|
|
146444
147136
|
this._n = 0;
|
|
146445
147137
|
this._proto = null;
|
|
146446
147138
|
this._local = null;
|
|
@@ -146533,13 +147225,21 @@ export class TerrainTrees {
|
|
|
146533
147225
|
return casters;
|
|
146534
147226
|
}
|
|
146535
147227
|
getStats() {
|
|
146536
|
-
let drawn = 0, visibleHosts = 0;
|
|
147228
|
+
let drawn = 0, visibleHosts = 0, capacity = 0;
|
|
146537
147229
|
for (let i = 0; i < this._hosts.length; i++) {
|
|
146538
|
-
|
|
146539
|
-
|
|
147230
|
+
const host = this._hosts[i];
|
|
147231
|
+
drawn += host.count;
|
|
147232
|
+
if (host.count > 0)
|
|
146540
147233
|
visibleHosts++;
|
|
147234
|
+
capacity += host.matrices.length / 16;
|
|
147235
|
+
for (let slotIndex = 0; slotIndex < host.slots.length; slotIndex++)
|
|
147236
|
+
capacity += host.slots[slotIndex].matrices.length / 16;
|
|
146541
147237
|
}
|
|
146542
|
-
return { hosts: this._hosts.length, instances: this._n, drawn: drawn, visibleHosts: visibleHosts, colliders: this._colliderBody != null };
|
|
147238
|
+
return { hosts: this._hosts.length, instances: this._n, drawn: drawn, visibleHosts: visibleHosts, colliders: this._colliderBody != null, capacity: capacity };
|
|
147239
|
+
}
|
|
147240
|
+
static PreparedHostCount(scene) {
|
|
147241
|
+
const prepared = TerrainTrees.PreparedHosts.get(scene.uniqueId);
|
|
147242
|
+
return (prepared != null) ? prepared.size : 0;
|
|
146543
147243
|
}
|
|
146544
147244
|
build() {
|
|
146545
147245
|
const owner = this._owner;
|
|
@@ -146597,43 +147297,44 @@ export class TerrainTrees {
|
|
|
146597
147297
|
this._built = true;
|
|
146598
147298
|
this._dirty = true;
|
|
146599
147299
|
}
|
|
146600
|
-
buildPrototype(
|
|
146601
|
-
const
|
|
146602
|
-
const runtime = { data:
|
|
146603
|
-
const
|
|
146604
|
-
|
|
146605
|
-
|
|
146606
|
-
|
|
146607
|
-
|
|
146608
|
-
|
|
146609
|
-
|
|
146610
|
-
|
|
147300
|
+
buildPrototype(prototype, template, instanceCount) {
|
|
147301
|
+
const prototypeLods = Array.isArray(prototype.lods) ? prototype.lods : [];
|
|
147302
|
+
const runtime = { data: prototype, heights: prototypeLods.map((prototypeLod) => (prototypeLod != null && prototypeLod.height != null) ? prototypeLod.height : 0), lodHosts: [], fade: (prototype.fademode > 0 && prototype.animatecrossfade === true) };
|
|
147303
|
+
const templateWorldInverse = Matrix.Invert(template.computeWorldMatrix(true));
|
|
147304
|
+
const initialCapacity = Math.min(instanceCount, TREE_INITIAL_CAPACITY);
|
|
147305
|
+
let hasHost = false;
|
|
147306
|
+
for (let lodIndex = 0; lodIndex < prototypeLods.length; lodIndex++) {
|
|
147307
|
+
const lodHostIndices = [];
|
|
147308
|
+
runtime.lodHosts.push(lodHostIndices);
|
|
147309
|
+
const renderers = (prototypeLods[lodIndex] != null && Array.isArray(prototypeLods[lodIndex].renderers)) ? prototypeLods[lodIndex].renderers : [];
|
|
147310
|
+
for (let rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
|
|
147311
|
+
const node = this._scene.getNodeById(renderers[rendererIndex]);
|
|
146611
147312
|
if (node == null)
|
|
146612
147313
|
continue;
|
|
146613
147314
|
let sources = [];
|
|
146614
147315
|
if (node instanceof AbstractMesh && TerrainTrees.HasGeometry(node))
|
|
146615
147316
|
sources = [node];
|
|
146616
147317
|
else
|
|
146617
|
-
sources = node.getChildMeshes(true).filter((
|
|
146618
|
-
const flags = (
|
|
146619
|
-
const probeLit = TerrainTrees.RendererUsesProbes(flags,
|
|
146620
|
-
for (let
|
|
146621
|
-
const matrices = new Float32Array(16 *
|
|
146622
|
-
const cast = !(
|
|
146623
|
-
const host = this.createHost(
|
|
147318
|
+
sources = node.getChildMeshes(true).filter((mesh) => TerrainTrees.HasGeometry(mesh));
|
|
147319
|
+
const flags = (prototypeLods[lodIndex] != null) ? prototypeLods[lodIndex].lightprobes : null;
|
|
147320
|
+
const probeLit = TerrainTrees.RendererUsesProbes(flags, rendererIndex) && TerrainProbePlugin != null;
|
|
147321
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
|
|
147322
|
+
const matrices = new Float32Array(16 * initialCapacity), fades = new Float32Array(initialCapacity);
|
|
147323
|
+
const cast = !(prototypeLods[lodIndex] != null && prototypeLods[lodIndex].billboard === true) && TerrainTrees.RendererCastsShadows(sources[sourceIndex], node);
|
|
147324
|
+
const host = this.createHost(prototype, lodIndex, sources[sourceIndex], templateWorldInverse, matrices, fades);
|
|
146624
147325
|
if (host == null)
|
|
146625
147326
|
continue;
|
|
146626
147327
|
if (probeLit)
|
|
146627
147328
|
this._probeHosts++;
|
|
146628
|
-
|
|
146629
|
-
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: [] });
|
|
147329
|
+
lodHostIndices.push(this._hosts.length);
|
|
147330
|
+
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 });
|
|
146630
147331
|
if (cast)
|
|
146631
|
-
this.createCasterSlots(this._hosts[this._hosts.length - 1],
|
|
146632
|
-
|
|
147332
|
+
this.createCasterSlots(this._hosts[this._hosts.length - 1], initialCapacity);
|
|
147333
|
+
hasHost = true;
|
|
146633
147334
|
}
|
|
146634
147335
|
}
|
|
146635
147336
|
}
|
|
146636
|
-
return
|
|
147337
|
+
return hasHost ? runtime : null;
|
|
146637
147338
|
}
|
|
146638
147339
|
static RendererUsesProbes(flags, index) {
|
|
146639
147340
|
if (!Array.isArray(flags) || index < 0 || index >= flags.length)
|
|
@@ -146641,20 +147342,32 @@ export class TerrainTrees {
|
|
|
146641
147342
|
return flags[index] !== false;
|
|
146642
147343
|
}
|
|
146643
147344
|
static AttachProbeBuffer(host, probes) {
|
|
146644
|
-
const
|
|
146645
|
-
for (let
|
|
146646
|
-
probes[
|
|
146647
|
-
const buffer =
|
|
146648
|
-
|
|
146649
|
-
|
|
146650
|
-
host.onDisposeObservable.addOnce(() => { buffer.dispose(); });
|
|
147345
|
+
const probePlugin = TerrainProbePlugin;
|
|
147346
|
+
for (let recordOffset = 0; recordOffset < probes.length; recordOffset += probePlugin.STRIDE)
|
|
147347
|
+
probes[recordOffset] = -1;
|
|
147348
|
+
const buffer = TerrainTrees.CreateProbeBuffer(host, probes);
|
|
147349
|
+
TerrainTrees.ProbeBuffers.set(host, buffer);
|
|
147350
|
+
host.onDisposeObservable.addOnce(() => { TerrainTrees.ProbeBuffers.get(host).dispose(); });
|
|
146651
147351
|
const material = host.material;
|
|
146652
|
-
const
|
|
146653
|
-
for (let i = 0; i <
|
|
146654
|
-
if (
|
|
146655
|
-
|
|
147352
|
+
const subMaterials = (material != null && material.subMaterials != null) ? material.subMaterials : [material];
|
|
147353
|
+
for (let i = 0; i < subMaterials.length; i++)
|
|
147354
|
+
if (subMaterials[i] != null)
|
|
147355
|
+
probePlugin.Attach(subMaterials[i]);
|
|
146656
147356
|
return buffer;
|
|
146657
147357
|
}
|
|
147358
|
+
static CreateProbeBuffer(host, probes) {
|
|
147359
|
+
const probePlugin = TerrainProbePlugin;
|
|
147360
|
+
const buffer = new Buffer(host.getEngine(), probes, true, probePlugin.STRIDE, false, true);
|
|
147361
|
+
for (let viewIndex = 0; viewIndex < 3; viewIndex++)
|
|
147362
|
+
host.setVerticesBuffer(buffer.createVertexBuffer(probePlugin.ATTRIBUTES[viewIndex], 4 * viewIndex, 4, probePlugin.STRIDE, true));
|
|
147363
|
+
return buffer;
|
|
147364
|
+
}
|
|
147365
|
+
static ResizeProbeBuffer(host) {
|
|
147366
|
+
const buffer = TerrainTrees.CreateProbeBuffer(host.mesh, host.probes);
|
|
147367
|
+
host.probeBuffer.dispose();
|
|
147368
|
+
host.probeBuffer = buffer;
|
|
147369
|
+
TerrainTrees.ProbeBuffers.set(host.mesh, buffer);
|
|
147370
|
+
}
|
|
146658
147371
|
sampleProbes(network) {
|
|
146659
147372
|
if (network == null || typeof network.sampleInto !== "function" || !TerrainTrees.NetworkLightsProbes(network))
|
|
146660
147373
|
return false;
|
|
@@ -146810,31 +147523,85 @@ export class TerrainTrees {
|
|
|
146810
147523
|
throw error;
|
|
146811
147524
|
}
|
|
146812
147525
|
}
|
|
146813
|
-
|
|
146814
|
-
const
|
|
146815
|
-
const
|
|
146816
|
-
if (
|
|
147526
|
+
static BakeHostGeometry(source, templateWorldInverse, name) {
|
|
147527
|
+
const geometrySource = (source instanceof InstancedMesh) ? source.sourceMesh : source;
|
|
147528
|
+
const mesh = geometrySource.clone(name, null, true);
|
|
147529
|
+
if (mesh == null)
|
|
147530
|
+
return null;
|
|
147531
|
+
mesh.metadata = null;
|
|
147532
|
+
mesh.makeGeometryUnique();
|
|
147533
|
+
const rendererToTemplate = source.computeWorldMatrix(true).multiply(templateWorldInverse);
|
|
147534
|
+
mesh.position.setAll(0);
|
|
147535
|
+
mesh.rotationQuaternion = null;
|
|
147536
|
+
mesh.rotation.setAll(0);
|
|
147537
|
+
mesh.scaling.setAll(1);
|
|
147538
|
+
mesh.bakeTransformIntoVertices(rendererToTemplate);
|
|
147539
|
+
TerrainTrees.InterleaveVertexData(mesh);
|
|
147540
|
+
return mesh;
|
|
147541
|
+
}
|
|
147542
|
+
prepareHostGeometry(prototype, lod, source, templateWorldInverse) {
|
|
147543
|
+
const scene = this._scene;
|
|
147544
|
+
const sceneKey = scene.uniqueId;
|
|
147545
|
+
let prepared = TerrainTrees.PreparedHosts.get(sceneKey);
|
|
147546
|
+
if (prepared == null) {
|
|
147547
|
+
const hosts = new Map();
|
|
147548
|
+
prepared = hosts;
|
|
147549
|
+
TerrainTrees.PreparedHosts.set(sceneKey, hosts);
|
|
147550
|
+
scene.onDisposeObservable.addOnce(() => {
|
|
147551
|
+
TerrainTrees.PreparedHosts.delete(sceneKey);
|
|
147552
|
+
hosts.forEach((mesh) => { mesh.dispose(false, false); });
|
|
147553
|
+
});
|
|
147554
|
+
}
|
|
147555
|
+
const key = prototype.templateid + "|" + lod + "|" + source.uniqueId;
|
|
147556
|
+
const existing = prepared.get(key);
|
|
147557
|
+
if (existing != null)
|
|
147558
|
+
return existing;
|
|
147559
|
+
const mesh = TerrainTrees.BakeHostGeometry(source, templateWorldInverse, prototype.name + ".L" + lod + "." + source.name + ".Prepared");
|
|
147560
|
+
if (mesh == null)
|
|
146817
147561
|
return null;
|
|
146818
|
-
|
|
146819
|
-
|
|
146820
|
-
|
|
146821
|
-
|
|
146822
|
-
|
|
146823
|
-
|
|
146824
|
-
|
|
146825
|
-
|
|
147562
|
+
mesh.setEnabled(false);
|
|
147563
|
+
mesh.isVisible = false;
|
|
147564
|
+
mesh.isPickable = false;
|
|
147565
|
+
prepared.set(key, mesh);
|
|
147566
|
+
return mesh;
|
|
147567
|
+
}
|
|
147568
|
+
createUniqueHost(source, templateWorldInverse, name, reason) {
|
|
147569
|
+
if (!this._warnedUniqueHost) {
|
|
147570
|
+
this._warnedUniqueHost = true;
|
|
147571
|
+
Tools.Warn("[TerrainBuilder] " + this._owner.transform.name + ": tree hosts cannot share gpu buffers, copying geometry per tile — " + reason);
|
|
147572
|
+
}
|
|
147573
|
+
const host = TerrainTrees.BakeHostGeometry(source, templateWorldInverse, name);
|
|
147574
|
+
if (host != null)
|
|
147575
|
+
host.setEnabled(true);
|
|
147576
|
+
return host;
|
|
147577
|
+
}
|
|
147578
|
+
createHost(prototype, lod, source, templateWorldInverse, matrices, fades) {
|
|
147579
|
+
const prepared = this.prepareHostGeometry(prototype, lod, source, templateWorldInverse);
|
|
147580
|
+
if (prepared == null)
|
|
147581
|
+
return null;
|
|
147582
|
+
const name = prototype.name + ".L" + lod + "." + source.name;
|
|
147583
|
+
let host = null;
|
|
147584
|
+
try {
|
|
147585
|
+
host = TerrainTrees.CreateSharedHost(this._scene, prepared, name);
|
|
147586
|
+
for (const key of TerrainTrees.HOST_DRAW_STATE)
|
|
147587
|
+
host[key] = prepared[key];
|
|
147588
|
+
}
|
|
147589
|
+
catch (error) {
|
|
147590
|
+
host = this.createUniqueHost(source, templateWorldInverse, name, String(error));
|
|
147591
|
+
if (host == null)
|
|
147592
|
+
return null;
|
|
147593
|
+
}
|
|
147594
|
+
const sourceMaterial = prepared.material;
|
|
146826
147595
|
host.parent = this._root;
|
|
146827
|
-
host.setEnabled(true);
|
|
146828
147596
|
host.isVisible = false;
|
|
146829
147597
|
host.isPickable = false;
|
|
146830
147598
|
host.receiveShadows = true;
|
|
146831
147599
|
host.alwaysSelectAsActiveMesh = true;
|
|
146832
147600
|
host.doNotSyncBoundingInfo = true;
|
|
146833
|
-
TerrainTrees.InterleaveVertexData(host);
|
|
146834
147601
|
host.thinInstanceSetBuffer("matrix", matrices, 16, false);
|
|
146835
147602
|
host.thinInstanceSetBuffer("tkLodFade", fades, 1, false);
|
|
146836
147603
|
host.thinInstanceCount = 0;
|
|
146837
|
-
host.material = this.hostMaterial(
|
|
147604
|
+
host.material = this.hostMaterial(prototype, lod, sourceMaterial, TerrainTrees.HasSpeedTreeWindVertexData(prepared));
|
|
146838
147605
|
if (ShaderGraphRuntime != null)
|
|
146839
147606
|
ShaderGraphRuntime.EnsureThinInstanceBuffers(host);
|
|
146840
147607
|
return host;
|
|
@@ -146862,7 +147629,7 @@ export class TerrainTrees {
|
|
|
146862
147629
|
ShaderGraphRuntime.EnsureThinInstanceBuffers(mesh);
|
|
146863
147630
|
if (TerrainBuilder != null)
|
|
146864
147631
|
TerrainBuilder.SetCasterSlot(mesh, slotIndex);
|
|
146865
|
-
slots.push({ mesh: mesh, matrices: matrices, fades: fades, probes: null, count: 0, lastCount: -1, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3() });
|
|
147632
|
+
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 });
|
|
146866
147633
|
}
|
|
146867
147634
|
host.slots = slots;
|
|
146868
147635
|
}
|
|
@@ -146903,7 +147670,8 @@ export class TerrainTrees {
|
|
|
146903
147670
|
const hue = (info != null && Array.isArray(info.hue) && info.hue.length >= 4) ? info.hue : null;
|
|
146904
147671
|
const F = TerrainFoliagePlugin;
|
|
146905
147672
|
const urp = (this._owner.contract != null && (this._owner.contract.flavour || "urp") === "urp");
|
|
146906
|
-
const
|
|
147673
|
+
const graphMoves = p.speedtree !== true && F.GraphOwnsMotion(source);
|
|
147674
|
+
const bend = (ownWind || graphMoves) ? 0 : F.MODE_TREE;
|
|
146907
147675
|
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));
|
|
146908
147676
|
if (plugin != null) {
|
|
146909
147677
|
plugin.params.bendFactor = (p.bendfactor != null) ? p.bendfactor : 0;
|
|
@@ -147608,7 +148376,7 @@ export class TerrainTrees {
|
|
|
147608
148376
|
}
|
|
147609
148377
|
appendInstance(target, instanceIndex, fade) {
|
|
147610
148378
|
const count = target.count;
|
|
147611
|
-
if (16 * (count + 1) > target.matrices.length)
|
|
148379
|
+
if (16 * (count + 1) > target.matrices.length && !TerrainTrees.GrowTarget(target, target.capacityCeiling))
|
|
147612
148380
|
return;
|
|
147613
148381
|
const sourceOffset = 16 * instanceIndex;
|
|
147614
148382
|
const targetOffset = 16 * count;
|
|
@@ -147646,6 +148414,26 @@ export class TerrainTrees {
|
|
|
147646
148414
|
if (z + radius > box[5])
|
|
147647
148415
|
box[5] = z + radius;
|
|
147648
148416
|
}
|
|
148417
|
+
static GrowTarget(target, ceiling) {
|
|
148418
|
+
const capacity = target.matrices.length / 16;
|
|
148419
|
+
if (capacity >= ceiling)
|
|
148420
|
+
return false;
|
|
148421
|
+
const grownCapacity = Math.min(ceiling, Math.max(target.count + 1, Math.ceil(capacity * TREE_GROWTH_FACTOR)));
|
|
148422
|
+
const matrices = new Float32Array(16 * grownCapacity);
|
|
148423
|
+
matrices.set(target.matrices.subarray(0, 16 * target.count));
|
|
148424
|
+
target.matrices = matrices;
|
|
148425
|
+
const fades = new Float32Array(grownCapacity);
|
|
148426
|
+
fades.set(target.fades.subarray(0, target.count));
|
|
148427
|
+
target.fades = fades;
|
|
148428
|
+
if (target.probes != null) {
|
|
148429
|
+
const stride = TerrainProbePlugin.STRIDE;
|
|
148430
|
+
const probes = new Float32Array(stride * grownCapacity);
|
|
148431
|
+
probes.set(target.probes.subarray(0, stride * target.count));
|
|
148432
|
+
target.probes = probes;
|
|
148433
|
+
}
|
|
148434
|
+
target.grown = true;
|
|
148435
|
+
return true;
|
|
148436
|
+
}
|
|
147649
148437
|
static LodCasts(hosts, proto, lod) {
|
|
147650
148438
|
if (lod < 0 || lod >= proto.lodHosts.length)
|
|
147651
148439
|
return false;
|
|
@@ -147683,16 +148471,39 @@ export class TerrainTrees {
|
|
|
147683
148471
|
}
|
|
147684
148472
|
}
|
|
147685
148473
|
}
|
|
148474
|
+
static RebindGrownTarget(target) {
|
|
148475
|
+
const mesh = target.mesh;
|
|
148476
|
+
mesh.thinInstanceSetBuffer("matrix", target.matrices, 16, false);
|
|
148477
|
+
TerrainTrees.ResizePreviousMatrices(mesh, target.matrices);
|
|
148478
|
+
mesh.thinInstanceSetBuffer("tkLodFade", target.fades, 1, false);
|
|
148479
|
+
if (target.probes != null)
|
|
148480
|
+
TerrainTrees.ResizeProbeBuffer(target);
|
|
148481
|
+
if (ShaderGraphRuntime != null)
|
|
148482
|
+
ShaderGraphRuntime.EnsureThinInstanceBuffers(mesh);
|
|
148483
|
+
target.grown = false;
|
|
148484
|
+
}
|
|
148485
|
+
static ResizePreviousMatrices(mesh, matrices) {
|
|
148486
|
+
const storage = mesh._thinInstanceDataStorage;
|
|
148487
|
+
if (storage == null || storage.previousMatrixBuffer == null || storage.previousMatrixData != null)
|
|
148488
|
+
return;
|
|
148489
|
+
storage.previousMatrixBuffer.dispose();
|
|
148490
|
+
storage.previousMatrixBuffer = mesh._thinInstanceCreateMatrixBuffer("previousWorld", matrices, false);
|
|
148491
|
+
}
|
|
147686
148492
|
uploadTarget(target, probeBuffer, isSlot) {
|
|
147687
148493
|
const mesh = target.mesh;
|
|
147688
148494
|
if (target.count > 0) {
|
|
147689
148495
|
if (isSlot && target.lastCount <= 0)
|
|
147690
148496
|
mesh.setEnabled(true);
|
|
148497
|
+
const grown = target.grown;
|
|
148498
|
+
if (grown)
|
|
148499
|
+
TerrainTrees.RebindGrownTarget(target);
|
|
147691
148500
|
mesh.thinInstanceCount = target.count;
|
|
147692
|
-
|
|
147693
|
-
|
|
147694
|
-
|
|
147695
|
-
probeBuffer
|
|
148501
|
+
if (!grown) {
|
|
148502
|
+
mesh.thinInstancePartialBufferUpdate("matrix", target.count, 0);
|
|
148503
|
+
mesh.thinInstancePartialBufferUpdate("tkLodFade", target.count, 0);
|
|
148504
|
+
if (probeBuffer != null)
|
|
148505
|
+
probeBuffer.updateDirectly(target.probes, 0, target.count);
|
|
148506
|
+
}
|
|
147696
148507
|
target.boxMin.set(target.box[0], target.box[1], target.box[2]);
|
|
147697
148508
|
target.boxMax.set(target.box[3], target.box[4], target.box[5]);
|
|
147698
148509
|
mesh.getBoundingInfo().reConstruct(target.boxMin, target.boxMax, mesh.computeWorldMatrix(isSlot));
|
|
@@ -147816,6 +148627,9 @@ TerrainTrees.CASTER_SLOTS = 4;
|
|
|
147816
148627
|
TerrainTrees.SLOT_MARGIN = 4;
|
|
147817
148628
|
TerrainTrees.NO_PROBE_RECORD = -1;
|
|
147818
148629
|
TerrainTrees.LIGHT_TURN_DOT = 0.99996192;
|
|
148630
|
+
TerrainTrees.HOST_DRAW_STATE = ["sideOrientation", "renderingGroupId", "layerMask", "alphaIndex", "hasVertexAlpha", "useVertexColors", "numBoneInfluencers", "skeleton"];
|
|
148631
|
+
TerrainTrees.PreparedHosts = new Map();
|
|
148632
|
+
TerrainTrees.ProbeBuffers = new WeakMap();
|
|
147819
148633
|
TerrainTrees._materials = new Map();
|
|
147820
148634
|
TerrainTrees.ProbeSampleHeight = 0;
|
|
147821
148635
|
TerrainTrees.SUBSURFACE_FIELDS = ["isTranslucencyEnabled", "isScatteringEnabled", "translucencyIntensity", "legacyTranslucency", "useGltfStyleTextures",
|
|
@@ -166744,6 +167558,1531 @@ export class VisualEffectSpawner {
|
|
|
166744
167558
|
}
|
|
166745
167559
|
VisualEffectSpawner.COUNT_EPSILON = 1e-6;
|
|
166746
167560
|
VisualEffectSpawner.MAX_PHASE_CHANGES_PER_STEP = 64;
|
|
167561
|
+
export class VolumetricCloudsMath {
|
|
167562
|
+
static ShellInterval(rayOrigin, rayDirection, innerRadius, outerRadius, planetRadius) {
|
|
167563
|
+
const radialDistance = Math.hypot(rayOrigin[0], rayOrigin[1], rayOrigin[2]);
|
|
167564
|
+
if (radialDistance <= 0)
|
|
167565
|
+
return null;
|
|
167566
|
+
const cosChi = (rayOrigin[0] * rayDirection[0] + rayOrigin[1] * rayDirection[1] + rayOrigin[2] * rayDirection[2]) / radialDistance;
|
|
167567
|
+
const inner = VolumetricCloudsMath.IntersectSphere(innerRadius, cosChi, radialDistance);
|
|
167568
|
+
const outer = VolumetricCloudsMath.IntersectSphere(outerRadius, cosChi, radialDistance);
|
|
167569
|
+
if (inner[0] < 0 && inner[1] >= 0) {
|
|
167570
|
+
const radius = (typeof planetRadius === "number") ? planetRadius : innerRadius;
|
|
167571
|
+
const sinHorizon = Math.min(1, radius / radialDistance);
|
|
167572
|
+
const horizonCosine = -Math.sqrt(Math.max(0, 1 - sinHorizon * sinHorizon));
|
|
167573
|
+
return (cosChi >= horizonCosine) ? [inner[1], outer[1]] : null;
|
|
167574
|
+
}
|
|
167575
|
+
if (outer[1] < 0)
|
|
167576
|
+
return null;
|
|
167577
|
+
return [Math.max(outer[0], 0), (inner[0] >= 0) ? inner[0] : outer[1]];
|
|
167578
|
+
}
|
|
167579
|
+
static StepSize(shellLength, steps, maximumStep) {
|
|
167580
|
+
return Math.min(shellLength / Math.max(1, steps), maximumStep);
|
|
167581
|
+
}
|
|
167582
|
+
static MaximumStep(altitudeRange, authoredSteps, tierSteps) {
|
|
167583
|
+
const stretch = (typeof authoredSteps === "number" && tierSteps > 0) ? Math.min(Math.max(1, authoredSteps / tierSteps), CLOUD_MAX_STEP_STRETCH) : 1;
|
|
167584
|
+
return altitudeRange / CLOUD_MAX_STEP_DIVISOR * stretch;
|
|
167585
|
+
}
|
|
167586
|
+
static TierSettings(tier, authored) {
|
|
167587
|
+
const block = authored || {};
|
|
167588
|
+
const positiveOr = (value, fallback) => (typeof value === "number" && value > 0) ? value : fallback;
|
|
167589
|
+
const caps = CLOUD_TIER_CAPS[Math.max(0, Math.min(VolumetricCloudsMath.LowestTier, Math.floor(tier)))];
|
|
167590
|
+
return {
|
|
167591
|
+
resolutionScale: Math.min(positiveOr(block.resolutionscale, CLOUD_DEFAULT_SETTINGS.resolutionScale), caps.resolutionScale),
|
|
167592
|
+
primarySteps: Math.min(positiveOr(block.numprimarysteps, CLOUD_DEFAULT_SETTINGS.primarySteps), caps.primarySteps),
|
|
167593
|
+
longRayMultiplier: Math.min(positiveOr(block.longraysamplemultiplier, CLOUD_DEFAULT_SETTINGS.longRayMultiplier), caps.longRayMultiplier),
|
|
167594
|
+
lightSteps: Math.min(positiveOr(block.numlightsteps, CLOUD_DEFAULT_SETTINGS.lightSteps), caps.lightSteps)
|
|
167595
|
+
};
|
|
167596
|
+
}
|
|
167597
|
+
static StartTier(maximumTextureSize, viewportCssWidth) {
|
|
167598
|
+
if (maximumTextureSize < CLOUD_WEAK_GPU_TEXTURE_SIZE || viewportCssWidth < CLOUD_PHONE_WIDTH_CSS_PX)
|
|
167599
|
+
return 1;
|
|
167600
|
+
return 0;
|
|
167601
|
+
}
|
|
167602
|
+
static IsCameraCut(previousPosition, currentPosition) {
|
|
167603
|
+
if (previousPosition == null)
|
|
167604
|
+
return true;
|
|
167605
|
+
const distance = Math.hypot(currentPosition[0] - previousPosition[0], currentPosition[1] - previousPosition[1], currentPosition[2] - previousPosition[2]);
|
|
167606
|
+
return distance > CLOUD_CAMERA_CUT_METERS;
|
|
167607
|
+
}
|
|
167608
|
+
static PackCurves(density, erosion, ambientOcclusion) {
|
|
167609
|
+
const texels = new Float32Array(CLOUD_CURVE_SAMPLES * 4);
|
|
167610
|
+
const hasCurves = Array.isArray(density) && density.length >= CLOUD_CURVE_SAMPLES;
|
|
167611
|
+
const clamp01 = (value) => Math.max(0, Math.min(1, (typeof value === "number") ? value : 0));
|
|
167612
|
+
for (let i = 0; i < CLOUD_CURVE_SAMPLES; i++) {
|
|
167613
|
+
const isEnd = (i === 0 || i === CLOUD_CURVE_SAMPLES - 1);
|
|
167614
|
+
texels[i * 4] = !hasCurves ? 1 : (isEnd ? 0 : clamp01(density[i]));
|
|
167615
|
+
texels[i * 4 + 1] = (!hasCurves || !Array.isArray(erosion)) ? 1 : clamp01(erosion[i]);
|
|
167616
|
+
texels[i * 4 + 2] = (!hasCurves || !Array.isArray(ambientOcclusion)) ? 1 : clamp01(1 - ambientOcclusion[i]);
|
|
167617
|
+
texels[i * 4 + 3] = 1;
|
|
167618
|
+
}
|
|
167619
|
+
return texels;
|
|
167620
|
+
}
|
|
167621
|
+
static IntersectSphere(sphereRadius, cosChi, radialDistance) {
|
|
167622
|
+
const ratio = sphereRadius / radialDistance;
|
|
167623
|
+
const discriminant = ratio * ratio - Math.max(0, Math.min(1, 1 - cosChi * cosChi));
|
|
167624
|
+
if (discriminant < 0)
|
|
167625
|
+
return [discriminant, discriminant];
|
|
167626
|
+
const root = Math.sqrt(discriminant);
|
|
167627
|
+
return [radialDistance * (-cosChi - root), radialDistance * (-cosChi + root)];
|
|
167628
|
+
}
|
|
167629
|
+
}
|
|
167630
|
+
VolumetricCloudsMath.LowestTier = 2;
|
|
167631
|
+
export class VolumetricCloudsTier {
|
|
167632
|
+
constructor(startTier) {
|
|
167633
|
+
this.isSettled = false;
|
|
167634
|
+
this.windowMilliseconds = 0;
|
|
167635
|
+
this.windowFrames = 0;
|
|
167636
|
+
this.meanBeforeStep = 0;
|
|
167637
|
+
this.previousMean = 0;
|
|
167638
|
+
this.tier = Math.max(0, Math.min(VolumetricCloudsMath.LowestTier, startTier));
|
|
167639
|
+
}
|
|
167640
|
+
addFrame(frameMilliseconds) {
|
|
167641
|
+
this.windowMilliseconds += frameMilliseconds;
|
|
167642
|
+
this.windowFrames++;
|
|
167643
|
+
if (this.windowFrames < CLOUD_TIER_WINDOW_FRAMES)
|
|
167644
|
+
return false;
|
|
167645
|
+
const meanMilliseconds = this.windowMilliseconds / this.windowFrames;
|
|
167646
|
+
this.windowMilliseconds = 0;
|
|
167647
|
+
this.windowFrames = 0;
|
|
167648
|
+
if (this.meanBeforeStep > 0) {
|
|
167649
|
+
const savedEnough = meanMilliseconds <= this.meanBeforeStep * (1 - CLOUD_TIER_MINIMUM_SAVING);
|
|
167650
|
+
this.meanBeforeStep = 0;
|
|
167651
|
+
if (!savedEnough) {
|
|
167652
|
+
this.tier--;
|
|
167653
|
+
this.isSettled = true;
|
|
167654
|
+
return true;
|
|
167655
|
+
}
|
|
167656
|
+
}
|
|
167657
|
+
const previousMean = this.previousMean;
|
|
167658
|
+
this.previousMean = meanMilliseconds;
|
|
167659
|
+
const isSteady = previousMean > 0 && meanMilliseconds > previousMean * (1 - CLOUD_TIER_MINIMUM_SAVING);
|
|
167660
|
+
if (this.isSettled || !isSteady || meanMilliseconds <= CLOUD_TIER_BUDGET_MS || this.tier >= VolumetricCloudsMath.LowestTier)
|
|
167661
|
+
return false;
|
|
167662
|
+
this.tier++;
|
|
167663
|
+
this.meanBeforeStep = meanMilliseconds;
|
|
167664
|
+
return true;
|
|
167665
|
+
}
|
|
167666
|
+
}
|
|
167667
|
+
export class VolumetricClouds {
|
|
167668
|
+
static TryCreate(scene, metadata, root) {
|
|
167669
|
+
const block = PhysicalSkyBlock.UrpBlock(metadata);
|
|
167670
|
+
const clouds = (block != null) ? block.clouds : null;
|
|
167671
|
+
if (clouds == null || clouds.state === false || scene == null)
|
|
167672
|
+
return null;
|
|
167673
|
+
if (HdrpPhysicallyBasedSky == null || !HdrpPhysicallyBasedSky.IsSupported(scene.getEngine()))
|
|
167674
|
+
return null;
|
|
167675
|
+
const existing = VolumetricClouds.Get(scene);
|
|
167676
|
+
if (existing != null)
|
|
167677
|
+
return existing;
|
|
167678
|
+
return new VolumetricClouds(scene, clouds, block.sky, (typeof root === "string") ? root : "");
|
|
167679
|
+
}
|
|
167680
|
+
static Get(scene) {
|
|
167681
|
+
if (scene == null)
|
|
167682
|
+
return null;
|
|
167683
|
+
const clouds = scene._tkVolumetricClouds;
|
|
167684
|
+
return (clouds != null && clouds.disposed !== true) ? clouds : null;
|
|
167685
|
+
}
|
|
167686
|
+
constructor(scene, block, sky, root) {
|
|
167687
|
+
this.disposed = false;
|
|
167688
|
+
this.tier = 0;
|
|
167689
|
+
this.hasAerial = false;
|
|
167690
|
+
this.placeholderAerial = null;
|
|
167691
|
+
this.worleyTexture = null;
|
|
167692
|
+
this.erosionTexture = null;
|
|
167693
|
+
this.curveTexture = null;
|
|
167694
|
+
this.placeholderDepth = null;
|
|
167695
|
+
this.marchTarget = null;
|
|
167696
|
+
this.renderer = null;
|
|
167697
|
+
this.marchPass = null;
|
|
167698
|
+
this.temporalPass = null;
|
|
167699
|
+
this.historyTargets = [];
|
|
167700
|
+
this.historyIndex = 0;
|
|
167701
|
+
this.hasHistory = false;
|
|
167702
|
+
this.compositeMesh = null;
|
|
167703
|
+
this.compositeMaterial = null;
|
|
167704
|
+
this.isReady = false;
|
|
167705
|
+
this.isPending = false;
|
|
167706
|
+
this.preExposure = 1;
|
|
167707
|
+
this.preExposureBinding = null;
|
|
167708
|
+
this.frameSeed = 0;
|
|
167709
|
+
this.windOffset = [0, 0];
|
|
167710
|
+
this.verticalShapeOffset = 0;
|
|
167711
|
+
this.verticalErosionOffset = 0;
|
|
167712
|
+
this.sceneDepth = null;
|
|
167713
|
+
this.beforeRenderObserver = null;
|
|
167714
|
+
this.afterRenderObserver = null;
|
|
167715
|
+
this.tierTracker = null;
|
|
167716
|
+
this.targetWidth = 1;
|
|
167717
|
+
this.targetHeight = 1;
|
|
167718
|
+
this.lastCamera = null;
|
|
167719
|
+
this.sunLight = null;
|
|
167720
|
+
this.ambientSource = null;
|
|
167721
|
+
this.ambientCaptureCount = -1;
|
|
167722
|
+
this.previousEye = [0, 0, 0];
|
|
167723
|
+
this.currentEye = [0, 0, 0];
|
|
167724
|
+
this.hasPreviousEye = false;
|
|
167725
|
+
this.beforeTargetsObserver = null;
|
|
167726
|
+
this.afterTargetsObserver = null;
|
|
167727
|
+
this.disposeObserver = null;
|
|
167728
|
+
this.mirror = Matrix.Identity();
|
|
167729
|
+
this.inverseViewProjection = new Matrix();
|
|
167730
|
+
this.previousViewProjection = new Matrix();
|
|
167731
|
+
this.previousViewProjectionUnity = new Matrix();
|
|
167732
|
+
this.sunDirection = new Vector3();
|
|
167733
|
+
this.cameraForward = new Vector3();
|
|
167734
|
+
this.ambientTop = new Color3();
|
|
167735
|
+
this.ambientBottom = new Color3();
|
|
167736
|
+
this.scene = scene;
|
|
167737
|
+
this.block = block;
|
|
167738
|
+
this.sky = sky;
|
|
167739
|
+
this.root = root;
|
|
167740
|
+
scene._tkVolumetricClouds = this;
|
|
167741
|
+
const planet = (sky != null) ? sky.planet : null;
|
|
167742
|
+
this.isLocal = (planet != null && typeof planet.renderingspace === "number") ? planet.renderingspace === 1 : block.localclouds === true;
|
|
167743
|
+
const curvature = (typeof block.earthcurvature === "number") ? block.earthcurvature : 0;
|
|
167744
|
+
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;
|
|
167745
|
+
this.planetCenter = (this.isLocal && planet != null && Array.isArray(planet.center)) ? Vector3.FromArray(planet.center) : new Vector3(0, -this.planetRadius, 0);
|
|
167746
|
+
this.scatteringTint = VolumetricClouds.LinearScatteringTint(block.scatteringtint);
|
|
167747
|
+
if (scene.useRightHandedSystem === true)
|
|
167748
|
+
Matrix.ScalingToRef(-1, 1, 1, this.mirror);
|
|
167749
|
+
this.localForward = new Vector3(0, 0, (scene.useRightHandedSystem === true) ? -1 : 1);
|
|
167750
|
+
this.tier = VolumetricCloudsMath.StartTier(scene.getEngine().getCaps().maxTextureSize, VolumetricClouds.CanvasCssWidth(scene));
|
|
167751
|
+
this.tierTracker = new VolumetricCloudsTier(this.tier);
|
|
167752
|
+
this.settings = VolumetricCloudsMath.TierSettings(this.tier, block);
|
|
167753
|
+
this.reportLoweredSettings();
|
|
167754
|
+
this.isPending = true;
|
|
167755
|
+
scene.addPendingData(this);
|
|
167756
|
+
this.preExposureBinding = HdrpRendering.Bind(scene, 1, 1, (scaled) => { this.preExposure = scaled; });
|
|
167757
|
+
this.disposeObserver = scene.onDisposeObservable.add(() => this.dispose());
|
|
167758
|
+
this.createPasses();
|
|
167759
|
+
this.loadNoise().then((loaded) => {
|
|
167760
|
+
if (this.disposed)
|
|
167761
|
+
return;
|
|
167762
|
+
if (!loaded) {
|
|
167763
|
+
this.releasePending();
|
|
167764
|
+
return;
|
|
167765
|
+
}
|
|
167766
|
+
this.waitUntilReady();
|
|
167767
|
+
});
|
|
167768
|
+
}
|
|
167769
|
+
dispose() {
|
|
167770
|
+
if (this.disposed)
|
|
167771
|
+
return;
|
|
167772
|
+
this.disposed = true;
|
|
167773
|
+
const scene = this.scene;
|
|
167774
|
+
this.releasePending();
|
|
167775
|
+
if (this.beforeRenderObserver != null)
|
|
167776
|
+
scene.onBeforeRenderObservable.remove(this.beforeRenderObserver);
|
|
167777
|
+
if (this.afterRenderObserver != null)
|
|
167778
|
+
scene.onAfterRenderObservable.remove(this.afterRenderObserver);
|
|
167779
|
+
if (this.beforeTargetsObserver != null)
|
|
167780
|
+
scene.onBeforeRenderTargetsRenderObservable.remove(this.beforeTargetsObserver);
|
|
167781
|
+
if (this.afterTargetsObserver != null)
|
|
167782
|
+
scene.onAfterRenderTargetsRenderObservable.remove(this.afterTargetsObserver);
|
|
167783
|
+
if (this.disposeObserver != null)
|
|
167784
|
+
scene.onDisposeObservable.remove(this.disposeObserver);
|
|
167785
|
+
if (this.preExposureBinding != null)
|
|
167786
|
+
HdrpRendering.Unbind(scene, this.preExposureBinding);
|
|
167787
|
+
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);
|
|
167788
|
+
for (const disposable of disposables) {
|
|
167789
|
+
try {
|
|
167790
|
+
if (disposable != null)
|
|
167791
|
+
disposable.dispose();
|
|
167792
|
+
}
|
|
167793
|
+
catch (error) { }
|
|
167794
|
+
}
|
|
167795
|
+
const sceneWithClouds = scene;
|
|
167796
|
+
if (sceneWithClouds._tkVolumetricClouds === this)
|
|
167797
|
+
sceneWithClouds._tkVolumetricClouds = null;
|
|
167798
|
+
}
|
|
167799
|
+
createPasses() {
|
|
167800
|
+
const scene = this.scene;
|
|
167801
|
+
const engine = scene.getEngine();
|
|
167802
|
+
const isWebGPU = engine.isWebGPU === true;
|
|
167803
|
+
const language = isWebGPU ? ShaderLanguage.WGSL : ShaderLanguage.GLSL;
|
|
167804
|
+
const defines = [];
|
|
167805
|
+
if (this.isLocal)
|
|
167806
|
+
defines.push("#define CLOUD_LOCAL");
|
|
167807
|
+
if (this.block.microerosion === true && this.block.microerosionfactor > 0)
|
|
167808
|
+
defines.push("#define CLOUD_MICRO_EROSION");
|
|
167809
|
+
const liveSky = HdrpPhysicallyBasedSky.Get(scene);
|
|
167810
|
+
this.hasAerial = liveSky != null && HdrpFogMath != null && HdrpFogMath.HasAerialPerspective(liveSky);
|
|
167811
|
+
const marchUniforms = VolumetricCloudsShaders.MarchUniforms.slice();
|
|
167812
|
+
const marchSamplers = VolumetricCloudsShaders.MarchSamplers.slice();
|
|
167813
|
+
if (this.hasAerial) {
|
|
167814
|
+
defines.push("#define CLOUD_AERIAL");
|
|
167815
|
+
marchUniforms.push(VolumetricCloudsShaders.AerialCameraUniform, ...HdrpPbSkyShaders.AtmosphereUniforms);
|
|
167816
|
+
marchSamplers.push(VolumetricCloudsShaders.AerialSampler);
|
|
167817
|
+
this.placeholderAerial = new RawTexture2DArray(new Uint8Array(4), 1, 1, 1, Constants.TEXTUREFORMAT_RGBA, scene, false, false, Texture.NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
|
|
167818
|
+
this.placeholderAerial.name = "Volumetric Clouds Aerial Placeholder";
|
|
167819
|
+
}
|
|
167820
|
+
VolumetricCloudsShaders.Register();
|
|
167821
|
+
this.curveTexture = this.createCurveTexture();
|
|
167822
|
+
this.placeholderDepth = new RawTexture(new Float32Array([0]), 1, 1, Constants.TEXTUREFORMAT_R, scene, false, false, Texture.NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
|
|
167823
|
+
this.placeholderDepth.name = "Volumetric Clouds Depth Placeholder";
|
|
167824
|
+
const textureType = (engine.getCaps().textureHalfFloatRender === true) ? Constants.TEXTURETYPE_HALF_FLOAT : Constants.TEXTURETYPE_FLOAT;
|
|
167825
|
+
this.updateTargetSize();
|
|
167826
|
+
this.marchTarget = this.createCloudTarget("Volumetric Clouds Lighting", textureType);
|
|
167827
|
+
this.historyTargets.push(this.createCloudTarget("Volumetric Clouds History A", textureType), this.createCloudTarget("Volumetric Clouds History B", textureType));
|
|
167828
|
+
this.renderer = new EffectRenderer(engine);
|
|
167829
|
+
this.marchPass = new EffectWrapper({
|
|
167830
|
+
engine: engine, name: VolumetricCloudsShaders.MarchName, fragmentShader: VolumetricCloudsShaders.MarchName, useShaderStore: true,
|
|
167831
|
+
uniformNames: marchUniforms, samplerNames: marchSamplers,
|
|
167832
|
+
defines: defines.join("\n"), shaderLanguage: language
|
|
167833
|
+
});
|
|
167834
|
+
this.marchPass.onApplyObservable.add(() => this.bindMarch(this.marchPass.effect));
|
|
167835
|
+
this.temporalPass = new EffectWrapper({
|
|
167836
|
+
engine: engine, name: VolumetricCloudsShaders.TemporalName, fragmentShader: VolumetricCloudsShaders.TemporalName, useShaderStore: true,
|
|
167837
|
+
uniformNames: VolumetricCloudsShaders.TemporalUniforms.slice(), samplerNames: VolumetricCloudsShaders.TemporalSamplers.slice(),
|
|
167838
|
+
defines: defines.join("\n"), shaderLanguage: language
|
|
167839
|
+
});
|
|
167840
|
+
this.temporalPass.onApplyObservable.add(() => this.bindTemporal(this.temporalPass.effect));
|
|
167841
|
+
this.createCompositeMesh(language);
|
|
167842
|
+
}
|
|
167843
|
+
createCloudTarget(name, textureType) {
|
|
167844
|
+
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);
|
|
167845
|
+
target.wrapU = Texture.CLAMP_ADDRESSMODE;
|
|
167846
|
+
target.wrapV = Texture.CLAMP_ADDRESSMODE;
|
|
167847
|
+
target.anisotropicFilteringLevel = 1;
|
|
167848
|
+
return target;
|
|
167849
|
+
}
|
|
167850
|
+
createCurveTexture() {
|
|
167851
|
+
const curves = this.block.curves || {};
|
|
167852
|
+
const texels = VolumetricCloudsMath.PackCurves(curves.density, curves.erosion, curves.ambientocclusion);
|
|
167853
|
+
const halfTexels = new Uint16Array(texels.length);
|
|
167854
|
+
for (let i = 0; i < texels.length; i++)
|
|
167855
|
+
halfTexels[i] = TextureTools.ToHalfFloat(texels[i]);
|
|
167856
|
+
const texture = new RawTexture(halfTexels, CLOUD_CURVE_SAMPLES, 1, Constants.TEXTUREFORMAT_RGBA, this.scene, false, false, Texture.BILINEAR_SAMPLINGMODE, Constants.TEXTURETYPE_HALF_FLOAT);
|
|
167857
|
+
texture.name = "Volumetric Clouds Curves";
|
|
167858
|
+
texture.wrapU = Texture.WRAP_ADDRESSMODE;
|
|
167859
|
+
texture.wrapV = Texture.CLAMP_ADDRESSMODE;
|
|
167860
|
+
return texture;
|
|
167861
|
+
}
|
|
167862
|
+
createCompositeMesh(language) {
|
|
167863
|
+
const scene = this.scene;
|
|
167864
|
+
const mesh = new Mesh(VolumetricClouds.MeshName, scene);
|
|
167865
|
+
const vertexData = new VertexData();
|
|
167866
|
+
vertexData.positions = [-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0];
|
|
167867
|
+
vertexData.indices = [0, 1, 2, 0, 2, 3];
|
|
167868
|
+
vertexData.applyToMesh(mesh, false);
|
|
167869
|
+
const material = new ShaderMaterial("Volumetric Clouds Composite", scene, { vertex: VolumetricCloudsShaders.CompositeName, fragment: VolumetricCloudsShaders.CompositeName }, { attributes: ["position"], uniforms: ["cloudDisplayEncoded"], samplers: ["cloudLighting"], needAlphaBlending: true, shaderLanguage: language });
|
|
167870
|
+
material.alphaMode = Constants.ALPHA_PREMULTIPLIED;
|
|
167871
|
+
material.disableDepthWrite = true;
|
|
167872
|
+
material.depthFunction = Constants.ALWAYS;
|
|
167873
|
+
material.backFaceCulling = false;
|
|
167874
|
+
material.setTexture("cloudLighting", this.marchTarget);
|
|
167875
|
+
material.setFloat("cloudDisplayEncoded", this.isDisplayEncoded() ? 1 : 0);
|
|
167876
|
+
mesh.material = material;
|
|
167877
|
+
mesh.renderingGroupId = Utilities.DefaultRenderGroup();
|
|
167878
|
+
mesh.alphaIndex = CLOUD_ALPHA_INDEX;
|
|
167879
|
+
mesh.alwaysSelectAsActiveMesh = true;
|
|
167880
|
+
mesh.isPickable = false;
|
|
167881
|
+
mesh.doNotSyncBoundingInfo = true;
|
|
167882
|
+
mesh.applyFog = false;
|
|
167883
|
+
mesh.setEnabled(false);
|
|
167884
|
+
this.compositeMesh = mesh;
|
|
167885
|
+
this.compositeMaterial = material;
|
|
167886
|
+
this.beforeTargetsObserver = scene.onBeforeRenderTargetsRenderObservable.add(() => { mesh.isVisible = false; });
|
|
167887
|
+
this.afterTargetsObserver = scene.onAfterRenderTargetsRenderObservable.add(() => { mesh.isVisible = true; });
|
|
167888
|
+
}
|
|
167889
|
+
async loadNoise() {
|
|
167890
|
+
const noise = this.block.noise;
|
|
167891
|
+
if (noise == null || noise.worley == null || noise.erosion == null) {
|
|
167892
|
+
LightingConversions.WarnOnce("urp:clouds:nonoise", "Volumetric clouds: the export carries no cloud noise, so the clouds do not draw");
|
|
167893
|
+
return false;
|
|
167894
|
+
}
|
|
167895
|
+
try {
|
|
167896
|
+
const buffers = await Promise.all([
|
|
167897
|
+
Tools.LoadFileAsync(this.root + noise.worley.url, true),
|
|
167898
|
+
Tools.LoadFileAsync(this.root + noise.erosion.url, true)
|
|
167899
|
+
]);
|
|
167900
|
+
if (this.disposed)
|
|
167901
|
+
return false;
|
|
167902
|
+
this.worleyTexture = this.createNoiseTexture("Volumetric Clouds Worley Noise", buffers[0], noise.worley.size);
|
|
167903
|
+
this.erosionTexture = this.createNoiseTexture("Volumetric Clouds Erosion Noise", buffers[1], noise.erosion.size);
|
|
167904
|
+
return this.worleyTexture != null && this.erosionTexture != null;
|
|
167905
|
+
}
|
|
167906
|
+
catch (error) {
|
|
167907
|
+
LightingConversions.WarnOnce("urp:clouds:noiseload", "Volumetric clouds: a cloud noise file failed to load, so the clouds do not draw (" + error + ")");
|
|
167908
|
+
return false;
|
|
167909
|
+
}
|
|
167910
|
+
}
|
|
167911
|
+
createNoiseTexture(name, buffer, size) {
|
|
167912
|
+
if (buffer == null || buffer.byteLength !== size * size * size) {
|
|
167913
|
+
LightingConversions.WarnOnce("urp:clouds:noisesize", "Volumetric clouds: " + name + " is not " + size + "³ bytes, so the clouds do not draw");
|
|
167914
|
+
return null;
|
|
167915
|
+
}
|
|
167916
|
+
const texture = new RawTexture3D(new Uint8Array(buffer), size, size, size, Constants.TEXTUREFORMAT_R, this.scene, true, false, Texture.TRILINEAR_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
|
|
167917
|
+
texture.name = name;
|
|
167918
|
+
texture.wrapU = Texture.WRAP_ADDRESSMODE;
|
|
167919
|
+
texture.wrapV = Texture.WRAP_ADDRESSMODE;
|
|
167920
|
+
texture.wrapR = Texture.WRAP_ADDRESSMODE;
|
|
167921
|
+
return texture;
|
|
167922
|
+
}
|
|
167923
|
+
waitUntilReady() {
|
|
167924
|
+
if (this.disposed)
|
|
167925
|
+
return;
|
|
167926
|
+
const camera = this.scene.activeCamera;
|
|
167927
|
+
if (camera != null && this.sceneDepth == null)
|
|
167928
|
+
this.sceneDepth = CameraDepthCopy.AcquireForSampler(this.scene, camera, this);
|
|
167929
|
+
const noiseReady = this.worleyTexture.isReady() && this.erosionTexture.isReady() && this.curveTexture.isReady();
|
|
167930
|
+
const compositeReady = this.compositeMaterial.isReady(this.compositeMesh);
|
|
167931
|
+
if (!noiseReady || !this.marchPass.isReady() || !this.temporalPass.isReady() || !compositeReady || camera == null) {
|
|
167932
|
+
setTimeout(() => this.waitUntilReady(), CLOUD_READY_POLL_MS);
|
|
167933
|
+
return;
|
|
167934
|
+
}
|
|
167935
|
+
this.isReady = true;
|
|
167936
|
+
this.compositeMesh.setEnabled(true);
|
|
167937
|
+
this.beforeRenderObserver = this.scene.onBeforeRenderObservable.add(() => this.render());
|
|
167938
|
+
this.releasePending();
|
|
167939
|
+
SceneManager.WhenSceneRevealed(this.scene).then((revealed) => {
|
|
167940
|
+
if (!revealed || this.disposed)
|
|
167941
|
+
return;
|
|
167942
|
+
this.afterRenderObserver = this.scene.onAfterRenderObservable.add(() => this.updateTier());
|
|
167943
|
+
});
|
|
167944
|
+
}
|
|
167945
|
+
releasePending() {
|
|
167946
|
+
if (!this.isPending)
|
|
167947
|
+
return;
|
|
167948
|
+
this.isPending = false;
|
|
167949
|
+
this.scene.removePendingData(this);
|
|
167950
|
+
}
|
|
167951
|
+
render() {
|
|
167952
|
+
const camera = this.scene.activeCamera;
|
|
167953
|
+
if (!this.isReady || this.disposed || camera == null)
|
|
167954
|
+
return;
|
|
167955
|
+
this.advanceWind(this.scene.getEngine().getDeltaTime() / 1000);
|
|
167956
|
+
this.resizeTargets();
|
|
167957
|
+
this.frameSeed = (this.frameSeed + 1) % 65536;
|
|
167958
|
+
this.sceneDepth = CameraDepthCopy.AcquireForSampler(this.scene, camera, this);
|
|
167959
|
+
this.trackCamera(camera);
|
|
167960
|
+
this.renderer.render(this.marchPass, this.marchTarget);
|
|
167961
|
+
const accumulation = (typeof this.block.temporalaccumulationfactor === "number") ? this.block.temporalaccumulationfactor : 0;
|
|
167962
|
+
if (accumulation < CLOUD_MINIMUM_ACCUMULATION) {
|
|
167963
|
+
this.compositeMaterial.setTexture("cloudLighting", this.marchTarget);
|
|
167964
|
+
}
|
|
167965
|
+
else {
|
|
167966
|
+
this.historyIndex = 1 - this.historyIndex;
|
|
167967
|
+
this.renderer.render(this.temporalPass, this.historyTargets[this.historyIndex]);
|
|
167968
|
+
this.compositeMaterial.setTexture("cloudLighting", this.historyTargets[this.historyIndex]);
|
|
167969
|
+
this.hasHistory = true;
|
|
167970
|
+
}
|
|
167971
|
+
this.compositeMaterial.setFloat("cloudDisplayEncoded", this.isDisplayEncoded() ? 1 : 0);
|
|
167972
|
+
this.previousViewProjection.copyFrom(camera.getTransformationMatrix());
|
|
167973
|
+
}
|
|
167974
|
+
isDisplayEncoded() {
|
|
167975
|
+
return HdrpRendering.IsParity(this.scene) !== true && this.scene.imageProcessingConfiguration.applyByPostProcess !== true;
|
|
167976
|
+
}
|
|
167977
|
+
trackCamera(camera) {
|
|
167978
|
+
const eye = camera.globalPosition;
|
|
167979
|
+
this.currentEye[0] = eye.x;
|
|
167980
|
+
this.currentEye[1] = eye.y;
|
|
167981
|
+
this.currentEye[2] = eye.z;
|
|
167982
|
+
const isCut = camera !== this.lastCamera || VolumetricCloudsMath.IsCameraCut(this.hasPreviousEye ? this.previousEye : null, this.currentEye);
|
|
167983
|
+
if (isCut)
|
|
167984
|
+
this.hasHistory = false;
|
|
167985
|
+
this.lastCamera = camera;
|
|
167986
|
+
this.previousEye[0] = eye.x;
|
|
167987
|
+
this.previousEye[1] = eye.y;
|
|
167988
|
+
this.previousEye[2] = eye.z;
|
|
167989
|
+
this.hasPreviousEye = true;
|
|
167990
|
+
}
|
|
167991
|
+
updateTier() {
|
|
167992
|
+
if (this.disposed || !this.tierTracker.addFrame(this.scene.getEngine().getDeltaTime()))
|
|
167993
|
+
return;
|
|
167994
|
+
this.tier = this.tierTracker.tier;
|
|
167995
|
+
this.settings = VolumetricCloudsMath.TierSettings(this.tier, this.block);
|
|
167996
|
+
if (this.tierTracker.isSettled) {
|
|
167997
|
+
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");
|
|
167998
|
+
return;
|
|
167999
|
+
}
|
|
168000
|
+
LightingConversions.WarnOnce("urp:clouds:tier" + this.tier, "Volumetric clouds: frames ran over " + CLOUD_TIER_BUDGET_MS + " ms, trying tier " + this.tier
|
|
168001
|
+
+ " (resolution " + this.settings.resolutionScale + ", " + this.settings.primarySteps + " primary steps, " + this.settings.lightSteps + " light steps)");
|
|
168002
|
+
}
|
|
168003
|
+
reportLoweredSettings() {
|
|
168004
|
+
const block = this.block;
|
|
168005
|
+
const settings = this.settings;
|
|
168006
|
+
if (typeof block.numprimarysteps === "number" && settings.primarySteps < block.numprimarysteps) {
|
|
168007
|
+
LightingConversions.WarnOnce("urp:clouds:primarysteps:" + this.tier, "Volumetric clouds: " + block.numprimarysteps + " primary steps are capped at " + settings.primarySteps + " in the browser (tier " + this.tier + ")");
|
|
168008
|
+
}
|
|
168009
|
+
if (typeof block.longraysamplemultiplier === "number" && settings.longRayMultiplier < block.longraysamplemultiplier) {
|
|
168010
|
+
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 + ")");
|
|
168011
|
+
}
|
|
168012
|
+
if (typeof block.numlightsteps === "number" && settings.lightSteps < block.numlightsteps) {
|
|
168013
|
+
LightingConversions.WarnOnce("urp:clouds:lightsteps:" + this.tier, "Volumetric clouds: " + block.numlightsteps + " light steps are capped at " + settings.lightSteps + " (tier " + this.tier + ")");
|
|
168014
|
+
}
|
|
168015
|
+
if (typeof block.resolutionscale === "number" && settings.resolutionScale < block.resolutionscale) {
|
|
168016
|
+
LightingConversions.WarnOnce("urp:clouds:resolution:" + this.tier, "Volumetric clouds: resolution scale " + block.resolutionscale + " is lowered to " + settings.resolutionScale + " (tier " + this.tier + ")");
|
|
168017
|
+
}
|
|
168018
|
+
}
|
|
168019
|
+
advanceWind(deltaSeconds) {
|
|
168020
|
+
const block = this.block;
|
|
168021
|
+
const signedDelta = Math.min(Math.max(deltaSeconds, 0), 1) * CLOUD_WIND_KMH_TO_MS;
|
|
168022
|
+
const theta = Tools.ToRadians(block.globalorientation || 0);
|
|
168023
|
+
const speed = block.globalspeed || 0;
|
|
168024
|
+
this.windOffset[0] = (this.windOffset[0] + signedDelta * speed * Math.cos(theta)) % CLOUD_WIND_WRAP_METERS;
|
|
168025
|
+
this.windOffset[1] = (this.windOffset[1] + signedDelta * speed * Math.sin(theta)) % CLOUD_WIND_WRAP_METERS;
|
|
168026
|
+
this.verticalShapeOffset = (this.verticalShapeOffset + signedDelta * (block.verticalshapewindspeed || 0)) % CLOUD_WIND_WRAP_METERS;
|
|
168027
|
+
this.verticalErosionOffset = (this.verticalErosionOffset + signedDelta * (block.erosionspeedmultiplier || 0)) % CLOUD_WIND_WRAP_METERS;
|
|
168028
|
+
}
|
|
168029
|
+
resizeTargets() {
|
|
168030
|
+
if (!this.updateTargetSize())
|
|
168031
|
+
return;
|
|
168032
|
+
const size = { width: this.targetWidth, height: this.targetHeight };
|
|
168033
|
+
this.marchTarget.resize(size);
|
|
168034
|
+
for (const history of this.historyTargets)
|
|
168035
|
+
history.resize(size);
|
|
168036
|
+
this.hasHistory = false;
|
|
168037
|
+
}
|
|
168038
|
+
updateTargetSize() {
|
|
168039
|
+
const engine = this.scene.getEngine();
|
|
168040
|
+
const scale = this.settings.resolutionScale;
|
|
168041
|
+
const width = Math.max(1, Math.round(engine.getRenderWidth() * scale));
|
|
168042
|
+
const height = Math.max(1, Math.round(engine.getRenderHeight() * scale));
|
|
168043
|
+
if (width === this.targetWidth && height === this.targetHeight)
|
|
168044
|
+
return false;
|
|
168045
|
+
this.targetWidth = width;
|
|
168046
|
+
this.targetHeight = height;
|
|
168047
|
+
return true;
|
|
168048
|
+
}
|
|
168049
|
+
bindMarch(effect) {
|
|
168050
|
+
const scene = this.scene;
|
|
168051
|
+
const camera = scene.activeCamera;
|
|
168052
|
+
if (camera == null)
|
|
168053
|
+
return;
|
|
168054
|
+
const block = this.block;
|
|
168055
|
+
const settings = this.settings;
|
|
168056
|
+
const mirrorX = this.mirror.m[0];
|
|
168057
|
+
camera.getTransformationMatrix().invertToRef(this.inverseViewProjection);
|
|
168058
|
+
this.inverseViewProjection.multiplyToRef(this.mirror, this.inverseViewProjection);
|
|
168059
|
+
effect.setMatrix("cloudInvViewProj", this.inverseViewProjection);
|
|
168060
|
+
const eye = camera.globalPosition;
|
|
168061
|
+
effect.setFloat4("cloudEye", eye.x * mirrorX, eye.y, eye.z, 0);
|
|
168062
|
+
camera.getDirectionToRef(this.localForward, this.cameraForward);
|
|
168063
|
+
effect.setFloat4("cloudForward", this.cameraForward.x * mirrorX, this.cameraForward.y, this.cameraForward.z, mirrorX);
|
|
168064
|
+
const hasDepth = this.sceneDepth != null;
|
|
168065
|
+
effect.setTexture("sceneDepth", hasDepth ? this.sceneDepth : this.placeholderDepth);
|
|
168066
|
+
effect.setFloat4("cloudCamera", (camera.maxZ > 0) ? camera.maxZ : Number.MAX_VALUE, hasDepth ? 1 : 0, this.frameSeed, this.preExposure);
|
|
168067
|
+
if (this.hasAerial)
|
|
168068
|
+
this.bindAerial(effect);
|
|
168069
|
+
const center = this.planetCenter;
|
|
168070
|
+
const bottomRadius = this.planetRadius + (block.bottomaltitude || 0);
|
|
168071
|
+
const altitudeRange = block.altituderange || 1;
|
|
168072
|
+
effect.setFloat4("cloudPlanet", center.x * mirrorX, center.y, center.z, this.planetRadius);
|
|
168073
|
+
effect.setFloat4("cloudLayer", bottomRadius, bottomRadius + altitudeRange, VolumetricCloudsMath.MaximumStep(altitudeRange, block.numprimarysteps, settings.primarySteps), settings.primarySteps);
|
|
168074
|
+
const isAutomaticFade = block.fadeinmode !== CLOUD_FADE_MODE_MANUAL;
|
|
168075
|
+
const fadeStart = isAutomaticFade ? Math.max(altitudeRange * CLOUD_AUTO_FADE_START_FRACTION, camera.minZ) : Math.max(block.fadeinstart || 0, camera.minZ);
|
|
168076
|
+
const fadeDistance = isAutomaticFade ? altitudeRange * CLOUD_AUTO_FADE_DISTANCE_FRACTION : (block.fadeindistance || 0);
|
|
168077
|
+
effect.setFloat4("cloudSteps", settings.longRayMultiplier, settings.lightSteps, fadeStart, fadeDistance);
|
|
168078
|
+
effect.setFloat4("cloudShape", block.shapescale, block.shapefactor, block.erosionscale, block.erosionfactor);
|
|
168079
|
+
const density = block.densitymultiplier * block.densitymultiplier * CLOUD_DENSITY_SCALE;
|
|
168080
|
+
effect.setFloat4("cloudDetail", block.erosionocclusion, block.microerosionscale, block.microerosionfactor, density);
|
|
168081
|
+
const shapeOffset = Array.isArray(block.shapeoffset) ? block.shapeoffset : [0, 0, 0];
|
|
168082
|
+
effect.setFloat4("cloudShapeOffset", shapeOffset[0], shapeOffset[2], shapeOffset[1], block.altitudedistortion * CLOUD_ALTITUDE_DISTORTION_SCALE);
|
|
168083
|
+
const theta = Tools.ToRadians(block.globalorientation || 0);
|
|
168084
|
+
effect.setFloat4("cloudWind", -Math.cos(theta), -Math.sin(theta), this.windOffset[0], this.windOffset[1]);
|
|
168085
|
+
effect.setFloat4("cloudWindSpeed", block.shapespeedmultiplier, block.erosionspeedmultiplier, this.verticalShapeOffset, this.verticalErosionOffset);
|
|
168086
|
+
const coverageInverseSize = 1 / Math.max(CLOUD_MINIMUM_COVERAGE_PATTERN_METERS, block.coveragepatternsize || CLOUD_DEFAULT_COVERAGE_PATTERN_METERS);
|
|
168087
|
+
const coverageGap = (typeof block.coveragegapamount === "number") ? block.coveragegapamount : CLOUD_DEFAULT_COVERAGE_GAP;
|
|
168088
|
+
effect.setFloat4("cloudCoverage", block.coveragevariation || 0, coverageInverseSize, coverageGap, 0);
|
|
168089
|
+
const coverageOffset = Array.isArray(block.coveragepatternoffset) ? block.coveragepatternoffset : [0, 0];
|
|
168090
|
+
effect.setFloat4("cloudCoverageOffset", coverageOffset[0], coverageOffset[1], 0, 0);
|
|
168091
|
+
effect.setFloat4("cloudScattering", 1 - block.multiscattering * CLOUD_MULTI_SCATTERING_SCALE, block.powdereffectintensity, 0, 0);
|
|
168092
|
+
effect.setFloat4("cloudTint", this.scatteringTint.r, this.scatteringTint.g, this.scatteringTint.b, 0);
|
|
168093
|
+
this.bindLighting(effect, mirrorX);
|
|
168094
|
+
effect.setTexture("worleyNoise", this.worleyTexture);
|
|
168095
|
+
effect.setTexture("erosionNoise", this.erosionTexture);
|
|
168096
|
+
effect.setTexture("cloudCurves", this.curveTexture);
|
|
168097
|
+
}
|
|
168098
|
+
bindLighting(effect, mirrorX) {
|
|
168099
|
+
const block = this.block;
|
|
168100
|
+
const sun = this.resolveSun();
|
|
168101
|
+
const sunScale = (this.sky != null && typeof this.sky.sunintensityscale === "number") ? this.sky.sunintensityscale : 1;
|
|
168102
|
+
const dimmer = (typeof block.sunlightdimmer === "number") ? block.sunlightdimmer : 1;
|
|
168103
|
+
if (sun != null) {
|
|
168104
|
+
const direction = this.sunDirection;
|
|
168105
|
+
const strength = sun.intensity * sunScale * dimmer;
|
|
168106
|
+
effect.setFloat4("cloudSunDirection", -direction.x * mirrorX, -direction.y, -direction.z, 0);
|
|
168107
|
+
effect.setFloat4("cloudSunColor", sun.diffuse.r * strength, sun.diffuse.g * strength, sun.diffuse.b * strength, 0);
|
|
168108
|
+
}
|
|
168109
|
+
else {
|
|
168110
|
+
effect.setFloat4("cloudSunDirection", 0, 1, 0, 0);
|
|
168111
|
+
effect.setFloat4("cloudSunColor", 0, 0, 0, 0);
|
|
168112
|
+
}
|
|
168113
|
+
this.refreshAmbient();
|
|
168114
|
+
const ambientDimmer = (typeof block.ambientlightprobedimmer === "number") ? block.ambientlightprobedimmer : 1;
|
|
168115
|
+
effect.setFloat4("cloudAmbientTop", this.ambientTop.r * ambientDimmer, this.ambientTop.g * ambientDimmer, this.ambientTop.b * ambientDimmer, 0);
|
|
168116
|
+
effect.setFloat4("cloudAmbientBottom", this.ambientBottom.r * ambientDimmer, this.ambientBottom.g * ambientDimmer, this.ambientBottom.b * ambientDimmer, 0);
|
|
168117
|
+
}
|
|
168118
|
+
bindAerial(effect) {
|
|
168119
|
+
const sky = HdrpPhysicallyBasedSky.Get(this.scene);
|
|
168120
|
+
const isBound = sky != null && sky.bindAerialPerspective(effect, VolumetricCloudsShaders.AerialSampler, VolumetricCloudsShaders.AerialCameraUniform);
|
|
168121
|
+
if (isBound)
|
|
168122
|
+
return;
|
|
168123
|
+
effect.setTexture(VolumetricCloudsShaders.AerialSampler, this.placeholderAerial);
|
|
168124
|
+
effect.setFloat4(VolumetricCloudsShaders.AerialCameraUniform, 0, 0, 1, 0);
|
|
168125
|
+
}
|
|
168126
|
+
resolveSun() {
|
|
168127
|
+
if (this.sunLight == null || this.sunLight.isDisposed() || !this.sunLight.isEnabled()) {
|
|
168128
|
+
const lights = HdrpPhysicallyBasedSky.InteractingLights(this.scene.lights);
|
|
168129
|
+
this.sunLight = (lights.length > 0) ? lights[0] : null;
|
|
168130
|
+
}
|
|
168131
|
+
const sun = this.sunLight;
|
|
168132
|
+
if (sun == null)
|
|
168133
|
+
return null;
|
|
168134
|
+
sun.computeTransformedInformation();
|
|
168135
|
+
const transformed = sun.transformedDirection;
|
|
168136
|
+
this.sunDirection.copyFrom((transformed != null && sun.parent != null) ? transformed : sun.direction).normalize();
|
|
168137
|
+
return sun;
|
|
168138
|
+
}
|
|
168139
|
+
refreshAmbient() {
|
|
168140
|
+
const sky = HdrpPhysicallyBasedSky.Get(this.scene);
|
|
168141
|
+
const source = (sky != null) ? sky : this.scene.environmentTexture;
|
|
168142
|
+
const captureCount = (sky != null) ? sky.captureCount : 0;
|
|
168143
|
+
if (source === this.ambientSource && captureCount === this.ambientCaptureCount && source != null)
|
|
168144
|
+
return;
|
|
168145
|
+
this.ambientSource = source;
|
|
168146
|
+
this.ambientCaptureCount = captureCount;
|
|
168147
|
+
if (sky != null) {
|
|
168148
|
+
this.ambientTop.copyFrom(sky.ambientAt(Vector3.UpReadOnly));
|
|
168149
|
+
this.ambientBottom.copyFrom(sky.ambientAt(Vector3.DownReadOnly));
|
|
168150
|
+
return;
|
|
168151
|
+
}
|
|
168152
|
+
const environment = this.scene.environmentTexture;
|
|
168153
|
+
const polynomial = (environment != null) ? environment.sphericalPolynomial : null;
|
|
168154
|
+
if (polynomial != null) {
|
|
168155
|
+
this.ambientTop.copyFrom(HdrpPbSkyMath.EvaluatePolynomial(polynomial, Vector3.UpReadOnly));
|
|
168156
|
+
this.ambientBottom.copyFrom(HdrpPbSkyMath.EvaluatePolynomial(polynomial, Vector3.DownReadOnly));
|
|
168157
|
+
return;
|
|
168158
|
+
}
|
|
168159
|
+
this.ambientTop.copyFrom(this.scene.ambientColor);
|
|
168160
|
+
this.ambientBottom.copyFrom(this.scene.ambientColor);
|
|
168161
|
+
}
|
|
168162
|
+
bindTemporal(effect) {
|
|
168163
|
+
const camera = this.scene.activeCamera;
|
|
168164
|
+
if (camera == null)
|
|
168165
|
+
return;
|
|
168166
|
+
const mirrorX = this.mirror.m[0];
|
|
168167
|
+
this.mirror.multiplyToRef(this.previousViewProjection, this.previousViewProjectionUnity);
|
|
168168
|
+
effect.setMatrix("cloudInvViewProj", this.inverseViewProjection);
|
|
168169
|
+
effect.setMatrix("cloudPrevViewProj", this.previousViewProjectionUnity);
|
|
168170
|
+
const eye = camera.globalPosition;
|
|
168171
|
+
effect.setFloat4("cloudEye", eye.x * mirrorX, eye.y, eye.z, 0);
|
|
168172
|
+
effect.setFloat4("cloudForward", this.cameraForward.x * mirrorX, this.cameraForward.y, this.cameraForward.z, 0);
|
|
168173
|
+
const hasDepth = this.sceneDepth != null;
|
|
168174
|
+
effect.setTexture("sceneDepth", hasDepth ? this.sceneDepth : this.placeholderDepth);
|
|
168175
|
+
const accumulation = this.block.temporalaccumulationfactor;
|
|
168176
|
+
effect.setFloat4("cloudCamera", (camera.maxZ > 0) ? camera.maxZ : Number.MAX_VALUE, hasDepth ? 1 : 0, this.hasHistory ? 1 : 0, accumulation);
|
|
168177
|
+
const center = this.planetCenter;
|
|
168178
|
+
const bottomRadius = this.planetRadius + (this.block.bottomaltitude || 0);
|
|
168179
|
+
const altitudeRange = this.block.altituderange || 1;
|
|
168180
|
+
effect.setFloat4("cloudPlanet", center.x * mirrorX, center.y, center.z, this.planetRadius);
|
|
168181
|
+
effect.setFloat4("cloudLayer", bottomRadius, bottomRadius + altitudeRange, altitudeRange / CLOUD_MAX_STEP_DIVISOR, this.settings.primarySteps);
|
|
168182
|
+
effect.setFloat4("cloudTexel", 1 / this.targetWidth, 1 / this.targetHeight, 0, 0);
|
|
168183
|
+
effect.setTexture("cloudCurrent", this.marchTarget);
|
|
168184
|
+
effect.setTexture("cloudHistory", this.historyTargets[1 - this.historyIndex]);
|
|
168185
|
+
}
|
|
168186
|
+
static LinearScatteringTint(tint) {
|
|
168187
|
+
const source = Array.isArray(tint) ? tint : [0, 0, 0];
|
|
168188
|
+
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();
|
|
168189
|
+
}
|
|
168190
|
+
static CanvasCssWidth(scene) {
|
|
168191
|
+
const engine = scene.getEngine();
|
|
168192
|
+
const canvas = engine.getRenderingCanvas();
|
|
168193
|
+
if (canvas != null && typeof canvas.clientWidth === "number" && canvas.clientWidth > 0)
|
|
168194
|
+
return canvas.clientWidth;
|
|
168195
|
+
return engine.getRenderWidth();
|
|
168196
|
+
}
|
|
168197
|
+
}
|
|
168198
|
+
VolumetricClouds.MeshName = "Volumetric Clouds";
|
|
168199
|
+
export class VolumetricCloudsShaders {
|
|
168200
|
+
static Register() {
|
|
168201
|
+
if (VolumetricCloudsShaders.registered === true)
|
|
168202
|
+
return;
|
|
168203
|
+
const glsl = ShaderStore.ShadersStore;
|
|
168204
|
+
const wgsl = ShaderStore.ShadersStoreWGSL;
|
|
168205
|
+
const shaders = VolumetricCloudsShaders;
|
|
168206
|
+
glsl[shaders.MarchName + "FragmentShader"] = shaders.GlslMarch();
|
|
168207
|
+
glsl[shaders.CompositeName + "VertexShader"] = shaders.GlslCompositeVertex();
|
|
168208
|
+
glsl[shaders.CompositeName + "FragmentShader"] = shaders.GlslCompositeFragment();
|
|
168209
|
+
glsl[shaders.TemporalName + "FragmentShader"] = shaders.GlslTemporal();
|
|
168210
|
+
wgsl[shaders.MarchName + "FragmentShader"] = shaders.WgslMarch();
|
|
168211
|
+
wgsl[shaders.CompositeName + "VertexShader"] = shaders.WgslCompositeVertex();
|
|
168212
|
+
wgsl[shaders.CompositeName + "FragmentShader"] = shaders.WgslCompositeFragment();
|
|
168213
|
+
wgsl[shaders.TemporalName + "FragmentShader"] = shaders.WgslTemporal();
|
|
168214
|
+
VolumetricCloudsShaders.registered = true;
|
|
168215
|
+
}
|
|
168216
|
+
static AllSources() {
|
|
168217
|
+
const shaders = VolumetricCloudsShaders;
|
|
168218
|
+
return [
|
|
168219
|
+
shaders.GlslMarch(), shaders.GlslCompositeVertex(), shaders.GlslCompositeFragment(), shaders.GlslTemporal(),
|
|
168220
|
+
shaders.WgslMarch(), shaders.WgslCompositeVertex(), shaders.WgslCompositeFragment(), shaders.WgslTemporal()
|
|
168221
|
+
];
|
|
168222
|
+
}
|
|
168223
|
+
static GlslMarchUniforms() {
|
|
168224
|
+
return VolumetricCloudsShaders.MarchUniforms.map((name) => (name === "cloudInvViewProj") ? "uniform mat4 " + name + ";" : "uniform vec4 " + name + ";").join("\n") + "\n";
|
|
168225
|
+
}
|
|
168226
|
+
static GlslConstants() {
|
|
168227
|
+
return `const float CLOUD_DENSITY_THRESHOLD = 0.001;
|
|
168228
|
+
const int EMPTY_STEPS_BEFORE_LARGE_STEPS = 8;
|
|
168229
|
+
const float FORWARD_ECCENTRICITY = 0.7;
|
|
168230
|
+
const float BACKWARD_ECCENTRICITY = 0.7;
|
|
168231
|
+
const float MIN_EROSION_DISTANCE = 3000.0;
|
|
168232
|
+
const float MAX_EROSION_DISTANCE = 100000.0;
|
|
168233
|
+
const float NOISE_NORMALIZATION = 100000.0;
|
|
168234
|
+
const float MAX_SKYBOX_DISTANCE = 200000.0;
|
|
168235
|
+
const float LIGHT_STEP_MAXIMAL_SIZE = 1000.0;
|
|
168236
|
+
const float CLOUD_PI = 3.14159265359;
|
|
168237
|
+
const int MAX_PRIMARY_STEPS = 1024;
|
|
168238
|
+
const int MAX_LIGHT_STEPS = 16;
|
|
168239
|
+
`;
|
|
168240
|
+
}
|
|
168241
|
+
static GlslGeometry() {
|
|
168242
|
+
return `vec2 cloudIntersectSphere(float sphereRadius, float cosChi, float radialDistance) {
|
|
168243
|
+
float ratio = sphereRadius / radialDistance;
|
|
168244
|
+
float d = ratio * ratio - clamp(1.0 - cosChi * cosChi, 0.0, 1.0);
|
|
168245
|
+
return (d < 0.0) ? vec2(d) : radialDistance * vec2(-cosChi - sqrt(d), -cosChi + sqrt(d));
|
|
168246
|
+
}
|
|
168247
|
+
float cloudHorizonCosine(float radialDistance) {
|
|
168248
|
+
float sinHorizon = cloudPlanet.w / radialDistance;
|
|
168249
|
+
return -sqrt(clamp(1.0 - sinHorizon * sinHorizon, 0.0, 1.0));
|
|
168250
|
+
}
|
|
168251
|
+
bool cloudIntersectVolume(vec3 originPS, vec3 direction, out float entry, out float exitDistance) {
|
|
168252
|
+
float radialDistance = length(originPS);
|
|
168253
|
+
float cosChi = dot(originPS, direction) / radialDistance;
|
|
168254
|
+
vec2 inner = cloudIntersectSphere(cloudLayer.x, cosChi, radialDistance);
|
|
168255
|
+
vec2 outer = cloudIntersectSphere(cloudLayer.y, cosChi, radialDistance);
|
|
168256
|
+
if (inner.x < 0.0 && inner.y >= 0.0) {
|
|
168257
|
+
entry = inner.y;
|
|
168258
|
+
exitDistance = outer.y;
|
|
168259
|
+
return cosChi >= cloudHorizonCosine(radialDistance);
|
|
168260
|
+
}
|
|
168261
|
+
entry = max(outer.x, 0.0);
|
|
168262
|
+
exitDistance = (inner.x >= 0.0) ? inner.x : outer.y;
|
|
168263
|
+
return outer.y >= 0.0;
|
|
168264
|
+
}
|
|
168265
|
+
int cloudRaySphere(vec3 startWS, vec3 direction, float radius, out vec2 result) {
|
|
168266
|
+
vec3 startPS = startWS + vec3(0.0, cloudPlanet.w, 0.0);
|
|
168267
|
+
float a = dot(direction, direction);
|
|
168268
|
+
float b = 2.0 * dot(direction, startPS);
|
|
168269
|
+
float c = dot(startPS, startPS) - radius * radius;
|
|
168270
|
+
float d = b * b - 4.0 * a * c;
|
|
168271
|
+
result = vec2(0.0);
|
|
168272
|
+
int solutions = 0;
|
|
168273
|
+
if (d >= 0.0) {
|
|
168274
|
+
float q = -0.5 * (b + ((b >= 0.0) ? 1.0 : -1.0) * sqrt(d));
|
|
168275
|
+
result = vec2(c / q, q / a);
|
|
168276
|
+
solutions = 2;
|
|
168277
|
+
if (result.x < 0.0) { solutions--; result.x = result.y; }
|
|
168278
|
+
if (result.y < 0.0) solutions--;
|
|
168279
|
+
}
|
|
168280
|
+
return solutions;
|
|
168281
|
+
}
|
|
168282
|
+
bool cloudRange(vec3 originWS, vec3 direction, out float rangeStart, out float rangeEnd) {
|
|
168283
|
+
#ifdef CLOUD_LOCAL
|
|
168284
|
+
return cloudIntersectVolume(originWS - cloudPlanet.xyz, direction, rangeStart, rangeEnd);
|
|
168285
|
+
#else
|
|
168286
|
+
vec2 inner = vec2(0.0);
|
|
168287
|
+
vec2 outer = vec2(0.0);
|
|
168288
|
+
cloudRaySphere(originWS, direction, cloudLayer.x, inner);
|
|
168289
|
+
cloudRaySphere(originWS, direction, cloudLayer.y, outer);
|
|
168290
|
+
rangeStart = inner.x;
|
|
168291
|
+
rangeEnd = outer.x;
|
|
168292
|
+
return true;
|
|
168293
|
+
#endif
|
|
168294
|
+
}
|
|
168295
|
+
`;
|
|
168296
|
+
}
|
|
168297
|
+
static GlslDensity() {
|
|
168298
|
+
return `struct CloudProperties {
|
|
168299
|
+
float density;
|
|
168300
|
+
float ambientOcclusion;
|
|
168301
|
+
float height;
|
|
168302
|
+
float sigmaT;
|
|
168303
|
+
};
|
|
168304
|
+
float cloudRemap(float x, float a, float b, float c, float d) {
|
|
168305
|
+
return (((x - a) / (b - a)) * (d - c)) + c;
|
|
168306
|
+
}
|
|
168307
|
+
float cloudNormalizedHeight(vec3 positionPS) {
|
|
168308
|
+
return clamp((length(positionPS) - cloudLayer.x) / (cloudLayer.y - cloudLayer.x), 0.0, 1.0);
|
|
168309
|
+
}
|
|
168310
|
+
vec3 cloudAnimateShape(vec3 positionPS) {
|
|
168311
|
+
vec3 position = positionPS;
|
|
168312
|
+
position.y += position.x / 3.0 + position.z / 7.0;
|
|
168313
|
+
return position + vec3(cloudWind.z, 0.0, cloudWind.w) * cloudWindSpeed.x + vec3(0.0, cloudWindSpeed.z, 0.0);
|
|
168314
|
+
}
|
|
168315
|
+
vec3 cloudAnimateErosion(vec3 positionPS) {
|
|
168316
|
+
return positionPS + vec3(cloudWind.z, 0.0, cloudWind.w) * cloudWindSpeed.y + vec3(0.0, cloudWindSpeed.w, 0.0);
|
|
168317
|
+
}
|
|
168318
|
+
float cloudCoverageHash(vec2 cell) {
|
|
168319
|
+
vec3 p = fract(vec3(cell.x, cell.y, cell.x) * 0.1031);
|
|
168320
|
+
p += dot(p, p.yzx + 33.33);
|
|
168321
|
+
return fract((p.x + p.y) * p.z);
|
|
168322
|
+
}
|
|
168323
|
+
float cloudCoverageNoise(vec2 position) {
|
|
168324
|
+
vec2 cell = floor(position);
|
|
168325
|
+
vec2 blend = fract(position);
|
|
168326
|
+
blend = blend * blend * (3.0 - 2.0 * blend);
|
|
168327
|
+
float bottom = mix(cloudCoverageHash(cell), cloudCoverageHash(cell + vec2(1.0, 0.0)), blend.x);
|
|
168328
|
+
float top = mix(cloudCoverageHash(cell + vec2(0.0, 1.0)), cloudCoverageHash(cell + vec2(1.0, 1.0)), blend.x);
|
|
168329
|
+
return mix(bottom, top, blend.y);
|
|
168330
|
+
}
|
|
168331
|
+
vec2 cloudCoverageAt(vec3 positionPS) {
|
|
168332
|
+
float coverage = 0.9;
|
|
168333
|
+
float shapeFill = 0.0;
|
|
168334
|
+
if (cloudCoverage.x != 0.0) {
|
|
168335
|
+
vec2 coordinates = (positionPS.xz + cloudWind.zw * cloudWindSpeed.x + cloudCoverageOffset.xy) * cloudCoverage.y;
|
|
168336
|
+
float coverageNoise = cloudCoverageNoise(coordinates) * 0.7 + cloudCoverageNoise(coordinates * 2.03 + vec2(17.13, 43.71)) * 0.3;
|
|
168337
|
+
float threshold = mix(-0.15, 1.15, cloudCoverage.z);
|
|
168338
|
+
float mask = smoothstep(threshold - 0.12, threshold + 0.12, coverageNoise);
|
|
168339
|
+
if (cloudCoverage.x > 0.0) {
|
|
168340
|
+
coverage *= mix(1.0, mask, cloudCoverage.x);
|
|
168341
|
+
} else {
|
|
168342
|
+
shapeFill = clamp(-cloudCoverage.x, 0.0, 1.0) * (1.0 - mask);
|
|
168343
|
+
coverage = mix(coverage, 1.0, shapeFill);
|
|
168344
|
+
}
|
|
168345
|
+
}
|
|
168346
|
+
return vec2(coverage, shapeFill);
|
|
168347
|
+
}
|
|
168348
|
+
CloudProperties cloudPropertiesAt(vec3 samplePositionPS, float noiseMip, float erosionMip, bool cheapVersion, bool lightSampling) {
|
|
168349
|
+
CloudProperties properties = CloudProperties(0.0, 0.0, 0.0, 0.0);
|
|
168350
|
+
vec3 positionPS = samplePositionPS;
|
|
168351
|
+
#ifndef CLOUD_LOCAL
|
|
168352
|
+
if (positionPS.y < cloudPlanet.w) return properties;
|
|
168353
|
+
#endif
|
|
168354
|
+
properties.ambientOcclusion = 1.0;
|
|
168355
|
+
properties.height = cloudNormalizedHeight(positionPS);
|
|
168356
|
+
#ifndef CLOUD_LOCAL
|
|
168357
|
+
positionPS.xz += cloudEye.xz;
|
|
168358
|
+
#endif
|
|
168359
|
+
vec3 baseCoordinates = (cloudAnimateShape(positionPS).xzy / NOISE_NORMALIZATION) * cloudShape.x - cloudShapeOffset.xyz;
|
|
168360
|
+
baseCoordinates += properties.height * vec3(cloudWind.x, cloudWind.y, 0.0) * cloudShapeOffset.w;
|
|
168361
|
+
float lowFrequencyNoise = textureLod(worleyNoise, baseCoordinates, noiseMip).r;
|
|
168362
|
+
vec2 coverage = cloudCoverageAt(positionPS);
|
|
168363
|
+
if (coverage.x <= CLOUD_DENSITY_THRESHOLD) return properties;
|
|
168364
|
+
vec3 curves = textureLod(cloudCurves, vec2(properties.height, 0.5), 0.0).xyz;
|
|
168365
|
+
float shapeFactor = mix(0.1, 1.0, cloudShape.y * (1.0 - coverage.y)) * curves.y;
|
|
168366
|
+
float erosionFactor = cloudShape.w * curves.y;
|
|
168367
|
+
float microDetailFactor = cloudDetail.z * curves.y;
|
|
168368
|
+
lowFrequencyNoise = mix(1.0, lowFrequencyNoise, shapeFactor);
|
|
168369
|
+
float baseCloud = 1.0 - curves.x * coverage.x * (1.0 - shapeFactor);
|
|
168370
|
+
baseCloud = clamp(cloudRemap(lowFrequencyNoise, baseCloud, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x * coverage.x;
|
|
168371
|
+
properties.sigmaT = 0.04;
|
|
168372
|
+
float occlusionBlend = clamp(1.0 - max(erosionFactor, shapeFactor) * 0.5, 0.0, 1.0);
|
|
168373
|
+
properties.ambientOcclusion = mix(1.0, curves.z, occlusionBlend);
|
|
168374
|
+
if (!cheapVersion) {
|
|
168375
|
+
vec3 erosionCoordinates = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * cloudShape.z;
|
|
168376
|
+
float erosionValue = 1.0 - textureLod(erosionNoise, erosionCoordinates, erosionMip).r;
|
|
168377
|
+
erosionValue = mix(0.0, erosionValue, erosionFactor * 0.75 * coverage.x);
|
|
168378
|
+
properties.ambientOcclusion = clamp(properties.ambientOcclusion - sqrt(erosionValue * cloudDetail.x), 0.0, 1.0);
|
|
168379
|
+
baseCloud = cloudRemap(baseCloud, erosionValue, 1.0, 0.0, 1.0);
|
|
168380
|
+
#ifdef CLOUD_MICRO_EROSION
|
|
168381
|
+
vec3 fineCoordinates = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * cloudDetail.y;
|
|
168382
|
+
float fineValue = 1.0 - textureLod(erosionNoise, fineCoordinates, erosionMip).r;
|
|
168383
|
+
fineValue = mix(0.0, fineValue, microDetailFactor * 0.5 * coverage.x);
|
|
168384
|
+
baseCloud = cloudRemap(baseCloud, fineValue, 1.0, 0.0, 1.0);
|
|
168385
|
+
#endif
|
|
168386
|
+
}
|
|
168387
|
+
if (lightSampling) {
|
|
168388
|
+
baseCloud -= erosionFactor * 0.1;
|
|
168389
|
+
#ifdef CLOUD_MICRO_EROSION
|
|
168390
|
+
baseCloud -= microDetailFactor * 0.15;
|
|
168391
|
+
#endif
|
|
168392
|
+
}
|
|
168393
|
+
properties.density = max(0.0, baseCloud) * cloudDetail.w;
|
|
168394
|
+
return properties;
|
|
168395
|
+
}
|
|
168396
|
+
`;
|
|
168397
|
+
}
|
|
168398
|
+
static GlslLighting() {
|
|
168399
|
+
return `float cloudPhase(float anisotropy, float cosTheta) {
|
|
168400
|
+
float denominator = 1.0 + anisotropy * anisotropy - 2.0 * anisotropy * cosTheta;
|
|
168401
|
+
float falloff = inversesqrt(max(denominator, 1e-6));
|
|
168402
|
+
return (1.0 / (4.0 * CLOUD_PI)) * (1.0 - anisotropy * anisotropy) * falloff * falloff * falloff;
|
|
168403
|
+
}
|
|
168404
|
+
float cloudPowder(float density, float cosAngle, float intensity) {
|
|
168405
|
+
float powder = clamp((1.0 - exp(-density * 4.0)) * 2.0, 0.0, 1.0);
|
|
168406
|
+
return mix(1.0, mix(1.0, powder, smoothstep(0.5, -0.5, cosAngle)), intensity);
|
|
168407
|
+
}
|
|
168408
|
+
vec3 cloudSunTransmittance(vec3 positionPS, vec3 sunDirection, vec2 phase) {
|
|
168409
|
+
float radialDistance = length(positionPS);
|
|
168410
|
+
float cosChi = dot(positionPS, sunDirection) / radialDistance;
|
|
168411
|
+
vec3 transmittance = vec3(0.0);
|
|
168412
|
+
if (cosChi < cloudHorizonCosine(radialDistance)) return transmittance;
|
|
168413
|
+
float lightSteps = cloudSteps.y;
|
|
168414
|
+
float totalLightDistance = clamp(cloudIntersectSphere(cloudLayer.y, cosChi, radialDistance).y, 0.0, lightSteps * LIGHT_STEP_MAXIMAL_SIZE) + 5.0;
|
|
168415
|
+
float intervalSize = totalLightDistance / lightSteps;
|
|
168416
|
+
float opticalDepth = 0.0;
|
|
168417
|
+
for (int j = 0; j < MAX_LIGHT_STEPS; j++) {
|
|
168418
|
+
if (float(j) >= lightSteps) break;
|
|
168419
|
+
float distanceAlongLight = intervalSize * (0.25 + float(j));
|
|
168420
|
+
CloudProperties lightProperties = cloudPropertiesAt(positionPS + sunDirection * distanceAlongLight, 3.0 * float(j) / lightSteps, 0.0, true, true);
|
|
168421
|
+
opticalDepth += lightProperties.density * lightProperties.sigmaT;
|
|
168422
|
+
}
|
|
168423
|
+
vec3 extinction = intervalSize * opticalDepth * cloudTint.rgb;
|
|
168424
|
+
transmittance += exp(-extinction) * phase.x;
|
|
168425
|
+
transmittance += exp(-extinction * cloudScattering.x) * (phase.y * cloudScattering.x);
|
|
168426
|
+
return transmittance;
|
|
168427
|
+
}
|
|
168428
|
+
`;
|
|
168429
|
+
}
|
|
168430
|
+
static GlslAerial() {
|
|
168431
|
+
const skyShaders = HdrpPbSkyShaders;
|
|
168432
|
+
const fogPass = HdrpFogPass;
|
|
168433
|
+
if (skyShaders == null || fogPass == null)
|
|
168434
|
+
return "";
|
|
168435
|
+
return "#ifdef CLOUD_AERIAL\nprecision highp sampler2DArray;\nuniform sampler2DArray apLut;\nuniform vec4 cloudApCamera;\n"
|
|
168436
|
+
+ skyShaders.GlslUniforms() + skyShaders.GlslCommon() + fogPass.AerialPerspectiveGlsl() + "#endif\n";
|
|
168437
|
+
}
|
|
168438
|
+
static GlslMarch() {
|
|
168439
|
+
const shaders = VolumetricCloudsShaders;
|
|
168440
|
+
return "precision highp float;\nprecision highp sampler3D;\nvarying vec2 vUV;\nuniform sampler3D worleyNoise;\nuniform sampler3D erosionNoise;\nuniform sampler2D cloudCurves;\nuniform sampler2D sceneDepth;\n"
|
|
168441
|
+
+ shaders.GlslMarchUniforms() + shaders.GlslConstants() + shaders.GlslGeometry() + shaders.GlslDensity() + shaders.GlslLighting() + shaders.GlslAerial() + `
|
|
168442
|
+
uint cloudJenkins(uint value) {
|
|
168443
|
+
uint x = value;
|
|
168444
|
+
x += (x << 10u);
|
|
168445
|
+
x ^= (x >> 6u);
|
|
168446
|
+
x += (x << 3u);
|
|
168447
|
+
x ^= (x >> 11u);
|
|
168448
|
+
x += (x << 15u);
|
|
168449
|
+
return x;
|
|
168450
|
+
}
|
|
168451
|
+
float cloudRandom(vec2 pixel, float seed) {
|
|
168452
|
+
uvec3 v = uvec3(uvec2(pixel), uint(seed));
|
|
168453
|
+
uint hash = cloudJenkins(v.x ^ cloudJenkins(v.y ^ cloudJenkins(v.z)));
|
|
168454
|
+
return uintBitsToFloat((hash & 0x007FFFFFu) | 0x3F800000u) - 1.0;
|
|
168455
|
+
}
|
|
168456
|
+
void main(void) {
|
|
168457
|
+
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
|
168458
|
+
vec4 farPoint = cloudInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
|
|
168459
|
+
vec3 direction = normalize(farPoint.xyz / farPoint.w - cloudEye.xyz);
|
|
168460
|
+
float maxRayLength = MAX_SKYBOX_DISTANCE;
|
|
168461
|
+
if (cloudCamera.y > 0.5) {
|
|
168462
|
+
float viewDepth = textureLod(sceneDepth, vUV, 0.0).r;
|
|
168463
|
+
if (viewDepth < cloudCamera.x * 0.999) {
|
|
168464
|
+
#ifdef CLOUD_LOCAL
|
|
168465
|
+
maxRayLength = viewDepth / max(dot(direction, cloudForward.xyz), 0.0001);
|
|
168466
|
+
#else
|
|
168467
|
+
return;
|
|
168468
|
+
#endif
|
|
168469
|
+
}
|
|
168470
|
+
}
|
|
168471
|
+
#ifdef CLOUD_LOCAL
|
|
168472
|
+
vec3 originWS = cloudEye.xyz;
|
|
168473
|
+
#else
|
|
168474
|
+
vec3 originWS = vec3(0.0);
|
|
168475
|
+
#endif
|
|
168476
|
+
float rangeStart = 0.0;
|
|
168477
|
+
float rangeEnd = 0.0;
|
|
168478
|
+
if (!cloudRange(originWS, direction, rangeStart, rangeEnd)) return;
|
|
168479
|
+
if (maxRayLength < rangeStart) return;
|
|
168480
|
+
float integrationNoise = cloudRandom(gl_FragCoord.xy, cloudCamera.z);
|
|
168481
|
+
float primarySteps = cloudLayer.w;
|
|
168482
|
+
float totalDistance = min(rangeEnd, maxRayLength) - rangeStart;
|
|
168483
|
+
float stepSize = min(totalDistance / primarySteps, cloudLayer.z);
|
|
168484
|
+
totalDistance = stepSize * primarySteps;
|
|
168485
|
+
float layerThickness = max(1.0, cloudLayer.y - cloudLayer.x);
|
|
168486
|
+
float sampleMultiplier = min(max(1.0, totalDistance / layerThickness), max(1.0, cloudSteps.x));
|
|
168487
|
+
int primaryStepCount = min(int(ceil(primarySteps * sampleMultiplier)), MAX_PRIMARY_STEPS);
|
|
168488
|
+
stepSize = totalDistance / float(max(1, primaryStepCount));
|
|
168489
|
+
vec3 sunDirection = cloudSunDirection.xyz;
|
|
168490
|
+
float cosAngle = dot(direction, sunDirection);
|
|
168491
|
+
vec2 phase = vec2(
|
|
168492
|
+
cloudPhase(FORWARD_ECCENTRICITY, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY, cosAngle),
|
|
168493
|
+
cloudPhase(FORWARD_ECCENTRICITY * cloudScattering.x, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY * cloudScattering.x, cosAngle));
|
|
168494
|
+
vec3 scattering = vec3(0.0);
|
|
168495
|
+
float ambient = 0.0;
|
|
168496
|
+
float transmittance = 1.0;
|
|
168497
|
+
float meanDistance = 0.0;
|
|
168498
|
+
float meanDistanceDivider = 0.0;
|
|
168499
|
+
float currentDistance = integrationNoise;
|
|
168500
|
+
vec3 currentPositionWS = originWS + (rangeStart + currentDistance) * direction;
|
|
168501
|
+
bool activeSampling = true;
|
|
168502
|
+
int sequentialEmptySamples = 0;
|
|
168503
|
+
int currentIndex = 0;
|
|
168504
|
+
for (int iteration = 0; iteration < MAX_PRIMARY_STEPS * 2; iteration++) {
|
|
168505
|
+
if (currentIndex >= primaryStepCount || currentDistance >= totalDistance) break;
|
|
168506
|
+
float cameraDistance = rangeStart + currentDistance;
|
|
168507
|
+
float densityFade = clamp((cameraDistance - cloudSteps.z) / (cloudSteps.z + cloudSteps.w), 0.0, 1.0);
|
|
168508
|
+
float erosionMip = mix(0.0, 4.0, clamp((cameraDistance - MIN_EROSION_DISTANCE) / (MAX_EROSION_DISTANCE - MIN_EROSION_DISTANCE), 0.0, 1.0));
|
|
168509
|
+
vec3 currentPositionPS = currentPositionWS - cloudPlanet.xyz;
|
|
168510
|
+
if (activeSampling) {
|
|
168511
|
+
CloudProperties properties = cloudPropertiesAt(currentPositionPS, 0.0, erosionMip, false, false);
|
|
168512
|
+
properties.density *= densityFade;
|
|
168513
|
+
if (properties.density > CLOUD_DENSITY_THRESHOLD) {
|
|
168514
|
+
float transmittanceDensity = transmittance * properties.density;
|
|
168515
|
+
meanDistance += cameraDistance * transmittanceDensity;
|
|
168516
|
+
meanDistanceDivider += transmittanceDensity;
|
|
168517
|
+
float stepTransmittance = exp(-properties.density * properties.sigmaT * stepSize);
|
|
168518
|
+
float powder = cloudPowder(properties.density, cosAngle, cloudScattering.y);
|
|
168519
|
+
vec3 sunLuminance = cloudSunTransmittance(currentPositionPS, sunDirection, phase) * powder;
|
|
168520
|
+
float absorbed = transmittance - transmittance * stepTransmittance;
|
|
168521
|
+
scattering += sunLuminance * absorbed;
|
|
168522
|
+
ambient += properties.ambientOcclusion * absorbed;
|
|
168523
|
+
transmittance *= stepTransmittance;
|
|
168524
|
+
if (transmittance < 0.003) {
|
|
168525
|
+
transmittance = 0.0;
|
|
168526
|
+
break;
|
|
168527
|
+
}
|
|
168528
|
+
sequentialEmptySamples = 0;
|
|
168529
|
+
} else {
|
|
168530
|
+
sequentialEmptySamples++;
|
|
168531
|
+
}
|
|
168532
|
+
if (sequentialEmptySamples == EMPTY_STEPS_BEFORE_LARGE_STEPS) activeSampling = false;
|
|
168533
|
+
float relativeStepSize = mix(integrationNoise, 1.0, clamp(float(currentIndex), 0.0, 1.0));
|
|
168534
|
+
currentPositionWS += direction * stepSize * relativeStepSize;
|
|
168535
|
+
currentDistance += stepSize * relativeStepSize;
|
|
168536
|
+
} else {
|
|
168537
|
+
CloudProperties properties = cloudPropertiesAt(currentPositionPS, 1.0, 0.0, true, false);
|
|
168538
|
+
properties.density *= densityFade;
|
|
168539
|
+
if (properties.density < CLOUD_DENSITY_THRESHOLD) {
|
|
168540
|
+
currentPositionWS += direction * stepSize * 2.0;
|
|
168541
|
+
currentDistance += stepSize * 2.0;
|
|
168542
|
+
} else {
|
|
168543
|
+
currentPositionWS -= direction * stepSize;
|
|
168544
|
+
currentDistance -= stepSize;
|
|
168545
|
+
currentIndex -= 1;
|
|
168546
|
+
activeSampling = true;
|
|
168547
|
+
sequentialEmptySamples = 0;
|
|
168548
|
+
}
|
|
168549
|
+
}
|
|
168550
|
+
currentIndex++;
|
|
168551
|
+
}
|
|
168552
|
+
if (meanDistance == 0.0) return;
|
|
168553
|
+
meanDistance = min(meanDistance / meanDistanceDivider, maxRayLength);
|
|
168554
|
+
float relativeHeight = cloudNormalizedHeight(originWS - cloudPlanet.xyz + meanDistance * direction);
|
|
168555
|
+
vec3 ambientColor = max(vec3(0.0), mix(cloudAmbientBottom.rgb, cloudAmbientTop.rgb, relativeHeight));
|
|
168556
|
+
vec3 color = (cloudSunColor.rgb * scattering + ambientColor * ambient) * cloudCamera.w;
|
|
168557
|
+
#ifdef CLOUD_AERIAL
|
|
168558
|
+
if (cloudApCamera.w > 0.5) {
|
|
168559
|
+
vec3 apDirection = vec3(direction.x * cloudForward.w, direction.y, direction.z);
|
|
168560
|
+
vec3 apColor = vec3(0.0);
|
|
168561
|
+
vec3 apOpacity = vec3(0.0);
|
|
168562
|
+
fogAerialPerspective(vUV, cloudApCamera.xyz, apDirection, meanDistance, apColor, apOpacity);
|
|
168563
|
+
color = apColor * (1.0 - transmittance) + (vec3(1.0) - apOpacity) * color;
|
|
168564
|
+
}
|
|
168565
|
+
#endif
|
|
168566
|
+
gl_FragColor = vec4(color, transmittance);
|
|
168567
|
+
}
|
|
168568
|
+
`;
|
|
168569
|
+
}
|
|
168570
|
+
static GlslCompositeVertex() {
|
|
168571
|
+
return `precision highp float;
|
|
168572
|
+
attribute vec3 position;
|
|
168573
|
+
varying vec2 vUV;
|
|
168574
|
+
void main(void) {
|
|
168575
|
+
vUV = position.xy * 0.5 + 0.5;
|
|
168576
|
+
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
168577
|
+
}
|
|
168578
|
+
`;
|
|
168579
|
+
}
|
|
168580
|
+
static GlslCompositeFragment() {
|
|
168581
|
+
return `precision highp float;
|
|
168582
|
+
varying vec2 vUV;
|
|
168583
|
+
uniform sampler2D cloudLighting;
|
|
168584
|
+
uniform float cloudDisplayEncoded;
|
|
168585
|
+
const float CLOUD_DISPLAY_GAMMA = 2.2;
|
|
168586
|
+
const vec3 CLOUD_LUMINANCE = vec3(0.2126, 0.7152, 0.0722);
|
|
168587
|
+
void main(void) {
|
|
168588
|
+
vec4 cloud = texture2D(cloudLighting, vUV);
|
|
168589
|
+
vec3 scattering = cloud.rgb;
|
|
168590
|
+
float throughput = cloud.a;
|
|
168591
|
+
if (cloudDisplayEncoded > 0.5) {
|
|
168592
|
+
float encodeExponent = 1.0 / CLOUD_DISPLAY_GAMMA;
|
|
168593
|
+
float scatteringLuminance = clamp(dot(scattering, CLOUD_LUMINANCE), 0.0, 1.0);
|
|
168594
|
+
throughput = pow(min(cloud.a + scatteringLuminance, 1.0), encodeExponent) - pow(scatteringLuminance, encodeExponent);
|
|
168595
|
+
scattering = pow(clamp(scattering, vec3(0.0), vec3(1.0)), vec3(encodeExponent));
|
|
168596
|
+
}
|
|
168597
|
+
gl_FragColor = vec4(scattering, 1.0 - throughput);
|
|
168598
|
+
}
|
|
168599
|
+
`;
|
|
168600
|
+
}
|
|
168601
|
+
static GlslTemporal() {
|
|
168602
|
+
const shaders = VolumetricCloudsShaders;
|
|
168603
|
+
return "precision highp float;\nvarying vec2 vUV;\nuniform sampler2D cloudCurrent;\nuniform sampler2D cloudHistory;\nuniform sampler2D sceneDepth;\n"
|
|
168604
|
+
+ shaders.TemporalUniforms.map((name) => (name.indexOf("ViewProj") >= 0) ? "uniform mat4 " + name + ";" : "uniform vec4 " + name + ";").join("\n") + "\n"
|
|
168605
|
+
+ shaders.GlslGeometry() + `
|
|
168606
|
+
float cloudReprojectionDistance(vec3 direction) {
|
|
168607
|
+
float distanceToCloud = 0.0;
|
|
168608
|
+
float exitDistance = 0.0;
|
|
168609
|
+
#ifdef CLOUD_LOCAL
|
|
168610
|
+
if (!cloudIntersectVolume(cloudEye.xyz - cloudPlanet.xyz, direction, distanceToCloud, exitDistance)) distanceToCloud = exitDistance;
|
|
168611
|
+
#else
|
|
168612
|
+
cloudRange(vec3(0.0), direction, distanceToCloud, exitDistance);
|
|
168613
|
+
#endif
|
|
168614
|
+
distanceToCloud = max(distanceToCloud, 1.0);
|
|
168615
|
+
if (cloudCamera.y > 0.5) {
|
|
168616
|
+
float viewDepth = textureLod(sceneDepth, vUV, 0.0).r;
|
|
168617
|
+
if (viewDepth < cloudCamera.x * 0.999) distanceToCloud = min(distanceToCloud, viewDepth / max(dot(direction, cloudForward.xyz), 0.0001));
|
|
168618
|
+
}
|
|
168619
|
+
return distanceToCloud;
|
|
168620
|
+
}
|
|
168621
|
+
void main(void) {
|
|
168622
|
+
vec4 current = textureLod(cloudCurrent, vUV, 0.0);
|
|
168623
|
+
gl_FragColor = current;
|
|
168624
|
+
if (cloudCamera.z < 0.5 || current.a >= 1.0) return;
|
|
168625
|
+
vec3 boxMin = current.rgb;
|
|
168626
|
+
vec3 boxMax = current.rgb;
|
|
168627
|
+
vec3 neighbour = textureLod(cloudCurrent, vUV + vec2(0.0, -cloudTexel.y), 0.0).rgb;
|
|
168628
|
+
boxMin = min(boxMin, neighbour);
|
|
168629
|
+
boxMax = max(boxMax, neighbour);
|
|
168630
|
+
neighbour = textureLod(cloudCurrent, vUV + vec2(-cloudTexel.x, 0.0), 0.0).rgb;
|
|
168631
|
+
boxMin = min(boxMin, neighbour);
|
|
168632
|
+
boxMax = max(boxMax, neighbour);
|
|
168633
|
+
neighbour = textureLod(cloudCurrent, vUV + vec2(cloudTexel.x, 0.0), 0.0).rgb;
|
|
168634
|
+
boxMin = min(boxMin, neighbour);
|
|
168635
|
+
boxMax = max(boxMax, neighbour);
|
|
168636
|
+
neighbour = textureLod(cloudCurrent, vUV + vec2(0.0, cloudTexel.y), 0.0).rgb;
|
|
168637
|
+
boxMin = min(boxMin, neighbour);
|
|
168638
|
+
boxMax = max(boxMax, neighbour);
|
|
168639
|
+
vec4 farPoint = cloudInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
|
|
168640
|
+
vec3 direction = normalize(farPoint.xyz / farPoint.w - cloudEye.xyz);
|
|
168641
|
+
vec3 worldPosition = cloudEye.xyz + direction * cloudReprojectionDistance(direction);
|
|
168642
|
+
vec4 previousClip = cloudPrevViewProj * vec4(worldPosition, 1.0);
|
|
168643
|
+
vec2 previousUV = previousClip.xy / previousClip.w * 0.5 + 0.5;
|
|
168644
|
+
vec2 velocity = previousUV - vUV;
|
|
168645
|
+
if (previousUV.x > 1.0 || previousUV.x < 0.0 || previousUV.y > 1.0 || previousUV.y < 0.0) return;
|
|
168646
|
+
vec3 previous = clamp(textureLod(cloudHistory, previousUV, 0.0).rgb, boxMin, boxMax);
|
|
168647
|
+
float accumulation = cloudCamera.w;
|
|
168648
|
+
float intensity = clamp(min(accumulation - abs(velocity.x) * accumulation, accumulation - abs(velocity.y) * accumulation), 0.0, 1.0);
|
|
168649
|
+
gl_FragColor = vec4(mix(current.rgb, previous, intensity), current.a);
|
|
168650
|
+
}
|
|
168651
|
+
`;
|
|
168652
|
+
}
|
|
168653
|
+
static WgslMarchUniforms() {
|
|
168654
|
+
return VolumetricCloudsShaders.MarchUniforms.map((name) => (name === "cloudInvViewProj") ? "uniform " + name + ": mat4x4f;" : "uniform " + name + ": vec4f;").join("\n") + "\n";
|
|
168655
|
+
}
|
|
168656
|
+
static WgslConstants() {
|
|
168657
|
+
return `const CLOUD_DENSITY_THRESHOLD: f32 = 0.001;
|
|
168658
|
+
const EMPTY_STEPS_BEFORE_LARGE_STEPS: i32 = 8;
|
|
168659
|
+
const FORWARD_ECCENTRICITY: f32 = 0.7;
|
|
168660
|
+
const BACKWARD_ECCENTRICITY: f32 = 0.7;
|
|
168661
|
+
const MIN_EROSION_DISTANCE: f32 = 3000.0;
|
|
168662
|
+
const MAX_EROSION_DISTANCE: f32 = 100000.0;
|
|
168663
|
+
const NOISE_NORMALIZATION: f32 = 100000.0;
|
|
168664
|
+
const MAX_SKYBOX_DISTANCE: f32 = 200000.0;
|
|
168665
|
+
const LIGHT_STEP_MAXIMAL_SIZE: f32 = 1000.0;
|
|
168666
|
+
const CLOUD_PI: f32 = 3.14159265359;
|
|
168667
|
+
const MAX_PRIMARY_STEPS: i32 = 1024;
|
|
168668
|
+
const MAX_LIGHT_STEPS: i32 = 16;
|
|
168669
|
+
`;
|
|
168670
|
+
}
|
|
168671
|
+
static WgslGeometry() {
|
|
168672
|
+
return `struct CloudRange {
|
|
168673
|
+
hit: bool,
|
|
168674
|
+
entryDistance: f32,
|
|
168675
|
+
exitDistance: f32,
|
|
168676
|
+
};
|
|
168677
|
+
fn cloudIntersectSphere(sphereRadius: f32, cosChi: f32, radialDistance: f32) -> vec2f {
|
|
168678
|
+
let ratio: f32 = sphereRadius / radialDistance;
|
|
168679
|
+
let d: f32 = ratio * ratio - clamp(1.0 - cosChi * cosChi, 0.0, 1.0);
|
|
168680
|
+
if (d < 0.0) { return vec2f(d); }
|
|
168681
|
+
return radialDistance * vec2f(-cosChi - sqrt(d), -cosChi + sqrt(d));
|
|
168682
|
+
}
|
|
168683
|
+
fn cloudHorizonCosine(radialDistance: f32) -> f32 {
|
|
168684
|
+
let sinHorizon: f32 = uniforms.cloudPlanet.w / radialDistance;
|
|
168685
|
+
return -sqrt(clamp(1.0 - sinHorizon * sinHorizon, 0.0, 1.0));
|
|
168686
|
+
}
|
|
168687
|
+
fn cloudIntersectVolume(originPS: vec3f, direction: vec3f) -> CloudRange {
|
|
168688
|
+
let radialDistance: f32 = length(originPS);
|
|
168689
|
+
let cosChi: f32 = dot(originPS, direction) / radialDistance;
|
|
168690
|
+
let inner: vec2f = cloudIntersectSphere(uniforms.cloudLayer.x, cosChi, radialDistance);
|
|
168691
|
+
let outer: vec2f = cloudIntersectSphere(uniforms.cloudLayer.y, cosChi, radialDistance);
|
|
168692
|
+
if (inner.x < 0.0 && inner.y >= 0.0) {
|
|
168693
|
+
return CloudRange(cosChi >= cloudHorizonCosine(radialDistance), inner.y, outer.y);
|
|
168694
|
+
}
|
|
168695
|
+
return CloudRange(outer.y >= 0.0, max(outer.x, 0.0), select(outer.y, inner.x, inner.x >= 0.0));
|
|
168696
|
+
}
|
|
168697
|
+
fn cloudRaySphere(startWS: vec3f, direction: vec3f, radius: f32) -> vec2f {
|
|
168698
|
+
let startPS: vec3f = startWS + vec3f(0.0, uniforms.cloudPlanet.w, 0.0);
|
|
168699
|
+
let a: f32 = dot(direction, direction);
|
|
168700
|
+
let b: f32 = 2.0 * dot(direction, startPS);
|
|
168701
|
+
let c: f32 = dot(startPS, startPS) - radius * radius;
|
|
168702
|
+
let d: f32 = b * b - 4.0 * a * c;
|
|
168703
|
+
var result: vec2f = vec2f(0.0);
|
|
168704
|
+
if (d >= 0.0) {
|
|
168705
|
+
let q: f32 = -0.5 * (b + select(-1.0, 1.0, b >= 0.0) * sqrt(d));
|
|
168706
|
+
result = vec2f(c / q, q / a);
|
|
168707
|
+
if (result.x < 0.0) { result.x = result.y; }
|
|
168708
|
+
}
|
|
168709
|
+
return result;
|
|
168710
|
+
}
|
|
168711
|
+
fn cloudRange(originWS: vec3f, direction: vec3f) -> CloudRange {
|
|
168712
|
+
#ifdef CLOUD_LOCAL
|
|
168713
|
+
return cloudIntersectVolume(originWS - uniforms.cloudPlanet.xyz, direction);
|
|
168714
|
+
#else
|
|
168715
|
+
let inner: vec2f = cloudRaySphere(originWS, direction, uniforms.cloudLayer.x);
|
|
168716
|
+
let outer: vec2f = cloudRaySphere(originWS, direction, uniforms.cloudLayer.y);
|
|
168717
|
+
return CloudRange(true, inner.x, outer.x);
|
|
168718
|
+
#endif
|
|
168719
|
+
}
|
|
168720
|
+
`;
|
|
168721
|
+
}
|
|
168722
|
+
static WgslDensity() {
|
|
168723
|
+
return `struct CloudProperties {
|
|
168724
|
+
density: f32,
|
|
168725
|
+
ambientOcclusion: f32,
|
|
168726
|
+
height: f32,
|
|
168727
|
+
sigmaT: f32,
|
|
168728
|
+
};
|
|
168729
|
+
fn cloudRemap(x: f32, a: f32, b: f32, c: f32, d: f32) -> f32 {
|
|
168730
|
+
return (((x - a) / (b - a)) * (d - c)) + c;
|
|
168731
|
+
}
|
|
168732
|
+
fn cloudNormalizedHeight(positionPS: vec3f) -> f32 {
|
|
168733
|
+
return clamp((length(positionPS) - uniforms.cloudLayer.x) / (uniforms.cloudLayer.y - uniforms.cloudLayer.x), 0.0, 1.0);
|
|
168734
|
+
}
|
|
168735
|
+
fn cloudAnimateShape(positionPS: vec3f) -> vec3f {
|
|
168736
|
+
var position: vec3f = positionPS;
|
|
168737
|
+
position.y = position.y + position.x / 3.0 + position.z / 7.0;
|
|
168738
|
+
return position + vec3f(uniforms.cloudWind.z, 0.0, uniforms.cloudWind.w) * uniforms.cloudWindSpeed.x + vec3f(0.0, uniforms.cloudWindSpeed.z, 0.0);
|
|
168739
|
+
}
|
|
168740
|
+
fn cloudAnimateErosion(positionPS: vec3f) -> vec3f {
|
|
168741
|
+
return positionPS + vec3f(uniforms.cloudWind.z, 0.0, uniforms.cloudWind.w) * uniforms.cloudWindSpeed.y + vec3f(0.0, uniforms.cloudWindSpeed.w, 0.0);
|
|
168742
|
+
}
|
|
168743
|
+
fn cloudCoverageHash(cell: vec2f) -> f32 {
|
|
168744
|
+
var p: vec3f = fract(vec3f(cell.x, cell.y, cell.x) * 0.1031);
|
|
168745
|
+
p = p + vec3f(dot(p, p.yzx + vec3f(33.33)));
|
|
168746
|
+
return fract((p.x + p.y) * p.z);
|
|
168747
|
+
}
|
|
168748
|
+
fn cloudCoverageNoise(position: vec2f) -> f32 {
|
|
168749
|
+
let cell: vec2f = floor(position);
|
|
168750
|
+
var blend: vec2f = fract(position);
|
|
168751
|
+
blend = blend * blend * (vec2f(3.0) - 2.0 * blend);
|
|
168752
|
+
let bottom: f32 = mix(cloudCoverageHash(cell), cloudCoverageHash(cell + vec2f(1.0, 0.0)), blend.x);
|
|
168753
|
+
let top: f32 = mix(cloudCoverageHash(cell + vec2f(0.0, 1.0)), cloudCoverageHash(cell + vec2f(1.0, 1.0)), blend.x);
|
|
168754
|
+
return mix(bottom, top, blend.y);
|
|
168755
|
+
}
|
|
168756
|
+
fn cloudCoverageAt(positionPS: vec3f) -> vec2f {
|
|
168757
|
+
var coverage: f32 = 0.9;
|
|
168758
|
+
var shapeFill: f32 = 0.0;
|
|
168759
|
+
if (uniforms.cloudCoverage.x != 0.0) {
|
|
168760
|
+
let coordinates: vec2f = (positionPS.xz + uniforms.cloudWind.zw * uniforms.cloudWindSpeed.x + uniforms.cloudCoverageOffset.xy) * uniforms.cloudCoverage.y;
|
|
168761
|
+
let coverageNoise: f32 = cloudCoverageNoise(coordinates) * 0.7 + cloudCoverageNoise(coordinates * 2.03 + vec2f(17.13, 43.71)) * 0.3;
|
|
168762
|
+
let threshold: f32 = mix(-0.15, 1.15, uniforms.cloudCoverage.z);
|
|
168763
|
+
let mask: f32 = smoothstep(threshold - 0.12, threshold + 0.12, coverageNoise);
|
|
168764
|
+
if (uniforms.cloudCoverage.x > 0.0) {
|
|
168765
|
+
coverage = coverage * mix(1.0, mask, uniforms.cloudCoverage.x);
|
|
168766
|
+
} else {
|
|
168767
|
+
shapeFill = clamp(-uniforms.cloudCoverage.x, 0.0, 1.0) * (1.0 - mask);
|
|
168768
|
+
coverage = mix(coverage, 1.0, shapeFill);
|
|
168769
|
+
}
|
|
168770
|
+
}
|
|
168771
|
+
return vec2f(coverage, shapeFill);
|
|
168772
|
+
}
|
|
168773
|
+
fn cloudPropertiesAt(samplePositionPS: vec3f, noiseMip: f32, erosionMip: f32, cheapVersion: bool, lightSampling: bool) -> CloudProperties {
|
|
168774
|
+
var properties: CloudProperties = CloudProperties(0.0, 0.0, 0.0, 0.0);
|
|
168775
|
+
var positionPS: vec3f = samplePositionPS;
|
|
168776
|
+
#ifndef CLOUD_LOCAL
|
|
168777
|
+
if (positionPS.y < uniforms.cloudPlanet.w) { return properties; }
|
|
168778
|
+
#endif
|
|
168779
|
+
properties.ambientOcclusion = 1.0;
|
|
168780
|
+
properties.height = cloudNormalizedHeight(positionPS);
|
|
168781
|
+
#ifndef CLOUD_LOCAL
|
|
168782
|
+
positionPS = vec3f(positionPS.x + uniforms.cloudEye.x, positionPS.y, positionPS.z + uniforms.cloudEye.z);
|
|
168783
|
+
#endif
|
|
168784
|
+
var baseCoordinates: vec3f = (cloudAnimateShape(positionPS).xzy / NOISE_NORMALIZATION) * uniforms.cloudShape.x - uniforms.cloudShapeOffset.xyz;
|
|
168785
|
+
baseCoordinates = baseCoordinates + properties.height * vec3f(uniforms.cloudWind.x, uniforms.cloudWind.y, 0.0) * uniforms.cloudShapeOffset.w;
|
|
168786
|
+
var lowFrequencyNoise: f32 = textureSampleLevel(worleyNoise, worleyNoiseSampler, baseCoordinates, noiseMip).r;
|
|
168787
|
+
let coverage: vec2f = cloudCoverageAt(positionPS);
|
|
168788
|
+
if (coverage.x <= CLOUD_DENSITY_THRESHOLD) { return properties; }
|
|
168789
|
+
let curves: vec3f = textureSampleLevel(cloudCurves, cloudCurvesSampler, vec2f(properties.height, 0.5), 0.0).xyz;
|
|
168790
|
+
let shapeFactor: f32 = mix(0.1, 1.0, uniforms.cloudShape.y * (1.0 - coverage.y)) * curves.y;
|
|
168791
|
+
let erosionFactor: f32 = uniforms.cloudShape.w * curves.y;
|
|
168792
|
+
let microDetailFactor: f32 = uniforms.cloudDetail.z * curves.y;
|
|
168793
|
+
lowFrequencyNoise = mix(1.0, lowFrequencyNoise, shapeFactor);
|
|
168794
|
+
var baseCloud: f32 = 1.0 - curves.x * coverage.x * (1.0 - shapeFactor);
|
|
168795
|
+
baseCloud = clamp(cloudRemap(lowFrequencyNoise, baseCloud, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x * coverage.x;
|
|
168796
|
+
properties.sigmaT = 0.04;
|
|
168797
|
+
let occlusionBlend: f32 = clamp(1.0 - max(erosionFactor, shapeFactor) * 0.5, 0.0, 1.0);
|
|
168798
|
+
properties.ambientOcclusion = mix(1.0, curves.z, occlusionBlend);
|
|
168799
|
+
if (!cheapVersion) {
|
|
168800
|
+
let erosionCoordinates: vec3f = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * uniforms.cloudShape.z;
|
|
168801
|
+
var erosionValue: f32 = 1.0 - textureSampleLevel(erosionNoise, erosionNoiseSampler, erosionCoordinates, erosionMip).r;
|
|
168802
|
+
erosionValue = mix(0.0, erosionValue, erosionFactor * 0.75 * coverage.x);
|
|
168803
|
+
properties.ambientOcclusion = clamp(properties.ambientOcclusion - sqrt(erosionValue * uniforms.cloudDetail.x), 0.0, 1.0);
|
|
168804
|
+
baseCloud = cloudRemap(baseCloud, erosionValue, 1.0, 0.0, 1.0);
|
|
168805
|
+
#ifdef CLOUD_MICRO_EROSION
|
|
168806
|
+
let fineCoordinates: vec3f = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * uniforms.cloudDetail.y;
|
|
168807
|
+
var fineValue: f32 = 1.0 - textureSampleLevel(erosionNoise, erosionNoiseSampler, fineCoordinates, erosionMip).r;
|
|
168808
|
+
fineValue = mix(0.0, fineValue, microDetailFactor * 0.5 * coverage.x);
|
|
168809
|
+
baseCloud = cloudRemap(baseCloud, fineValue, 1.0, 0.0, 1.0);
|
|
168810
|
+
#endif
|
|
168811
|
+
}
|
|
168812
|
+
if (lightSampling) {
|
|
168813
|
+
baseCloud = baseCloud - erosionFactor * 0.1;
|
|
168814
|
+
#ifdef CLOUD_MICRO_EROSION
|
|
168815
|
+
baseCloud = baseCloud - microDetailFactor * 0.15;
|
|
168816
|
+
#endif
|
|
168817
|
+
}
|
|
168818
|
+
properties.density = max(0.0, baseCloud) * uniforms.cloudDetail.w;
|
|
168819
|
+
return properties;
|
|
168820
|
+
}
|
|
168821
|
+
`;
|
|
168822
|
+
}
|
|
168823
|
+
static WgslLighting() {
|
|
168824
|
+
return `fn cloudPhase(anisotropy: f32, cosTheta: f32) -> f32 {
|
|
168825
|
+
let denominator: f32 = 1.0 + anisotropy * anisotropy - 2.0 * anisotropy * cosTheta;
|
|
168826
|
+
let falloff: f32 = inverseSqrt(max(denominator, 1e-6));
|
|
168827
|
+
return (1.0 / (4.0 * CLOUD_PI)) * (1.0 - anisotropy * anisotropy) * falloff * falloff * falloff;
|
|
168828
|
+
}
|
|
168829
|
+
fn cloudPowder(density: f32, cosAngle: f32, intensity: f32) -> f32 {
|
|
168830
|
+
let powder: f32 = clamp((1.0 - exp(-density * 4.0)) * 2.0, 0.0, 1.0);
|
|
168831
|
+
return mix(1.0, mix(1.0, powder, smoothstep(0.5, -0.5, cosAngle)), intensity);
|
|
168832
|
+
}
|
|
168833
|
+
fn cloudSunTransmittance(positionPS: vec3f, sunDirection: vec3f, phase: vec2f) -> vec3f {
|
|
168834
|
+
let radialDistance: f32 = length(positionPS);
|
|
168835
|
+
let cosChi: f32 = dot(positionPS, sunDirection) / radialDistance;
|
|
168836
|
+
var transmittance: vec3f = vec3f(0.0);
|
|
168837
|
+
if (cosChi < cloudHorizonCosine(radialDistance)) { return transmittance; }
|
|
168838
|
+
let lightSteps: f32 = uniforms.cloudSteps.y;
|
|
168839
|
+
let totalLightDistance: f32 = clamp(cloudIntersectSphere(uniforms.cloudLayer.y, cosChi, radialDistance).y, 0.0, lightSteps * LIGHT_STEP_MAXIMAL_SIZE) + 5.0;
|
|
168840
|
+
let intervalSize: f32 = totalLightDistance / lightSteps;
|
|
168841
|
+
var opticalDepth: f32 = 0.0;
|
|
168842
|
+
for (var j: i32 = 0; j < MAX_LIGHT_STEPS; j++) {
|
|
168843
|
+
if (f32(j) >= lightSteps) { break; }
|
|
168844
|
+
let distanceAlongLight: f32 = intervalSize * (0.25 + f32(j));
|
|
168845
|
+
let lightProperties: CloudProperties = cloudPropertiesAt(positionPS + sunDirection * distanceAlongLight, 3.0 * f32(j) / lightSteps, 0.0, true, true);
|
|
168846
|
+
opticalDepth = opticalDepth + lightProperties.density * lightProperties.sigmaT;
|
|
168847
|
+
}
|
|
168848
|
+
let extinction: vec3f = intervalSize * opticalDepth * uniforms.cloudTint.rgb;
|
|
168849
|
+
transmittance = transmittance + exp(-extinction) * phase.x;
|
|
168850
|
+
transmittance = transmittance + exp(-extinction * uniforms.cloudScattering.x) * (phase.y * uniforms.cloudScattering.x);
|
|
168851
|
+
return transmittance;
|
|
168852
|
+
}
|
|
168853
|
+
`;
|
|
168854
|
+
}
|
|
168855
|
+
static WgslAerial() {
|
|
168856
|
+
const skyShaders = HdrpPbSkyShaders;
|
|
168857
|
+
const fogPass = HdrpFogPass;
|
|
168858
|
+
if (skyShaders == null || fogPass == null)
|
|
168859
|
+
return "";
|
|
168860
|
+
return "#ifdef CLOUD_AERIAL\nvar apLutSampler: sampler;\nvar apLut: texture_2d_array<f32>;\nuniform cloudApCamera: vec4f;\n"
|
|
168861
|
+
+ skyShaders.WgslUniforms() + skyShaders.WgslCommon() + fogPass.AerialPerspectiveWgsl() + "#endif\n";
|
|
168862
|
+
}
|
|
168863
|
+
static WgslMarch() {
|
|
168864
|
+
const shaders = VolumetricCloudsShaders;
|
|
168865
|
+
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"
|
|
168866
|
+
+ shaders.WgslMarchUniforms() + shaders.WgslConstants() + shaders.WgslGeometry() + shaders.WgslDensity() + shaders.WgslLighting() + shaders.WgslAerial() + `
|
|
168867
|
+
fn cloudJenkins(value: u32) -> u32 {
|
|
168868
|
+
var x: u32 = value;
|
|
168869
|
+
x = x + (x << 10u);
|
|
168870
|
+
x = x ^ (x >> 6u);
|
|
168871
|
+
x = x + (x << 3u);
|
|
168872
|
+
x = x ^ (x >> 11u);
|
|
168873
|
+
x = x + (x << 15u);
|
|
168874
|
+
return x;
|
|
168875
|
+
}
|
|
168876
|
+
fn cloudRandom(pixel: vec2f, seed: f32) -> f32 {
|
|
168877
|
+
let v: vec3u = vec3u(u32(pixel.x), u32(pixel.y), u32(seed));
|
|
168878
|
+
let hash: u32 = cloudJenkins(v.x ^ cloudJenkins(v.y ^ cloudJenkins(v.z)));
|
|
168879
|
+
return bitcast<f32>((hash & 0x007FFFFFu) | 0x3F800000u) - 1.0;
|
|
168880
|
+
}
|
|
168881
|
+
@fragment
|
|
168882
|
+
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
168883
|
+
fragmentOutputs.color = vec4f(0.0, 0.0, 0.0, 1.0);
|
|
168884
|
+
let uv: vec2f = fragmentInputs.vUV;
|
|
168885
|
+
let farPoint: vec4f = uniforms.cloudInvViewProj * vec4f(uv * 2.0 - vec2f(1.0), 1.0, 1.0);
|
|
168886
|
+
let direction: vec3f = normalize(farPoint.xyz / farPoint.w - uniforms.cloudEye.xyz);
|
|
168887
|
+
var maxRayLength: f32 = MAX_SKYBOX_DISTANCE;
|
|
168888
|
+
let viewDepth: f32 = textureSampleLevel(sceneDepth, sceneDepthSampler, uv, 0.0).r;
|
|
168889
|
+
if (uniforms.cloudCamera.y > 0.5 && viewDepth < uniforms.cloudCamera.x * 0.999) {
|
|
168890
|
+
#ifdef CLOUD_LOCAL
|
|
168891
|
+
maxRayLength = viewDepth / max(dot(direction, uniforms.cloudForward.xyz), 0.0001);
|
|
168892
|
+
#else
|
|
168893
|
+
return fragmentOutputs;
|
|
168894
|
+
#endif
|
|
168895
|
+
}
|
|
168896
|
+
#ifdef CLOUD_LOCAL
|
|
168897
|
+
let originWS: vec3f = uniforms.cloudEye.xyz;
|
|
168898
|
+
#else
|
|
168899
|
+
let originWS: vec3f = vec3f(0.0);
|
|
168900
|
+
#endif
|
|
168901
|
+
let marchRange: CloudRange = cloudRange(originWS, direction);
|
|
168902
|
+
if (!marchRange.hit || maxRayLength < marchRange.entryDistance) { return fragmentOutputs; }
|
|
168903
|
+
let rangeStart: f32 = marchRange.entryDistance;
|
|
168904
|
+
let integrationNoise: f32 = cloudRandom(fragmentInputs.position.xy, uniforms.cloudCamera.z);
|
|
168905
|
+
let primarySteps: f32 = uniforms.cloudLayer.w;
|
|
168906
|
+
var totalDistance: f32 = min(marchRange.exitDistance, maxRayLength) - rangeStart;
|
|
168907
|
+
var stepSize: f32 = min(totalDistance / primarySteps, uniforms.cloudLayer.z);
|
|
168908
|
+
totalDistance = stepSize * primarySteps;
|
|
168909
|
+
let layerThickness: f32 = max(1.0, uniforms.cloudLayer.y - uniforms.cloudLayer.x);
|
|
168910
|
+
let sampleMultiplier: f32 = min(max(1.0, totalDistance / layerThickness), max(1.0, uniforms.cloudSteps.x));
|
|
168911
|
+
let primaryStepCount: i32 = min(i32(ceil(primarySteps * sampleMultiplier)), MAX_PRIMARY_STEPS);
|
|
168912
|
+
stepSize = totalDistance / f32(max(1, primaryStepCount));
|
|
168913
|
+
let sunDirection: vec3f = uniforms.cloudSunDirection.xyz;
|
|
168914
|
+
let cosAngle: f32 = dot(direction, sunDirection);
|
|
168915
|
+
let phase: vec2f = vec2f(
|
|
168916
|
+
cloudPhase(FORWARD_ECCENTRICITY, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY, cosAngle),
|
|
168917
|
+
cloudPhase(FORWARD_ECCENTRICITY * uniforms.cloudScattering.x, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY * uniforms.cloudScattering.x, cosAngle));
|
|
168918
|
+
var scattering: vec3f = vec3f(0.0);
|
|
168919
|
+
var ambient: f32 = 0.0;
|
|
168920
|
+
var transmittance: f32 = 1.0;
|
|
168921
|
+
var meanDistance: f32 = 0.0;
|
|
168922
|
+
var meanDistanceDivider: f32 = 0.0;
|
|
168923
|
+
var currentDistance: f32 = integrationNoise;
|
|
168924
|
+
var currentPositionWS: vec3f = originWS + (rangeStart + currentDistance) * direction;
|
|
168925
|
+
var activeSampling: bool = true;
|
|
168926
|
+
var sequentialEmptySamples: i32 = 0;
|
|
168927
|
+
var currentIndex: i32 = 0;
|
|
168928
|
+
for (var iteration: i32 = 0; iteration < MAX_PRIMARY_STEPS * 2; iteration++) {
|
|
168929
|
+
if (currentIndex >= primaryStepCount || currentDistance >= totalDistance) { break; }
|
|
168930
|
+
let cameraDistance: f32 = rangeStart + currentDistance;
|
|
168931
|
+
let densityFade: f32 = clamp((cameraDistance - uniforms.cloudSteps.z) / (uniforms.cloudSteps.z + uniforms.cloudSteps.w), 0.0, 1.0);
|
|
168932
|
+
let erosionMip: f32 = mix(0.0, 4.0, clamp((cameraDistance - MIN_EROSION_DISTANCE) / (MAX_EROSION_DISTANCE - MIN_EROSION_DISTANCE), 0.0, 1.0));
|
|
168933
|
+
let currentPositionPS: vec3f = currentPositionWS - uniforms.cloudPlanet.xyz;
|
|
168934
|
+
if (activeSampling) {
|
|
168935
|
+
var properties: CloudProperties = cloudPropertiesAt(currentPositionPS, 0.0, erosionMip, false, false);
|
|
168936
|
+
properties.density = properties.density * densityFade;
|
|
168937
|
+
if (properties.density > CLOUD_DENSITY_THRESHOLD) {
|
|
168938
|
+
let transmittanceDensity: f32 = transmittance * properties.density;
|
|
168939
|
+
meanDistance = meanDistance + cameraDistance * transmittanceDensity;
|
|
168940
|
+
meanDistanceDivider = meanDistanceDivider + transmittanceDensity;
|
|
168941
|
+
let stepTransmittance: f32 = exp(-properties.density * properties.sigmaT * stepSize);
|
|
168942
|
+
let powder: f32 = cloudPowder(properties.density, cosAngle, uniforms.cloudScattering.y);
|
|
168943
|
+
let sunLuminance: vec3f = cloudSunTransmittance(currentPositionPS, sunDirection, phase) * powder;
|
|
168944
|
+
let absorbed: f32 = transmittance - transmittance * stepTransmittance;
|
|
168945
|
+
scattering = scattering + sunLuminance * absorbed;
|
|
168946
|
+
ambient = ambient + properties.ambientOcclusion * absorbed;
|
|
168947
|
+
transmittance = transmittance * stepTransmittance;
|
|
168948
|
+
if (transmittance < 0.003) {
|
|
168949
|
+
transmittance = 0.0;
|
|
168950
|
+
break;
|
|
168951
|
+
}
|
|
168952
|
+
sequentialEmptySamples = 0;
|
|
168953
|
+
} else {
|
|
168954
|
+
sequentialEmptySamples = sequentialEmptySamples + 1;
|
|
168955
|
+
}
|
|
168956
|
+
if (sequentialEmptySamples == EMPTY_STEPS_BEFORE_LARGE_STEPS) { activeSampling = false; }
|
|
168957
|
+
let relativeStepSize: f32 = mix(integrationNoise, 1.0, clamp(f32(currentIndex), 0.0, 1.0));
|
|
168958
|
+
currentPositionWS = currentPositionWS + direction * stepSize * relativeStepSize;
|
|
168959
|
+
currentDistance = currentDistance + stepSize * relativeStepSize;
|
|
168960
|
+
} else {
|
|
168961
|
+
var properties: CloudProperties = cloudPropertiesAt(currentPositionPS, 1.0, 0.0, true, false);
|
|
168962
|
+
properties.density = properties.density * densityFade;
|
|
168963
|
+
if (properties.density < CLOUD_DENSITY_THRESHOLD) {
|
|
168964
|
+
currentPositionWS = currentPositionWS + direction * stepSize * 2.0;
|
|
168965
|
+
currentDistance = currentDistance + stepSize * 2.0;
|
|
168966
|
+
} else {
|
|
168967
|
+
currentPositionWS = currentPositionWS - direction * stepSize;
|
|
168968
|
+
currentDistance = currentDistance - stepSize;
|
|
168969
|
+
currentIndex = currentIndex - 1;
|
|
168970
|
+
activeSampling = true;
|
|
168971
|
+
sequentialEmptySamples = 0;
|
|
168972
|
+
}
|
|
168973
|
+
}
|
|
168974
|
+
currentIndex = currentIndex + 1;
|
|
168975
|
+
}
|
|
168976
|
+
if (meanDistance == 0.0) { return fragmentOutputs; }
|
|
168977
|
+
meanDistance = min(meanDistance / meanDistanceDivider, maxRayLength);
|
|
168978
|
+
let relativeHeight: f32 = cloudNormalizedHeight(originWS - uniforms.cloudPlanet.xyz + meanDistance * direction);
|
|
168979
|
+
let ambientColor: vec3f = max(vec3f(0.0), mix(uniforms.cloudAmbientBottom.rgb, uniforms.cloudAmbientTop.rgb, relativeHeight));
|
|
168980
|
+
var color: vec3f = (uniforms.cloudSunColor.rgb * scattering + ambientColor * ambient) * uniforms.cloudCamera.w;
|
|
168981
|
+
#ifdef CLOUD_AERIAL
|
|
168982
|
+
if (uniforms.cloudApCamera.w > 0.5) {
|
|
168983
|
+
let apDirection: vec3f = vec3f(direction.x * uniforms.cloudForward.w, direction.y, direction.z);
|
|
168984
|
+
let aerial: FogAerial = fogAerialPerspective(uv, uniforms.cloudApCamera.xyz, apDirection, meanDistance);
|
|
168985
|
+
color = aerial.color * (1.0 - transmittance) + (vec3f(1.0) - aerial.opacity) * color;
|
|
168986
|
+
}
|
|
168987
|
+
#endif
|
|
168988
|
+
fragmentOutputs.color = vec4f(color, transmittance);
|
|
168989
|
+
}
|
|
168990
|
+
`;
|
|
168991
|
+
}
|
|
168992
|
+
static WgslCompositeVertex() {
|
|
168993
|
+
return `attribute position: vec3f;
|
|
168994
|
+
varying vUV: vec2f;
|
|
168995
|
+
@vertex
|
|
168996
|
+
fn main(input: VertexInputs) -> FragmentInputs {
|
|
168997
|
+
vertexOutputs.vUV = vertexInputs.position.xy * 0.5 + vec2f(0.5);
|
|
168998
|
+
vertexOutputs.position = vec4f(vertexInputs.position.xy, 0.0, 1.0);
|
|
168999
|
+
}
|
|
169000
|
+
`;
|
|
169001
|
+
}
|
|
169002
|
+
static WgslCompositeFragment() {
|
|
169003
|
+
return `varying vUV: vec2f;
|
|
169004
|
+
var cloudLightingSampler: sampler;
|
|
169005
|
+
var cloudLighting: texture_2d<f32>;
|
|
169006
|
+
uniform cloudDisplayEncoded: f32;
|
|
169007
|
+
const CLOUD_DISPLAY_GAMMA: f32 = 2.2;
|
|
169008
|
+
const CLOUD_LUMINANCE: vec3f = vec3f(0.2126, 0.7152, 0.0722);
|
|
169009
|
+
@fragment
|
|
169010
|
+
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
169011
|
+
let cloud: vec4f = textureSample(cloudLighting, cloudLightingSampler, fragmentInputs.vUV);
|
|
169012
|
+
var scattering: vec3f = cloud.rgb;
|
|
169013
|
+
var throughput: f32 = cloud.a;
|
|
169014
|
+
if (uniforms.cloudDisplayEncoded > 0.5) {
|
|
169015
|
+
let encodeExponent: f32 = 1.0 / CLOUD_DISPLAY_GAMMA;
|
|
169016
|
+
let scatteringLuminance: f32 = clamp(dot(scattering, CLOUD_LUMINANCE), 0.0, 1.0);
|
|
169017
|
+
throughput = pow(min(cloud.a + scatteringLuminance, 1.0), encodeExponent) - pow(scatteringLuminance, encodeExponent);
|
|
169018
|
+
scattering = pow(clamp(scattering, vec3f(0.0), vec3f(1.0)), vec3f(encodeExponent));
|
|
169019
|
+
}
|
|
169020
|
+
fragmentOutputs.color = vec4f(scattering, 1.0 - throughput);
|
|
169021
|
+
}
|
|
169022
|
+
`;
|
|
169023
|
+
}
|
|
169024
|
+
static WgslTemporal() {
|
|
169025
|
+
const shaders = VolumetricCloudsShaders;
|
|
169026
|
+
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"
|
|
169027
|
+
+ shaders.TemporalUniforms.map((name) => (name.indexOf("ViewProj") >= 0) ? "uniform " + name + ": mat4x4f;" : "uniform " + name + ": vec4f;").join("\n") + "\n"
|
|
169028
|
+
+ shaders.WgslGeometry() + `
|
|
169029
|
+
fn cloudReprojectionDistance(direction: vec3f, viewDepth: f32) -> f32 {
|
|
169030
|
+
#ifdef CLOUD_LOCAL
|
|
169031
|
+
let layerRange: CloudRange = cloudIntersectVolume(uniforms.cloudEye.xyz - uniforms.cloudPlanet.xyz, direction);
|
|
169032
|
+
var distanceToCloud: f32 = select(layerRange.exitDistance, layerRange.entryDistance, layerRange.hit);
|
|
169033
|
+
#else
|
|
169034
|
+
let layerRange: CloudRange = cloudRange(vec3f(0.0), direction);
|
|
169035
|
+
var distanceToCloud: f32 = layerRange.entryDistance;
|
|
169036
|
+
#endif
|
|
169037
|
+
distanceToCloud = max(distanceToCloud, 1.0);
|
|
169038
|
+
if (uniforms.cloudCamera.y > 0.5 && viewDepth < uniforms.cloudCamera.x * 0.999) {
|
|
169039
|
+
distanceToCloud = min(distanceToCloud, viewDepth / max(dot(direction, uniforms.cloudForward.xyz), 0.0001));
|
|
169040
|
+
}
|
|
169041
|
+
return distanceToCloud;
|
|
169042
|
+
}
|
|
169043
|
+
@fragment
|
|
169044
|
+
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
169045
|
+
let uv: vec2f = fragmentInputs.vUV;
|
|
169046
|
+
let current: vec4f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv, 0.0);
|
|
169047
|
+
let viewDepth: f32 = textureSampleLevel(sceneDepth, sceneDepthSampler, uv, 0.0).r;
|
|
169048
|
+
var north: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(0.0, -uniforms.cloudTexel.y), 0.0).rgb;
|
|
169049
|
+
var west: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(-uniforms.cloudTexel.x, 0.0), 0.0).rgb;
|
|
169050
|
+
var east: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(uniforms.cloudTexel.x, 0.0), 0.0).rgb;
|
|
169051
|
+
var south: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(0.0, uniforms.cloudTexel.y), 0.0).rgb;
|
|
169052
|
+
fragmentOutputs.color = current;
|
|
169053
|
+
if (uniforms.cloudCamera.z < 0.5 || current.a >= 1.0) { return fragmentOutputs; }
|
|
169054
|
+
let boxMin: vec3f = min(min(min(current.rgb, north), min(west, east)), south);
|
|
169055
|
+
let boxMax: vec3f = max(max(max(current.rgb, north), max(west, east)), south);
|
|
169056
|
+
let farPoint: vec4f = uniforms.cloudInvViewProj * vec4f(uv * 2.0 - vec2f(1.0), 1.0, 1.0);
|
|
169057
|
+
let direction: vec3f = normalize(farPoint.xyz / farPoint.w - uniforms.cloudEye.xyz);
|
|
169058
|
+
let worldPosition: vec3f = uniforms.cloudEye.xyz + direction * cloudReprojectionDistance(direction, viewDepth);
|
|
169059
|
+
let previousClip: vec4f = uniforms.cloudPrevViewProj * vec4f(worldPosition, 1.0);
|
|
169060
|
+
let previousUV: vec2f = previousClip.xy / previousClip.w * 0.5 + vec2f(0.5);
|
|
169061
|
+
let velocity: vec2f = previousUV - uv;
|
|
169062
|
+
let history: vec3f = textureSampleLevel(cloudHistory, cloudHistorySampler, clamp(previousUV, vec2f(0.0), vec2f(1.0)), 0.0).rgb;
|
|
169063
|
+
if (previousUV.x > 1.0 || previousUV.x < 0.0 || previousUV.y > 1.0 || previousUV.y < 0.0) { return fragmentOutputs; }
|
|
169064
|
+
let previous: vec3f = clamp(history, boxMin, boxMax);
|
|
169065
|
+
let accumulation: f32 = uniforms.cloudCamera.w;
|
|
169066
|
+
let intensity: f32 = clamp(min(accumulation - abs(velocity.x) * accumulation, accumulation - abs(velocity.y) * accumulation), 0.0, 1.0);
|
|
169067
|
+
fragmentOutputs.color = vec4f(mix(current.rgb, previous, intensity), current.a);
|
|
169068
|
+
}
|
|
169069
|
+
`;
|
|
169070
|
+
}
|
|
169071
|
+
}
|
|
169072
|
+
VolumetricCloudsShaders.MarchName = "tkCloudMarch";
|
|
169073
|
+
VolumetricCloudsShaders.CompositeName = "tkCloudComposite";
|
|
169074
|
+
VolumetricCloudsShaders.TemporalName = "tkCloudTemporal";
|
|
169075
|
+
VolumetricCloudsShaders.TemporalUniforms = ["cloudInvViewProj", "cloudPrevViewProj", "cloudEye", "cloudForward", "cloudCamera", "cloudPlanet", "cloudLayer", "cloudTexel"];
|
|
169076
|
+
VolumetricCloudsShaders.TemporalSamplers = ["cloudCurrent", "cloudHistory", "sceneDepth"];
|
|
169077
|
+
VolumetricCloudsShaders.MarchUniforms = [
|
|
169078
|
+
"cloudInvViewProj", "cloudEye", "cloudForward", "cloudCamera", "cloudPlanet", "cloudLayer", "cloudSteps", "cloudShape",
|
|
169079
|
+
"cloudDetail", "cloudShapeOffset", "cloudWind", "cloudWindSpeed", "cloudCoverage", "cloudCoverageOffset", "cloudScattering",
|
|
169080
|
+
"cloudTint", "cloudSunDirection", "cloudSunColor", "cloudAmbientTop", "cloudAmbientBottom"
|
|
169081
|
+
];
|
|
169082
|
+
VolumetricCloudsShaders.MarchSamplers = ["worleyNoise", "erosionNoise", "cloudCurves", "sceneDepth"];
|
|
169083
|
+
VolumetricCloudsShaders.AerialSampler = "apLut";
|
|
169084
|
+
VolumetricCloudsShaders.AerialCameraUniform = "cloudApCamera";
|
|
169085
|
+
VolumetricCloudsShaders.registered = false;
|
|
166747
169086
|
export class WebVideoPlayer extends ScriptComponent {
|
|
166748
169087
|
getVideoMaterial() { return this.m_videoMaterial; }
|
|
166749
169088
|
getVideoTexture() { return this.m_videoTexture; }
|
|
@@ -167098,6 +169437,7 @@ SceneManager.RegisterClass("TOOLKIT.HdrpWaterDecalSurface", HdrpWaterDecalSurfac
|
|
|
167098
169437
|
SceneManager.RegisterClass("TOOLKIT.LensFlareComponentSRP", LensFlareComponentSRP);
|
|
167099
169438
|
SceneManager.RegisterClass("TOOLKIT.LegacyLensFlare", LegacyLensFlare);
|
|
167100
169439
|
SceneManager.RegisterClass("TOOLKIT.LightProbeNetwork", LightProbeNetwork);
|
|
169440
|
+
SceneManager.RegisterClass("TOOLKIT.MeshWaterSurface", MeshWaterSurface);
|
|
167101
169441
|
SceneManager.RegisterClass("TOOLKIT.NavigationAgent", NavigationAgent);
|
|
167102
169442
|
Object.defineProperty(PlayerRef, "None", { value: new PlayerRef(0), writable: false });
|
|
167103
169443
|
Object.defineProperty(PlayerRef, "MasterClient", { value: new PlayerRef(-1), writable: false });
|
|
@@ -167129,11 +169469,14 @@ SceneManager.RegisterClass("TOOLKIT.ShurikenParticles", ShurikenParticles);
|
|
|
167129
169469
|
SceneManager.RegisterClass("TOOLKIT.SkinTextureArray", SkinTextureArray);
|
|
167130
169470
|
SceneManager.RegisterClass(SplineAnimate.ClassName, SplineAnimate);
|
|
167131
169471
|
SceneManager.RegisterClass(SplineContainer.ClassName, SplineContainer);
|
|
169472
|
+
const TERRAIN_EDGE_TOLERANCE_METERS = 0.01;
|
|
167132
169473
|
SceneManager.RegisterClass("TOOLKIT.TerrainBuilder", TerrainBuilder);
|
|
167133
169474
|
SceneManager.RegisterClass("TOOLKIT.TreeBranchMaterial", TreeBranchMaterial);
|
|
167134
169475
|
SceneManager.RegisterClass("TOOLKIT.GrassStandardMaterial", GrassStandardMaterial);
|
|
167135
169476
|
SceneManager.RegisterClass("TOOLKIT.GrassBillboardMaterial", GrassBillboardMaterial);
|
|
167136
169477
|
SceneManager.RegisterClass("TOOLKIT.TerrainGrassPlugin", TerrainGrassPlugin);
|
|
169478
|
+
const TREE_INITIAL_CAPACITY = 64;
|
|
169479
|
+
const TREE_GROWTH_FACTOR = 1.5;
|
|
167137
169480
|
SceneManager.RegisterClass("TOOLKIT.TextureAtlasSkin", TextureAtlasSkin);
|
|
167138
169481
|
SceneManager.RegisterClass(ThirdPersonController.ClassName, ThirdPersonController);
|
|
167139
169482
|
SceneManager.RegisterClass("BABYLON.GUI.UnitySlider", UnitySlider);
|
|
@@ -167143,4 +169486,36 @@ SceneManager.RegisterClass("TOOLKIT.UserInterface", UserInterface);
|
|
|
167143
169486
|
SceneManager.RegisterClass("TOOLKIT.VisualEffect", VisualEffect);
|
|
167144
169487
|
const VFX_EPSILON = 1e-5;
|
|
167145
169488
|
const VFX_FLT_MIN = 1.175494351e-38;
|
|
169489
|
+
const CLOUD_ALPHA_INDEX = -1000000;
|
|
169490
|
+
const CLOUD_TIER_WINDOW_FRAMES = 120;
|
|
169491
|
+
const CLOUD_TIER_BUDGET_MS = 33;
|
|
169492
|
+
const CLOUD_TIER_MINIMUM_SAVING = 0.1;
|
|
169493
|
+
const CLOUD_MAX_STEP_STRETCH = 4;
|
|
169494
|
+
const CLOUD_CAMERA_CUT_METERS = 50;
|
|
169495
|
+
const CLOUD_CURVE_SAMPLES = 64;
|
|
169496
|
+
const CLOUD_EARTH_RADIUS_METERS = 6378100;
|
|
169497
|
+
const CLOUD_MINIMUM_CURVATURE_SCALE = 0.025;
|
|
169498
|
+
const CLOUD_WIND_KMH_TO_MS = -0.277778;
|
|
169499
|
+
const CLOUD_WIND_WRAP_METERS = 100000;
|
|
169500
|
+
const CLOUD_MAX_STEP_DIVISOR = 8;
|
|
169501
|
+
const CLOUD_PHONE_WIDTH_CSS_PX = 800;
|
|
169502
|
+
const CLOUD_WEAK_GPU_TEXTURE_SIZE = 8192;
|
|
169503
|
+
const CLOUD_READY_POLL_MS = 16;
|
|
169504
|
+
const CLOUD_MINIMUM_ACCUMULATION = 0.01;
|
|
169505
|
+
const CLOUD_AUTO_FADE_START_FRACTION = 0.2;
|
|
169506
|
+
const CLOUD_AUTO_FADE_DISTANCE_FRACTION = 0.3;
|
|
169507
|
+
const CLOUD_FADE_MODE_MANUAL = 1;
|
|
169508
|
+
const CLOUD_DENSITY_SCALE = 2;
|
|
169509
|
+
const CLOUD_ALTITUDE_DISTORTION_SCALE = 0.25;
|
|
169510
|
+
const CLOUD_MULTI_SCATTERING_SCALE = 0.95;
|
|
169511
|
+
const CLOUD_SCATTERING_TINT_SCALE = 0.75;
|
|
169512
|
+
const CLOUD_MINIMUM_COVERAGE_PATTERN_METERS = 100;
|
|
169513
|
+
const CLOUD_DEFAULT_COVERAGE_PATTERN_METERS = 10000;
|
|
169514
|
+
const CLOUD_DEFAULT_COVERAGE_GAP = 0.45;
|
|
169515
|
+
const CLOUD_DEFAULT_SETTINGS = { resolutionScale: 0.5, primarySteps: 32, longRayMultiplier: 1, lightSteps: 2 };
|
|
169516
|
+
const CLOUD_TIER_CAPS = [
|
|
169517
|
+
{ resolutionScale: Infinity, primarySteps: 64, longRayMultiplier: 2, lightSteps: Infinity },
|
|
169518
|
+
{ resolutionScale: 0.35, primarySteps: 64, longRayMultiplier: 1, lightSteps: 2 },
|
|
169519
|
+
{ resolutionScale: 0.25, primarySteps: 64, longRayMultiplier: 1, lightSteps: 1 }
|
|
169520
|
+
];
|
|
167146
169521
|
SceneManager.RegisterClass("TOOLKIT.WebVideoPlayer", WebVideoPlayer);
|