@plasius/gpu-renderer 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ var DEFAULT_HEIGHT = 720;
4
4
  var DEFAULT_MAX_DEPTH = 6;
5
5
  var DEFAULT_TILE_SIZE = 128;
6
6
  var DEFAULT_SAMPLES_PER_PIXEL = 1;
7
+ var DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION = 256;
7
8
  var DEFAULT_SCENE_OBJECT_CAPACITY = 128;
8
9
  var DEFAULT_ENVIRONMENT_PORTAL_CAPACITY = 32;
9
10
  var WORKGROUP_SIZE = 64;
@@ -1034,6 +1035,15 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
1034
1035
  1,
1035
1036
  64
1036
1037
  );
1038
+ const maxFramePassesPerSubmission = clamp(
1039
+ readPositiveInteger(
1040
+ "maxFramePassesPerSubmission",
1041
+ options.maxFramePassesPerSubmission,
1042
+ DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION
1043
+ ),
1044
+ 1,
1045
+ 4096
1046
+ );
1037
1047
  const tilePixelCapacity = readPositiveInteger(
1038
1048
  "tilePixelCapacity",
1039
1049
  options.tilePixelCapacity,
@@ -1101,6 +1111,7 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
1101
1111
  maxDepth,
1102
1112
  tileSize,
1103
1113
  samplesPerPixel,
1114
+ maxFramePassesPerSubmission,
1104
1115
  tilePixelCapacity,
1105
1116
  sceneObjects,
1106
1117
  sceneObjectCount: sceneObjects.length,
@@ -1967,6 +1978,80 @@ fn terminal_surface_environment_contribution(ray: RayRecord, hit: HitRecord) ->
1967
1978
  return clamp_sample_radiance(ray.throughput.xyz * surfaceColor * environmentFloor * materialFloor);
1968
1979
  }
1969
1980
 
1981
+ fn direct_environment_portal_irradiance(origin: vec3<f32>, normal: vec3<f32>) -> vec3<f32> {
1982
+ if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
1983
+ return vec3<f32>(0.0);
1984
+ }
1985
+
1986
+ var irradiance = vec3<f32>(0.0);
1987
+ for (var portalIndex = 0u; portalIndex < config.environmentPortalCount; portalIndex = portalIndex + 1u) {
1988
+ let portal = environmentPortals[portalIndex];
1989
+ if (portal.kind != 1u) {
1990
+ continue;
1991
+ }
1992
+
1993
+ let toPortal = portal.position.xyz - origin;
1994
+ let distanceSquared = max(dot(toPortal, toPortal), 0.01);
1995
+ let direction = safe_normalize(toPortal, normal);
1996
+ let surfaceFacing = saturate(dot(normal, direction));
1997
+ if (surfaceFacing <= 0.0001) {
1998
+ continue;
1999
+ }
2000
+
2001
+ let portalNormal = safe_normalize(portal.normal.xyz, vec3<f32>(0.0, 0.0, 1.0));
2002
+ let twoSided = (portal.flags & 1u) != 0u;
2003
+ let portalFacing = select(
2004
+ saturate(dot(-direction, portalNormal)),
2005
+ max(abs(dot(direction, portalNormal)), 0.15),
2006
+ twoSided
2007
+ );
2008
+ let area = max(portal.position.w, 0.0001);
2009
+ let distanceFalloff = clamp(area / max(distanceSquared, area * 0.25), 0.0, 2.5);
2010
+ irradiance = irradiance +
2011
+ portal.color.rgb *
2012
+ portal.normal.w *
2013
+ portal.color.a *
2014
+ surfaceFacing *
2015
+ portalFacing *
2016
+ distanceFalloff;
2017
+ }
2018
+ return irradiance;
2019
+ }
2020
+
2021
+ fn surface_direct_environment_contribution(ray: RayRecord, hit: HitRecord) -> vec3<f32> {
2022
+ let normal = safe_normalize(hit.shadingNormal.xyz, vec3<f32>(0.0, 1.0, 0.0));
2023
+ let origin = hit.position.xyz + normal * 0.003;
2024
+ let viewDirection = safe_normalize(-ray.direction.xyz, normal);
2025
+ let surfaceColor = clamp(max(hit.color.xyz, config.ambientColor.xyz * 0.35), vec3<f32>(0.0), vec3<f32>(1.0));
2026
+ let roughness = clamp(hit.material.x, 0.0, 1.0);
2027
+ let metallic = clamp(hit.material.y, 0.0, 1.0);
2028
+
2029
+ let normalEnvironment = gated_environment_radiance(origin, normal);
2030
+ let skyVisibility = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.45;
2031
+ let skyIrradiance = max(config.ambientColor.xyz * 0.72, normalEnvironment * skyVisibility * 0.16);
2032
+
2033
+ let sunDirection = safe_normalize(
2034
+ config.environmentSunDirectionIntensity.xyz,
2035
+ vec3<f32>(0.0, 1.0, 0.0)
2036
+ );
2037
+ let sunFacing = saturate(dot(normal, sunDirection));
2038
+ let sunRadiance = gated_environment_radiance(origin, sunDirection);
2039
+ let sunIrradiance = sunRadiance * sunFacing * 0.2;
2040
+ let portalIrradiance = direct_environment_portal_irradiance(origin, normal);
2041
+
2042
+ let diffuseWeight = select(1.0 - metallic * 0.65, 0.22, hit.materialKind == 1u);
2043
+ let diffuse = surfaceColor * (skyIrradiance + sunIrradiance + portalIrradiance) * diffuseWeight;
2044
+
2045
+ let halfVector = safe_normalize(sunDirection + viewDirection, normal);
2046
+ let specularPower = 8.0 + (1.0 - roughness) * 96.0;
2047
+ let specularFacing = pow(saturate(dot(normal, halfVector)), specularPower) * sunFacing;
2048
+ let specularTint = mix(vec3<f32>(0.04), surfaceColor, metallic);
2049
+ let specular = specularTint * sunRadiance * specularFacing * select(0.16, 0.48, hit.materialKind == 1u || hit.materialKind == 2u);
2050
+
2051
+ let bounceWeight = select(1.0, 0.38, ray.bounce > 0u);
2052
+ return clamp_sample_radiance(ray.throughput.xyz * (diffuse + specular) * bounceWeight);
2053
+ }
2054
+
1970
2055
  fn default_mesh_range() -> MeshRange {
1971
2056
  return MeshRange(
1972
2057
  0u,
@@ -2836,6 +2921,14 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
2836
2921
  return;
2837
2922
  }
2838
2923
 
2924
+ let shouldEstimateDirectEnvironment =
2925
+ (hit.materialKind == 0u || hit.materialKind == 1u) && hit.material.z >= 0.95;
2926
+ if (shouldEstimateDirectEnvironment) {
2927
+ let directEnvironment = surface_direct_environment_contribution(ray, hit);
2928
+ accumulation[ray.rayId] =
2929
+ accumulation[ray.rayId] + vec4<f32>(directEnvironment * sample_weight(), 0.0);
2930
+ }
2931
+
2839
2932
  if (ray.bounce + 1u >= config.maxDepth) {
2840
2933
  let terminalEnvironment = terminal_surface_environment_contribution(ray, hit);
2841
2934
  accumulation[ray.rayId] =
@@ -3511,6 +3604,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
3511
3604
  let frame = 0;
3512
3605
  let accelerationBuilt = !config.gpuAccelerationBuildRequired;
3513
3606
  let accelerationBuildCount = 0;
3607
+ let activeCameraOptions = options.camera ?? DEFAULT_CAMERA;
3514
3608
  function createFrameConfigWriter(frameIndex) {
3515
3609
  let slot = 0;
3516
3610
  return (tile, buildRange = {}) => {
@@ -3670,33 +3764,53 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
3670
3764
  }
3671
3765
  function dispatchFrame(frameIndex) {
3672
3766
  const writeFrameConfig = createFrameConfigWriter(frameIndex);
3673
- const encoder = device.createCommandEncoder({
3674
- label: `plasius.wavefront.frame.${frameIndex}.batched`
3767
+ let submissionCount = 0;
3768
+ let encodedFramePasses = 0;
3769
+ let encoder = device.createCommandEncoder({
3770
+ label: `plasius.wavefront.frame.${frameIndex}.batched.${submissionCount + 1}`
3675
3771
  });
3772
+ function submitCurrentEncoder() {
3773
+ if (encodedFramePasses <= 0) {
3774
+ return;
3775
+ }
3776
+ device.queue.submit([encoder.finish()]);
3777
+ submissionCount += 1;
3778
+ encodedFramePasses = 0;
3779
+ encoder = device.createCommandEncoder({
3780
+ label: `plasius.wavefront.frame.${frameIndex}.batched.${submissionCount + 1}`
3781
+ });
3782
+ }
3783
+ function reserveEncoder(passCount = 1) {
3784
+ if (encodedFramePasses > 0 && encodedFramePasses + passCount > config.maxFramePassesPerSubmission) {
3785
+ submitCurrentEncoder();
3786
+ }
3787
+ encodedFramePasses += passCount;
3788
+ return encoder;
3789
+ }
3676
3790
  for (const tile of tiles) {
3677
3791
  for (let sampleIndex = 0; sampleIndex < config.samplesPerPixel; sampleIndex += 1) {
3678
3792
  const configOffset = writeFrameConfig(tile, {
3679
3793
  sampleIndex,
3680
3794
  sampleWeight: 1 / config.samplesPerPixel
3681
3795
  });
3682
- encodeTileSample(encoder, tile, configOffset);
3796
+ encodeTileSample(reserveEncoder(), tile, configOffset);
3683
3797
  }
3684
3798
  const outputConfigOffset = writeFrameConfig(tile, {
3685
3799
  sampleIndex: 0,
3686
3800
  sampleWeight: 1 / config.samplesPerPixel
3687
3801
  });
3688
- encodeTileOutput(encoder, tile, outputConfigOffset);
3802
+ encodeTileOutput(reserveEncoder(), tile, outputConfigOffset);
3689
3803
  }
3690
3804
  if (config.denoise) {
3691
3805
  const denoiseConfigOffset = writeFrameConfig(
3692
3806
  { x: 0, y: 0, width: config.width, height: config.height },
3693
3807
  { sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
3694
3808
  );
3695
- encodeDenoise(encoder, denoiseConfigOffset);
3809
+ encodeDenoise(reserveEncoder(), denoiseConfigOffset);
3696
3810
  }
3697
- encodePresent(encoder);
3698
- device.queue.submit([encoder.finish()]);
3699
- return 1;
3811
+ encodePresent(reserveEncoder());
3812
+ submitCurrentEncoder();
3813
+ return submissionCount;
3700
3814
  }
3701
3815
  function renderOnce() {
3702
3816
  frame += 1;
@@ -3711,6 +3825,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
3711
3825
  tiles: tiles.length,
3712
3826
  tileSize: config.tileSize,
3713
3827
  samplesPerPixel: config.samplesPerPixel,
3828
+ maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
3714
3829
  screenRays: config.width * config.height,
3715
3830
  primaryRays: config.width * config.height * config.samplesPerPixel,
3716
3831
  sceneObjectCount: config.sceneObjectCount,
@@ -3797,11 +3912,29 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
3797
3912
  samplesPerPixel: config.samplesPerPixel,
3798
3913
  sceneObjectCapacity: config.sceneObjectCapacity,
3799
3914
  sceneObjects: packedScene.objects,
3915
+ camera: activeCameraOptions,
3800
3916
  frameIndex: config.frameIndex
3801
3917
  });
3802
3918
  device.queue.writeBuffer(sceneObjectBuffer, 0, packedScene.buffer);
3803
3919
  return config;
3804
3920
  }
3921
+ function updateCamera(cameraOptions = {}) {
3922
+ activeCameraOptions = cameraOptions;
3923
+ config = createWavefrontPathTracingComputeConfig({
3924
+ ...options,
3925
+ canvas,
3926
+ width: config.width,
3927
+ height: config.height,
3928
+ maxDepth: config.maxDepth,
3929
+ tileSize: config.tileSize,
3930
+ samplesPerPixel: config.samplesPerPixel,
3931
+ sceneObjectCapacity: config.sceneObjectCapacity,
3932
+ sceneObjects: packedScene.objects,
3933
+ camera: activeCameraOptions,
3934
+ frameIndex: config.frameIndex
3935
+ });
3936
+ return config;
3937
+ }
3805
3938
  function getSnapshot() {
3806
3939
  return Object.freeze({
3807
3940
  frame,
@@ -3811,6 +3944,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
3811
3944
  tiles: tiles.length,
3812
3945
  tileSize: config.tileSize,
3813
3946
  samplesPerPixel: config.samplesPerPixel,
3947
+ maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
3814
3948
  sceneObjectCount: config.sceneObjectCount,
3815
3949
  triangleCount: config.triangleCount,
3816
3950
  emissiveTriangleCount: config.emissiveTriangleCount,
@@ -3858,6 +3992,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
3858
3992
  renderFrame,
3859
3993
  readOutputProbe,
3860
3994
  updateSceneObjects,
3995
+ updateCamera,
3861
3996
  getSnapshot,
3862
3997
  destroy
3863
3998
  });