@rings-webgpu/core 1.0.13 → 1.0.14

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.
@@ -22679,6 +22679,13 @@ const GSplat_VS = (
22679
22679
  `
22680
22680
  #include "GlobalUniform"
22681
22681
 
22682
+ // Constants
22683
+ const ALPHA_THRESHOLD: f32 = 0.00392156863; // 1.0 / 255.0
22684
+ const COV_COMPENSATION: f32 = 0.3;
22685
+ const MAX_SPLAT_SIZE: f32 = 1024.0;
22686
+ const MIN_LAMBDA: f32 = 0.1;
22687
+ const LOG_255: f32 = 5.541263545; // log(255.0) - natural log for WGSL
22688
+
22682
22689
  struct MaterialUniform {
22683
22690
  modelMatrix: mat4x4<f32>,
22684
22691
  tex_params: vec4<f32>, // [numSplats, texWidth, validCount, visBoost]
@@ -22698,107 +22705,92 @@ const GSplat_VS = (
22698
22705
  @group(1) @binding(3) var transformB: texture_2d<f32>;
22699
22706
  @group(1) @binding(4) var splatOrder: texture_2d<u32>;
22700
22707
 
22701
- // Global variables for texture lookups
22702
- var<private> splatUV: vec2<i32>;
22703
- var<private> splatId: u32;
22704
- var<private> tA: vec4<u32>;
22708
+ struct SplatData {
22709
+ center: vec3f,
22710
+ covA: vec3f,
22711
+ covB: vec3f,
22712
+ };
22705
22713
 
22706
- // === calcSplatUV() - returns bool ===
22707
- fn calcSplatUV(orderId: u32) -> bool {
22708
- let textureWidth = u32(materialUniform.tex_params.y);
22709
- let numSplats = u32(materialUniform.tex_params.x);
22710
-
22711
- if (orderId >= numSplats) {
22712
- return false;
22713
- }
22714
-
22714
+ // Helper function to discard splat
22715
+ fn discardSplat() -> VSOut {
22716
+ var o: VSOut;
22717
+ o.member = vec4f(0.0, 0.0, 2.0, 1.0);
22718
+ o.vColor = vec4f(0.0);
22719
+ o.vTexCoord = vec2f(0.0);
22720
+ return o;
22721
+ }
22722
+
22723
+ // === calcSplatUV() - returns optional UV ===
22724
+ fn calcSplatUV(orderId: u32, textureWidth: u32, numSplats: u32) -> vec2<i32> {
22715
22725
  let orderUV = vec2<i32>(
22716
22726
  i32(orderId % textureWidth),
22717
22727
  i32(orderId / textureWidth)
22718
22728
  );
22719
22729
 
22720
- // calculate splatUV
22721
- splatId = textureLoad(splatOrder, orderUV, 0).r;
22722
- splatUV = vec2<i32>(
22730
+ let splatId = textureLoad(splatOrder, orderUV, 0).r;
22731
+ return vec2<i32>(
22723
22732
  i32(splatId % textureWidth),
22724
22733
  i32(splatId / textureWidth)
22725
22734
  );
22726
-
22727
- return true;
22728
- }
22729
-
22730
- // === getCenter() - returns vec3 ===
22731
- fn getCenter() -> vec3f {
22732
- tA = textureLoad(transformA, splatUV, 0);
22733
- return vec3f(bitcast<f32>(tA.x), bitcast<f32>(tA.y), bitcast<f32>(tA.z));
22734
22735
  }
22735
22736
 
22736
- // Struct to return covA and covB
22737
- struct CovarianceData {
22738
- covA: vec3f,
22739
- covB: vec3f,
22740
- };
22741
-
22742
- // === getCovariance() - returns struct ===
22743
- fn getCovariance() -> CovarianceData {
22744
- let tB = textureLoad(transformB, splatUV, 0);
22737
+ // === getSplatData() - unified texture loading ===
22738
+ fn getSplatData(splatUV: vec2<i32>) -> SplatData {
22739
+ var data: SplatData;
22745
22740
 
22746
- // Use WGSL built-in unpack2x16float (equivalent to GLSL unpackHalf2x16)
22741
+ // Load both textures once
22742
+ let tA = textureLoad(transformA, splatUV, 0);
22743
+ let tB = textureLoad(transformB, splatUV, 0);
22747
22744
  let tC = unpack2x16float(tA.w);
22748
22745
 
22749
- var result: CovarianceData;
22750
- result.covA = tB.xyz;
22751
- result.covB = vec3f(tC.x, tC.y, tB.w);
22746
+ // Extract center
22747
+ data.center = vec3f(bitcast<f32>(tA.x), bitcast<f32>(tA.y), bitcast<f32>(tA.z));
22752
22748
 
22753
- return result;
22754
- }
22755
-
22756
- // === getRotationMatrix() - returns mat3x3 ===
22757
- fn getRotationMatrix() -> mat3x3f {
22758
- let cov_data = getCovariance();
22759
- let covA = cov_data.covA;
22760
- let covB = cov_data.covB;
22749
+ // Extract covariance
22750
+ data.covA = tB.xyz;
22751
+ data.covB = vec3f(tC.x, tC.y, tB.w);
22761
22752
 
22762
- return mat3x3f(
22763
- vec3f(1.0 - 2.0 * (covA.z * covA.z + covB.x * covB.x), 2.0 * (covA.y * covA.z + covB.y * covB.x), 2.0 * (covA.y * covB.x - covB.y * covA.z)),
22764
- vec3f(2.0 * (covA.y * covA.z - covB.y * covB.x), 1.0 - 2.0 * (covA.y * covA.y + covB.x * covB.x), 2.0 * (covA.z * covB.x + covA.y * covB.y)),
22765
- vec3f(2.0 * (covA.y * covB.x + covB.y * covA.z), 2.0 * (covA.z * covB.x - covA.y * covB.y), 1.0 - 2.0 * (covA.y * covA.y + covA.z * covA.z))
22766
- );
22753
+ return data;
22767
22754
  }
22768
22755
 
22769
22756
  // === calcV1V2() - returns vec4 ===
22770
22757
  fn calcV1V2(splat_cam: vec3f, covA: vec3f, covB: vec3f, W: mat3x3f, viewport: vec2f, projMat: mat4x4f) -> vec4f {
22758
+ // Construct symmetric covariance matrix
22771
22759
  let Vrk = mat3x3f(
22772
22760
  vec3f(covA.x, covA.y, covA.z),
22773
22761
  vec3f(covA.y, covB.x, covB.y),
22774
22762
  vec3f(covA.z, covB.y, covB.z)
22775
22763
  );
22776
22764
 
22765
+ // Calculate Jacobian
22777
22766
  let focal = viewport.x * projMat[0][0];
22778
-
22779
- let J1 = focal / splat_cam.z;
22780
- let J2 = -J1 / splat_cam.z * splat_cam.xy;
22767
+ let inv_z = 1.0 / splat_cam.z;
22768
+ let J1 = focal * inv_z;
22769
+ let J2 = -J1 * inv_z * splat_cam.xy;
22781
22770
  let J = mat3x3f(
22782
22771
  vec3f(J1, 0.0, J2.x),
22783
22772
  vec3f(0.0, J1, J2.y),
22784
22773
  vec3f(0.0, 0.0, 0.0)
22785
22774
  );
22786
22775
 
22776
+ // Project covariance to screen space
22787
22777
  let T = W * J;
22788
22778
  let cov = transpose(T) * Vrk * T;
22789
22779
 
22790
- let diagonal1 = cov[0][0] + 0.3;
22780
+ // Eigenvalue decomposition with compensation
22781
+ let diagonal1 = cov[0][0] + COV_COMPENSATION;
22791
22782
  let offDiagonal = cov[0][1];
22792
- let diagonal2 = cov[1][1] + 0.3;
22783
+ let diagonal2 = cov[1][1] + COV_COMPENSATION;
22793
22784
 
22794
22785
  let mid = 0.5 * (diagonal1 + diagonal2);
22795
- let radius = length(vec2f((diagonal1 - diagonal2) / 2.0, offDiagonal));
22786
+ let radius = length(vec2f((diagonal1 - diagonal2) * 0.5, offDiagonal));
22796
22787
  let lambda1 = mid + radius;
22797
- let lambda2 = max(mid - radius, 0.1);
22788
+ let lambda2 = max(mid - radius, MIN_LAMBDA);
22798
22789
  let diagonalVector = normalize(vec2f(offDiagonal, lambda1 - diagonal1));
22799
22790
 
22800
- let v1 = min(sqrt(2.0 * lambda1), 1024.0) * diagonalVector;
22801
- let v2 = min(sqrt(2.0 * lambda2), 1024.0) * vec2f(diagonalVector.y, -diagonalVector.x);
22791
+ // Calculate axis vectors with size clamping
22792
+ let v1 = min(sqrt(2.0 * lambda1), MAX_SPLAT_SIZE) * diagonalVector;
22793
+ let v2 = min(sqrt(2.0 * lambda2), MAX_SPLAT_SIZE) * vec2f(diagonalVector.y, -diagonalVector.x);
22802
22794
 
22803
22795
  // WebGPU Y-axis flip: WebGPU NDC Y goes from top(-1) to bottom(1), opposite of WebGL
22804
22796
  return vec4f(v1.x, -v1.y, v2.x, -v2.y);
@@ -22812,115 +22804,97 @@ const GSplat_VS = (
22812
22804
  @builtin(instance_index) iid : u32,
22813
22805
  @location(0) position: vec3<f32> // vertex_position from mesh (x, y, local_index)
22814
22806
  ) -> VSOut {
22815
- var o: VSOut;
22816
- let discardVec = vec4f(0.0, 0.0, 2.0, 1.0);
22817
-
22818
22807
  // Calculate splat ID
22819
- // orderId = vertex_id_attrib + uint(vertex_position.z)
22820
- // In our case: vertex_id_attrib = iid * batchSize
22821
22808
  let batchSize = u32(materialUniform.pixelCull.w);
22822
- let base_splat_index = iid * batchSize;
22823
- let local_splat_index = u32(position.z);
22824
- let orderId = base_splat_index + local_splat_index;
22809
+ let orderId = iid * batchSize + u32(position.z);
22825
22810
 
22826
- // Use vertex position from mesh
22827
- let vertex_pos = position.xy;
22828
-
22829
- // calculate splat uv
22830
- if (!calcSplatUV(orderId)) {
22831
- o.member = discardVec;
22832
- o.vColor = vec4f(0.0);
22833
- o.vTexCoord = vec2f(0.0);
22834
- return o;
22811
+ // Early bounds check
22812
+ let textureWidth = u32(materialUniform.tex_params.y);
22813
+ let numSplats = u32(materialUniform.tex_params.x);
22814
+ if (orderId >= numSplats) {
22815
+ return discardSplat();
22835
22816
  }
22836
22817
 
22837
- // get center
22838
- let center = getCenter();
22818
+ // Calculate splat UV and load all data in one go
22819
+ let splatUV = calcSplatUV(orderId, textureWidth, numSplats);
22820
+ let splatData = getSplatData(splatUV);
22839
22821
 
22840
- // handle transforms
22822
+ // Load color early for alpha test
22823
+ let color = textureLoad(splatColor, splatUV, 0);
22824
+ if (color.a < ALPHA_THRESHOLD) {
22825
+ return discardSplat();
22826
+ }
22827
+
22828
+ // Transform matrices
22829
+ let matrix_model = materialUniform.modelMatrix;
22841
22830
  let matrix_view = globalUniform.viewMat;
22842
22831
  let matrix_projection = globalUniform.projMat;
22843
- let matrix_model = materialUniform.modelMatrix;
22844
-
22845
22832
  let model_view = matrix_view * matrix_model;
22846
- let splat_cam = model_view * vec4f(center, 1.0);
22847
- let splat_proj = matrix_projection * splat_cam;
22848
22833
 
22849
- // Frustum culling: cull splats behind camera
22850
- if (splat_proj.z < 0.0) {
22851
- o.member = discardVec;
22852
- o.vColor = vec4f(0.0);
22853
- o.vTexCoord = vec2f(0.0);
22854
- return o;
22834
+ // Transform center to camera and clip space
22835
+ let splat_cam = model_view * vec4f(splatData.center, 1.0);
22836
+
22837
+ // Early depth culling
22838
+ if (splat_cam.z <= 0.0) {
22839
+ return discardSplat();
22855
22840
  }
22856
22841
 
22857
- // Frustum culling: cull splats outside screen bounds
22858
- let ndc = splat_proj.xyz / splat_proj.w;
22859
- let margin = 0.0;
22860
- if (ndc.x < -1.0 - margin || ndc.x > 1.0 + margin ||
22861
- ndc.y < -1.0 - margin || ndc.y > 1.0 + margin ||
22842
+ let splat_proj = matrix_projection * splat_cam;
22843
+
22844
+ // Frustum culling with NDC check
22845
+ let inv_w = 1.0 / splat_proj.w;
22846
+ let ndc = splat_proj.xyz * inv_w;
22847
+ if (ndc.x < -1.0 || ndc.x > 1.0 ||
22848
+ ndc.y < -1.0 || ndc.y > 1.0 ||
22862
22849
  ndc.z < 0.0 || ndc.z > 1.0) {
22863
- o.member = discardVec;
22864
- o.vColor = vec4f(0.0);
22865
- o.vTexCoord = vec2f(0.0);
22866
- return o;
22850
+ return discardSplat();
22867
22851
  }
22868
22852
 
22869
- // get covariance
22870
- let cov_data = getCovariance();
22871
-
22853
+ // Calculate v1v2 (screen-space ellipse axes)
22872
22854
  let viewport = vec2f(globalUniform.windowWidth, globalUniform.windowHeight);
22873
- let v1v2 = calcV1V2(splat_cam.xyz, cov_data.covA, cov_data.covB, transpose(mat3x3f(model_view[0].xyz, model_view[1].xyz, model_view[2].xyz)), viewport, matrix_projection);
22855
+ let W = transpose(mat3x3f(model_view[0].xyz, model_view[1].xyz, model_view[2].xyz));
22856
+ let v1v2 = calcV1V2(splat_cam.xyz, splatData.covA, splatData.covB, W, viewport, matrix_projection);
22874
22857
 
22875
- // get color
22876
- let color = textureLoad(splatColor, splatUV, 0);
22877
- if (color.a < 1.0 / 255.0) {
22878
- o.member = discardVec;
22879
- o.vColor = vec4f(0.0);
22880
- o.vTexCoord = vec2f(0.0);
22881
- return o;
22882
- }
22858
+ // Calculate scale based on alpha (optimized formula)
22859
+ let scale = min(1.0, sqrt(LOG_255 + log(color.a)) * 0.5);
22883
22860
 
22884
- // calculate scale based on alpha
22885
- let scale = min(1.0, sqrt(-log(1.0 / 255.0 / color.a)) / 2.0);
22886
-
22887
- // apply visBoost (size multiplier)
22861
+ // Apply visBoost (size multiplier)
22888
22862
  let visBoost = materialUniform.tex_params.w;
22889
- var v1v2_scaled = v1v2 * scale * visBoost;
22863
+ let v1v2_scaled = v1v2 * (scale * visBoost);
22890
22864
 
22891
- // Pixel coverage culling
22892
- let v1_len_sq = dot(v1v2_scaled.xy, v1v2_scaled.xy);
22893
- let v2_len_sq = dot(v1v2_scaled.zw, v1v2_scaled.zw);
22865
+ // Pixel coverage culling (vectorized squared length calculation)
22866
+ let v1v2_sq = v1v2_scaled * v1v2_scaled;
22867
+ let v1_len_sq = v1v2_sq.x + v1v2_sq.y;
22868
+ let v2_len_sq = v1v2_sq.z + v1v2_sq.w;
22894
22869
 
22895
22870
  let minPixels = materialUniform.pixelCull.x;
22896
22871
  let maxPixels = materialUniform.pixelCull.y;
22897
- let maxPixelCullDistance = materialUniform.pixelCull.z;
22898
22872
 
22899
22873
  // Early out tiny splats
22900
22874
  if (v1_len_sq < minPixels && v2_len_sq < minPixels) {
22901
- o.member = discardVec;
22902
- o.vColor = vec4f(0.0);
22903
- o.vTexCoord = vec2f(0.0);
22904
- return o;
22875
+ return discardSplat();
22905
22876
  }
22906
22877
 
22907
22878
  // Cull oversized splats
22908
22879
  if (maxPixels > 0.0) {
22880
+ let maxPixelCullDistance = materialUniform.pixelCull.z;
22909
22881
  let splatDistance = length(splat_cam.xyz);
22910
22882
  if (maxPixelCullDistance <= 0.0 || splatDistance < maxPixelCullDistance) {
22911
22883
  let maxAxisSq = maxPixels * maxPixels;
22912
22884
  if (v1_len_sq > maxAxisSq || v2_len_sq > maxAxisSq) {
22913
- o.member = discardVec;
22914
- o.vColor = vec4f(0.0);
22915
- o.vTexCoord = vec2f(0.0);
22916
- return o;
22885
+ return discardSplat();
22917
22886
  }
22918
22887
  }
22919
22888
  }
22920
22889
 
22921
- // Final position
22922
- o.member = splat_proj + vec4f((vertex_pos.x * v1v2_scaled.xy + vertex_pos.y * v1v2_scaled.zw) / viewport * splat_proj.w, 0.0, 0.0);
22923
- o.vTexCoord = vertex_pos * scale / 2.0;
22890
+ // Final position calculation (optimized)
22891
+ let vertex_pos = position.xy;
22892
+ let inv_viewport = 1.0 / viewport;
22893
+ let offset = (vertex_pos.x * v1v2_scaled.xy + vertex_pos.y * v1v2_scaled.zw) * inv_viewport * splat_proj.w;
22894
+
22895
+ var o: VSOut;
22896
+ o.member = splat_proj + vec4f(offset, 0.0, 0.0);
22897
+ o.vTexCoord = vertex_pos * (scale * 0.5);
22924
22898
  o.vColor = color;
22925
22899
 
22926
22900
  return o;
@@ -22931,20 +22905,20 @@ const GSplat_FS = (
22931
22905
  /* wgsl */
22932
22906
  `
22933
22907
  #include "FragmentOutput"
22908
+
22909
+ // Constants
22910
+ const ALPHA_THRESHOLD: f32 = 0.00392156863; // 1.0 / 255.0
22911
+ const GAUSSIAN_SCALE: f32 = 4.0;
22934
22912
 
22935
- // === evalSplat() - like PlayCanvas splatCoreFS ===
22913
+ // === evalSplat() - optimized gaussian evaluation ===
22936
22914
  fn evalSplat(texCoord: vec2f, color: vec4f) -> vec4f {
22937
22915
  let A = dot(texCoord, texCoord);
22938
- var B = exp(-A * 4.0) * color.a;
22939
- if (A > 1.0) {
22940
- B = 0.0;
22941
- }
22942
22916
 
22943
- if (B < 1.0 / 255.0) {
22944
- B = 0.0;
22945
- }
22917
+ // Branch-less optimization using select
22918
+ let gaussian = exp(-A * GAUSSIAN_SCALE) * color.a;
22919
+ let alpha = select(gaussian, 0.0, A > 1.0 || gaussian < ALPHA_THRESHOLD);
22946
22920
 
22947
- return vec4f(color.rgb, B);
22921
+ return vec4f(color.rgb, alpha);
22948
22922
  }
22949
22923
 
22950
22924
  @fragment
@@ -23184,6 +23158,7 @@ let GSplatShader = class extends Shader {
23184
23158
  pass.shaderState.transparent = true;
23185
23159
  pass.shaderState.blendMode = BlendMode.NORMAL;
23186
23160
  pass.shaderState.writeMasks = [15, 15];
23161
+ pass.shaderState.castReflection = false;
23187
23162
  this.addRenderPass(pass);
23188
23163
  this.setDefault();
23189
23164
  }
@@ -24968,6 +24943,31 @@ let GSplatRenderer = class extends RenderNode {
24968
24943
  this._sortWorker.terminate();
24969
24944
  this._sortWorker = null;
24970
24945
  }
24946
+ if (this.splatColor) {
24947
+ this.splatColor.destroy(force);
24948
+ this.splatColor = null;
24949
+ }
24950
+ if (this.transformA) {
24951
+ this.transformA.destroy(force);
24952
+ this.transformA = null;
24953
+ }
24954
+ if (this.transformB) {
24955
+ this.transformB.destroy(force);
24956
+ this.transformB = null;
24957
+ }
24958
+ if (this.splatOrder) {
24959
+ this.splatOrder.destroy(force);
24960
+ this.splatOrder = null;
24961
+ }
24962
+ if (this.gsplatMaterial) {
24963
+ this.gsplatMaterial.destroy(force);
24964
+ this.gsplatMaterial = null;
24965
+ }
24966
+ this._positions = null;
24967
+ this._worldPositions = null;
24968
+ this._orderData = null;
24969
+ this.texParams = null;
24970
+ this._mapping = null;
24971
24971
  super.destroy(force);
24972
24972
  }
24973
24973
  };
@@ -26875,6 +26875,43 @@ class MorphTargetData {
26875
26875
  usage
26876
26876
  );
26877
26877
  }
26878
+ destroy(force) {
26879
+ if (this._computeConfigBuffer) {
26880
+ this._computeConfigBuffer.destroy(force);
26881
+ this._computeConfigBuffer = null;
26882
+ }
26883
+ if (this._morphInfluenceBuffer) {
26884
+ this._morphInfluenceBuffer.destroy(force);
26885
+ this._morphInfluenceBuffer = null;
26886
+ }
26887
+ if (this._computeShader) {
26888
+ this._computeShader.destroy(force);
26889
+ this._computeShader = null;
26890
+ }
26891
+ if (this._positionAttrDataGroup) {
26892
+ if (this._positionAttrDataGroup.input) {
26893
+ this._positionAttrDataGroup.input.destroy(force);
26894
+ }
26895
+ if (this._positionAttrDataGroup.output) {
26896
+ this._positionAttrDataGroup.output.destroy(force);
26897
+ }
26898
+ this._positionAttrDataGroup = null;
26899
+ }
26900
+ if (this._normalAttrDataGroup) {
26901
+ if (this._normalAttrDataGroup.input) {
26902
+ this._normalAttrDataGroup.input.destroy(force);
26903
+ }
26904
+ if (this._normalAttrDataGroup.output) {
26905
+ this._normalAttrDataGroup.output.destroy(force);
26906
+ }
26907
+ this._normalAttrDataGroup = null;
26908
+ }
26909
+ this._computeConfigArray = null;
26910
+ this._morphInfluenceArray = null;
26911
+ this._collectMorphTargetData = null;
26912
+ this._blendTarget = null;
26913
+ this._computeShaders = null;
26914
+ }
26878
26915
  }
26879
26916
 
26880
26917
  var __defProp = Object.defineProperty;
@@ -26986,6 +27023,10 @@ let MeshRenderer = class extends RenderNode {
26986
27023
  super.nodeUpdate(view, passType, renderPassState, clusterLightingBuffer);
26987
27024
  }
26988
27025
  destroy(force) {
27026
+ if (this.morphData) {
27027
+ this.morphData.destroy(force);
27028
+ this.morphData = null;
27029
+ }
26989
27030
  super.destroy(force);
26990
27031
  }
26991
27032
  };
@@ -40891,7 +40932,7 @@ class PostProcessingComponent extends ComponentBase {
40891
40932
  }
40892
40933
  }
40893
40934
 
40894
- const version = "1.0.13";
40935
+ const version = "1.0.14";
40895
40936
 
40896
40937
  class Engine3D {
40897
40938
  /**