@plasius/gpu-renderer 0.2.49 → 0.2.51

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasius/gpu-renderer",
3
- "version": "0.2.49",
3
+ "version": "0.2.51",
4
4
  "description": "Framework-agnostic WebGPU renderer runtime for Plasius projects.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -18,6 +18,7 @@ import {
18
18
  import {
19
19
  estimateWavefrontPathTracingMemoryForConfig,
20
20
  createWavefrontPathTracingComputeConfig,
21
+ updateWavefrontCameraConfig,
21
22
  packEnvironmentPortals,
22
23
  supportsWavefrontPathTracingCompute,
23
24
  } from "./wavefront-config.js";
@@ -1040,7 +1041,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
1040
1041
  function updateCamera(cameraOptions = {}) {
1041
1042
  progressive?.reset();
1042
1043
  activeCameraOptions = cameraOptions;
1043
- config = rebuildLiveConfig();
1044
+ config = updateWavefrontCameraConfig(config, cameraOptions);
1044
1045
  return config;
1045
1046
  }
1046
1047
 
@@ -265,6 +265,14 @@ function resolveCamera(input, width, height) {
265
265
  });
266
266
  }
267
267
 
268
+ /** Camera changes do not invalidate immutable mesh, material or lighting resources. */
269
+ export function updateWavefrontCameraConfig(config, camera) {
270
+ return Object.freeze({
271
+ ...config,
272
+ camera: resolveCamera(camera, config.width, config.height),
273
+ });
274
+ }
275
+
268
276
  export function estimateWavefrontPathTracingMemory(options = {}) {
269
277
  const width = readPositiveInteger("width", options.width, DEFAULT_WIDTH);
270
278
  const height = readPositiveInteger("height", options.height, DEFAULT_HEIGHT);
@@ -451,7 +451,7 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord) -> ScatterResult {
451
451
  if (cannotRefract || transmissionSelector < transmissionReflectChance) {
452
452
  return ScatterResult(
453
453
  vec4<f32>(reflect(ray.direction.xyz, normal), 0.0),
454
- 1.0,
454
+ select(transmissionReflectChance, 1.0, cannotRefract),
455
455
  ray.mediumRefId,
456
456
  RAY_FLAG_DELTA_SAMPLE,
457
457
  SCATTER_LOBE_DELTA_REFLECTION,
@@ -459,7 +459,7 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord) -> ScatterResult {
459
459
  }
460
460
  return ScatterResult(
461
461
  vec4<f32>(refract_direction(ray.direction.xyz, normal, etaRatio), 0.0),
462
- 1.0,
462
+ 1.0 - transmissionReflectChance,
463
463
  transmitted_medium_ref_id(ray, hit),
464
464
  RAY_FLAG_DELTA_SAMPLE,
465
465
  SCATTER_LOBE_DELTA_TRANSMISSION,
@@ -851,7 +851,6 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
851
851
  }
852
852
  record_deferred_path_throughput(ray, continuationThroughput);
853
853
  let throughput = ray.throughput.xyz * continuationThroughput;
854
- let transmissionBranch = scatter.lobeKind == SCATTER_LOBE_DELTA_TRANSMISSION;
855
854
  let nextMediumStack = select(
856
855
  transitioned_medium_stack(ray, hit),
857
856
  ray.mediumStack,
@@ -890,66 +889,9 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
890
889
  nextMediumStack
891
890
  );
892
891
 
893
- // Keep both sides of a dielectric event when queue capacity permits. The
894
- // selected continuation remains the primary sample; this bounded second
895
- // record preserves a reflected/transmitted branch for explicit transport.
896
- if (
897
- scatter.lobeKind == SCATTER_LOBE_DELTA_REFLECTION ||
898
- scatter.lobeKind == SCATTER_LOBE_DELTA_TRANSMISSION
899
- ) {
900
- let secondaryIndex = atomicAdd(&counters.nextCount, 1u);
901
- if (secondaryIndex < config.tilePixelCount) {
902
- let secondaryIsReflection = transmissionBranch;
903
- let secondaryDirection = select(
904
- refract_direction(ray.direction.xyz, surface_shading_normal(hit),
905
- select(1.0 / max(hit.material.w, 1.01), max(hit.material.w, 1.01), hit.frontFace == 1u)),
906
- reflect(ray.direction.xyz, surface_shading_normal(hit)),
907
- secondaryIsReflection
908
- );
909
- let secondaryThroughput = select(
910
- surface_delta_transmission_throughput(hit, continuationViewDirection),
911
- surface_delta_reflection_throughput(hit, continuationViewDirection),
912
- secondaryIsReflection
913
- ) * segmentTransmittance;
914
- let secondaryStack = select(
915
- transitioned_medium_stack(ray, hit),
916
- ray.mediumStack,
917
- secondaryIsReflection
918
- );
919
- let secondaryDepth = select(
920
- transitioned_medium_stack_depth(ray, hit),
921
- ray.mediumStackDepth,
922
- secondaryIsReflection
923
- );
924
- let secondaryMedium = select(
925
- transmitted_medium_ref_id(ray, hit),
926
- ray.mediumRefId,
927
- secondaryIsReflection
928
- );
929
- nextQueue[secondaryIndex] = RayRecord(
930
- ray.rayId,
931
- ray.rayId,
932
- ray.sourcePixelId,
933
- ray.sampleId,
934
- ray.bounce + 1u,
935
- secondaryMedium,
936
- scatter.flags,
937
- secondaryDepth,
938
- vec4<f32>(
939
- offset_origin(
940
- hit.position.xyz,
941
- hit.geometricNormal.xyz,
942
- hit.shadingNormal.xyz,
943
- secondaryDirection
944
- ),
945
- 1.0
946
- ),
947
- vec4<f32>(secondaryDirection, 0.0),
948
- vec4<f32>(ray.throughput.xyz * secondaryThroughput, scatter.pdf),
949
- secondaryStack
950
- );
951
- }
952
- }
892
+ // One continuation per incoming path keeps the tile queue bounded and gives
893
+ // each accumulation/deferred record a single writer. The selected dielectric
894
+ // lobe is probability-weighted; a sibling must never be silently dropped.
953
895
  }
954
896
 
955
897
  @compute @workgroup_size(1)
@@ -633,10 +633,20 @@ fn surface_continuation_throughput(
633
633
  scatter: ScatterResult
634
634
  ) -> vec3<f32> {
635
635
  if ((scatter.flags & RAY_FLAG_DELTA_SAMPLE) != 0u) {
636
+ let refractive = hit.materialKind == 2u || hit.materialKind == 3u || hit.materialExtension.z > 0.001;
637
+ if (refractive) {
638
+ let normal = surface_shading_normal(hit);
639
+ let eta = select(max(hit.material.w, 1.01), 1.0 / max(hit.material.w, 1.01), hit.frontFace == 1u);
640
+ let cosine = min(dot(viewDirection, normal), 1.0);
641
+ let cannotRefract = eta * sqrt(max(0.0, 1.0 - cosine * cosine)) > 1.0;
642
+ if (cannotRefract) {
643
+ return vec3<f32>(1.0);
644
+ }
645
+ }
636
646
  if (scatter.lobeKind == SCATTER_LOBE_DELTA_TRANSMISSION) {
637
- return surface_delta_transmission_throughput(hit, viewDirection);
647
+ return surface_delta_transmission_throughput(hit, viewDirection) / scatter.pdf;
638
648
  }
639
- return surface_delta_reflection_throughput(hit, viewDirection);
649
+ return surface_delta_reflection_throughput(hit, viewDirection) / scatter.pdf;
640
650
  }
641
651
  if (scatter.pdf <= 0.000001) {
642
652
  return vec3<f32>(0.0);