@plasius/gpu-lighting 0.1.7 → 0.1.9

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/src/index.js CHANGED
@@ -127,10 +127,151 @@ export const lightingProfiles = Object.freeze(
127
127
  export const lightingProfileNames = Object.freeze(Object.keys(lightingProfiles));
128
128
 
129
129
  export const defaultLightingProfile = "realtime";
130
+ export const lightingDistanceBands = Object.freeze([
131
+ "near",
132
+ "mid",
133
+ "far",
134
+ "horizon",
135
+ ]);
130
136
 
131
137
  export const lightingWorkerQueueClass = "lighting";
132
138
  export const lightingDebugOwner = "lighting";
133
139
 
140
+ const lightingImportanceLevels = Object.freeze([
141
+ "low",
142
+ "medium",
143
+ "high",
144
+ "critical",
145
+ ]);
146
+
147
+ const lightingBandPolicySpecs = Object.freeze({
148
+ near: Object.freeze({
149
+ primaryShadowSource: "ray-traced-primary",
150
+ assistShadowSources: Object.freeze(["visibility-raster", "shadow-map-assist"]),
151
+ temporalReuse: "balanced",
152
+ updateCadenceDivisor: 1,
153
+ liveObjectShadows: true,
154
+ impressionOnly: false,
155
+ }),
156
+ mid: Object.freeze({
157
+ primaryShadowSource: "selective-raster-and-proxy",
158
+ assistShadowSources: Object.freeze([
159
+ "regional-shadow-map",
160
+ "proxy-caster",
161
+ "temporal-history",
162
+ ]),
163
+ temporalReuse: "aggressive",
164
+ updateCadenceDivisor: 2,
165
+ liveObjectShadows: true,
166
+ impressionOnly: false,
167
+ }),
168
+ far: Object.freeze({
169
+ primaryShadowSource: "merged-proxy-casters",
170
+ assistShadowSources: Object.freeze([
171
+ "coarse-directional",
172
+ "semi-static-occlusion",
173
+ ]),
174
+ temporalReuse: "high",
175
+ updateCadenceDivisor: 8,
176
+ liveObjectShadows: false,
177
+ impressionOnly: false,
178
+ }),
179
+ horizon: Object.freeze({
180
+ primaryShadowSource: "baked-impression",
181
+ assistShadowSources: Object.freeze(["atmospheric-gradient", "skyline-darkening"]),
182
+ temporalReuse: "baked",
183
+ updateCadenceDivisor: 60,
184
+ liveObjectShadows: false,
185
+ impressionOnly: true,
186
+ }),
187
+ });
188
+
189
+ function assertLightingImportance(name, value) {
190
+ if (!lightingImportanceLevels.includes(value)) {
191
+ throw new Error(
192
+ `${name} must be one of: ${lightingImportanceLevels.join(", ")}.`
193
+ );
194
+ }
195
+ return value;
196
+ }
197
+
198
+ function resolveBandParticipation(profileName, band, importance) {
199
+ const referenceProfile = profileName === "reference";
200
+ const premiumImportance =
201
+ importance === "critical" || importance === "high";
202
+
203
+ if (band === "near") {
204
+ return Object.freeze({
205
+ directShadows: premiumImportance ? "premium" : "selective",
206
+ reflections: premiumImportance ? "premium" : "selective",
207
+ globalIllumination:
208
+ referenceProfile || importance === "critical" ? "premium" : "selective",
209
+ });
210
+ }
211
+
212
+ if (band === "mid") {
213
+ return Object.freeze({
214
+ directShadows: premiumImportance ? "selective" : "proxy",
215
+ reflections:
216
+ referenceProfile && premiumImportance ? "selective" : "proxy",
217
+ globalIllumination:
218
+ referenceProfile && importance === "critical" ? "selective" : "disabled",
219
+ });
220
+ }
221
+
222
+ if (band === "far") {
223
+ return Object.freeze({
224
+ directShadows: "proxy",
225
+ reflections:
226
+ referenceProfile && importance === "critical" ? "proxy" : "disabled",
227
+ globalIllumination: "disabled",
228
+ });
229
+ }
230
+
231
+ return Object.freeze({
232
+ directShadows: "disabled",
233
+ reflections: "disabled",
234
+ globalIllumination: "disabled",
235
+ });
236
+ }
237
+
238
+ export function createLightingBandPlan(options = {}) {
239
+ const profileName = options.profile ?? defaultLightingProfile;
240
+ const profile = getLightingProfile(profileName);
241
+ const importance = assertLightingImportance(
242
+ "importance",
243
+ options.importance ?? "high"
244
+ );
245
+
246
+ const bands = Object.freeze(
247
+ lightingDistanceBands.map((band) =>
248
+ Object.freeze({
249
+ band,
250
+ profile: profile.name,
251
+ importance,
252
+ primaryShadowSource: lightingBandPolicySpecs[band].primaryShadowSource,
253
+ assistShadowSources: Object.freeze([
254
+ ...lightingBandPolicySpecs[band].assistShadowSources,
255
+ ]),
256
+ rtParticipation: resolveBandParticipation(profile.name, band, importance),
257
+ temporalReuse: lightingBandPolicySpecs[band].temporalReuse,
258
+ updateCadenceDivisor: lightingBandPolicySpecs[band].updateCadenceDivisor,
259
+ liveObjectShadows: lightingBandPolicySpecs[band].liveObjectShadows,
260
+ impressionOnly: lightingBandPolicySpecs[band].impressionOnly,
261
+ })
262
+ )
263
+ );
264
+
265
+ return Object.freeze({
266
+ schemaVersion: 1,
267
+ owner: lightingDebugOwner,
268
+ profile: profile.name,
269
+ importance,
270
+ techniques: Object.freeze([...profile.techniques]),
271
+ bands,
272
+ });
273
+ }
274
+
134
275
  function buildWorkerBudgetLevels(jobType, queueClass, presets) {
135
276
  return Object.freeze([
136
277
  Object.freeze({
@@ -713,6 +854,80 @@ const lightingWorkerDagSpecs = {
713
854
  },
714
855
  };
715
856
 
857
+ function resolveLightingQualityDimensions(techniqueName, jobKey) {
858
+ const key = `${techniqueName}.${jobKey}`;
859
+ return Object.freeze(
860
+ {
861
+ "hybrid.directLighting": { shadows: 1, lightingSamples: 0.7 },
862
+ "hybrid.screenTrace": { rayTracing: 1, temporalReuse: 0.4 },
863
+ "hybrid.radianceCache": {
864
+ lightingSamples: 0.8,
865
+ updateCadence: 0.7,
866
+ temporalReuse: 1,
867
+ },
868
+ "hybrid.finalGather": { lightingSamples: 1, rayTracing: 0.6 },
869
+ "hybrid.reflectionResolve": {
870
+ rayTracing: 0.5,
871
+ temporalReuse: 1,
872
+ shading: 0.3,
873
+ },
874
+ "pathtracer.pathTrace": { rayTracing: 1, lightingSamples: 1 },
875
+ "pathtracer.accumulate": { temporalReuse: 1, updateCadence: 0.4 },
876
+ "pathtracer.denoise": { temporalReuse: 1, shading: 0.4 },
877
+ "volumetrics.froxelIntegrate": {
878
+ lightingSamples: 0.6,
879
+ shading: 0.4,
880
+ updateCadence: 0.3,
881
+ },
882
+ "volumetrics.volumetricShadow": { shadows: 0.8, updateCadence: 0.5 },
883
+ "hdri.irradianceConvolution": {
884
+ lightingSamples: 0.4,
885
+ temporalReuse: 1,
886
+ updateCadence: 1,
887
+ },
888
+ "hdri.specularPrefilter": {
889
+ lightingSamples: 0.5,
890
+ temporalReuse: 1,
891
+ updateCadence: 1,
892
+ },
893
+ "hdri.brdfLut": {
894
+ shading: 0.4,
895
+ temporalReuse: 1,
896
+ updateCadence: 1,
897
+ },
898
+ }[key] ?? {}
899
+ );
900
+ }
901
+
902
+ function resolveLightingImportanceSignals(techniqueName, jobKey) {
903
+ const key = `${techniqueName}.${jobKey}`;
904
+ return Object.freeze(
905
+ {
906
+ "hybrid.directLighting": { visible: true, shadowSignificance: "high" },
907
+ "hybrid.screenTrace": { visible: true, reflectionSignificance: "high" },
908
+ "hybrid.radianceCache": { visible: true },
909
+ "hybrid.finalGather": {
910
+ visible: true,
911
+ shadowSignificance: "critical",
912
+ reflectionSignificance: "high",
913
+ },
914
+ "hybrid.reflectionResolve": { visible: true, reflectionSignificance: "high" },
915
+ "pathtracer.pathTrace": {
916
+ visible: true,
917
+ shadowSignificance: "high",
918
+ reflectionSignificance: "high",
919
+ },
920
+ "pathtracer.accumulate": { visible: true },
921
+ "pathtracer.denoise": { visible: true },
922
+ "volumetrics.froxelIntegrate": { visible: true },
923
+ "volumetrics.volumetricShadow": { visible: true, shadowSignificance: "high" },
924
+ "hdri.irradianceConvolution": { visible: false },
925
+ "hdri.specularPrefilter": { visible: false, reflectionSignificance: "medium" },
926
+ "hdri.brdfLut": { visible: false },
927
+ }[key] ?? {}
928
+ );
929
+ }
930
+
716
931
  function buildWorkerManifestJob(techniqueName, job) {
717
932
  const spec = lightingWorkerSpecPresets[techniqueName].jobs[job.key];
718
933
  const dag = lightingWorkerDagSpecs[techniqueName][job.key];
@@ -737,6 +952,8 @@ function buildWorkerManifestJob(techniqueName, job) {
737
952
  domain: spec.domain,
738
953
  authority: "visual",
739
954
  importance: spec.importance,
955
+ qualityDimensions: resolveLightingQualityDimensions(techniqueName, job.key),
956
+ importanceSignals: resolveLightingImportanceSignals(techniqueName, job.key),
740
957
  levels: spec.levels,
741
958
  }),
742
959
  debug: Object.freeze({
@@ -830,6 +1047,7 @@ export function getLightingProfileWorkerManifest(
830
1047
  const techniques = profile.techniques.map((techniqueName) =>
831
1048
  getLightingTechniqueWorkerManifest(techniqueName)
832
1049
  );
1050
+ const lightingBandPlan = createLightingBandPlan({ profile: profile.name });
833
1051
 
834
1052
  return Object.freeze({
835
1053
  schemaVersion: 1,
@@ -838,6 +1056,7 @@ export function getLightingProfileWorkerManifest(
838
1056
  description: profile.description,
839
1057
  schedulerMode: "dag",
840
1058
  techniques: Object.freeze(techniques),
1059
+ lightingBands: lightingBandPlan.bands,
841
1060
  jobs: Object.freeze(techniques.flatMap((technique) => technique.jobs)),
842
1061
  });
843
1062
  }
@@ -1011,6 +1230,7 @@ export async function loadLightingProfileWorkerPlan(
1011
1230
  return {
1012
1231
  profile,
1013
1232
  techniques,
1233
+ lightingBandPlan: createLightingBandPlan({ profile: profile.name }),
1014
1234
  workerManifest: getLightingProfileWorkerManifest(profile.name),
1015
1235
  };
1016
1236
  }