@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.
@@ -22686,6 +22686,13 @@ struct InstanceData {
22686
22686
  `
22687
22687
  #include "GlobalUniform"
22688
22688
 
22689
+ // Constants
22690
+ const ALPHA_THRESHOLD: f32 = 0.00392156863; // 1.0 / 255.0
22691
+ const COV_COMPENSATION: f32 = 0.3;
22692
+ const MAX_SPLAT_SIZE: f32 = 1024.0;
22693
+ const MIN_LAMBDA: f32 = 0.1;
22694
+ const LOG_255: f32 = 5.541263545; // log(255.0) - natural log for WGSL
22695
+
22689
22696
  struct MaterialUniform {
22690
22697
  modelMatrix: mat4x4<f32>,
22691
22698
  tex_params: vec4<f32>, // [numSplats, texWidth, validCount, visBoost]
@@ -22705,107 +22712,92 @@ struct InstanceData {
22705
22712
  @group(1) @binding(3) var transformB: texture_2d<f32>;
22706
22713
  @group(1) @binding(4) var splatOrder: texture_2d<u32>;
22707
22714
 
22708
- // Global variables for texture lookups
22709
- var<private> splatUV: vec2<i32>;
22710
- var<private> splatId: u32;
22711
- var<private> tA: vec4<u32>;
22715
+ struct SplatData {
22716
+ center: vec3f,
22717
+ covA: vec3f,
22718
+ covB: vec3f,
22719
+ };
22712
22720
 
22713
- // === calcSplatUV() - returns bool ===
22714
- fn calcSplatUV(orderId: u32) -> bool {
22715
- let textureWidth = u32(materialUniform.tex_params.y);
22716
- let numSplats = u32(materialUniform.tex_params.x);
22717
-
22718
- if (orderId >= numSplats) {
22719
- return false;
22720
- }
22721
-
22721
+ // Helper function to discard splat
22722
+ fn discardSplat() -> VSOut {
22723
+ var o: VSOut;
22724
+ o.member = vec4f(0.0, 0.0, 2.0, 1.0);
22725
+ o.vColor = vec4f(0.0);
22726
+ o.vTexCoord = vec2f(0.0);
22727
+ return o;
22728
+ }
22729
+
22730
+ // === calcSplatUV() - returns optional UV ===
22731
+ fn calcSplatUV(orderId: u32, textureWidth: u32, numSplats: u32) -> vec2<i32> {
22722
22732
  let orderUV = vec2<i32>(
22723
22733
  i32(orderId % textureWidth),
22724
22734
  i32(orderId / textureWidth)
22725
22735
  );
22726
22736
 
22727
- // calculate splatUV
22728
- splatId = textureLoad(splatOrder, orderUV, 0).r;
22729
- splatUV = vec2<i32>(
22737
+ let splatId = textureLoad(splatOrder, orderUV, 0).r;
22738
+ return vec2<i32>(
22730
22739
  i32(splatId % textureWidth),
22731
22740
  i32(splatId / textureWidth)
22732
22741
  );
22733
-
22734
- return true;
22735
- }
22736
-
22737
- // === getCenter() - returns vec3 ===
22738
- fn getCenter() -> vec3f {
22739
- tA = textureLoad(transformA, splatUV, 0);
22740
- return vec3f(bitcast<f32>(tA.x), bitcast<f32>(tA.y), bitcast<f32>(tA.z));
22741
22742
  }
22742
22743
 
22743
- // Struct to return covA and covB
22744
- struct CovarianceData {
22745
- covA: vec3f,
22746
- covB: vec3f,
22747
- };
22748
-
22749
- // === getCovariance() - returns struct ===
22750
- fn getCovariance() -> CovarianceData {
22751
- let tB = textureLoad(transformB, splatUV, 0);
22744
+ // === getSplatData() - unified texture loading ===
22745
+ fn getSplatData(splatUV: vec2<i32>) -> SplatData {
22746
+ var data: SplatData;
22752
22747
 
22753
- // Use WGSL built-in unpack2x16float (equivalent to GLSL unpackHalf2x16)
22748
+ // Load both textures once
22749
+ let tA = textureLoad(transformA, splatUV, 0);
22750
+ let tB = textureLoad(transformB, splatUV, 0);
22754
22751
  let tC = unpack2x16float(tA.w);
22755
22752
 
22756
- var result: CovarianceData;
22757
- result.covA = tB.xyz;
22758
- result.covB = vec3f(tC.x, tC.y, tB.w);
22753
+ // Extract center
22754
+ data.center = vec3f(bitcast<f32>(tA.x), bitcast<f32>(tA.y), bitcast<f32>(tA.z));
22759
22755
 
22760
- return result;
22761
- }
22762
-
22763
- // === getRotationMatrix() - returns mat3x3 ===
22764
- fn getRotationMatrix() -> mat3x3f {
22765
- let cov_data = getCovariance();
22766
- let covA = cov_data.covA;
22767
- let covB = cov_data.covB;
22756
+ // Extract covariance
22757
+ data.covA = tB.xyz;
22758
+ data.covB = vec3f(tC.x, tC.y, tB.w);
22768
22759
 
22769
- return mat3x3f(
22770
- 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)),
22771
- 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)),
22772
- 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))
22773
- );
22760
+ return data;
22774
22761
  }
22775
22762
 
22776
22763
  // === calcV1V2() - returns vec4 ===
22777
22764
  fn calcV1V2(splat_cam: vec3f, covA: vec3f, covB: vec3f, W: mat3x3f, viewport: vec2f, projMat: mat4x4f) -> vec4f {
22765
+ // Construct symmetric covariance matrix
22778
22766
  let Vrk = mat3x3f(
22779
22767
  vec3f(covA.x, covA.y, covA.z),
22780
22768
  vec3f(covA.y, covB.x, covB.y),
22781
22769
  vec3f(covA.z, covB.y, covB.z)
22782
22770
  );
22783
22771
 
22772
+ // Calculate Jacobian
22784
22773
  let focal = viewport.x * projMat[0][0];
22785
-
22786
- let J1 = focal / splat_cam.z;
22787
- let J2 = -J1 / splat_cam.z * splat_cam.xy;
22774
+ let inv_z = 1.0 / splat_cam.z;
22775
+ let J1 = focal * inv_z;
22776
+ let J2 = -J1 * inv_z * splat_cam.xy;
22788
22777
  let J = mat3x3f(
22789
22778
  vec3f(J1, 0.0, J2.x),
22790
22779
  vec3f(0.0, J1, J2.y),
22791
22780
  vec3f(0.0, 0.0, 0.0)
22792
22781
  );
22793
22782
 
22783
+ // Project covariance to screen space
22794
22784
  let T = W * J;
22795
22785
  let cov = transpose(T) * Vrk * T;
22796
22786
 
22797
- let diagonal1 = cov[0][0] + 0.3;
22787
+ // Eigenvalue decomposition with compensation
22788
+ let diagonal1 = cov[0][0] + COV_COMPENSATION;
22798
22789
  let offDiagonal = cov[0][1];
22799
- let diagonal2 = cov[1][1] + 0.3;
22790
+ let diagonal2 = cov[1][1] + COV_COMPENSATION;
22800
22791
 
22801
22792
  let mid = 0.5 * (diagonal1 + diagonal2);
22802
- let radius = length(vec2f((diagonal1 - diagonal2) / 2.0, offDiagonal));
22793
+ let radius = length(vec2f((diagonal1 - diagonal2) * 0.5, offDiagonal));
22803
22794
  let lambda1 = mid + radius;
22804
- let lambda2 = max(mid - radius, 0.1);
22795
+ let lambda2 = max(mid - radius, MIN_LAMBDA);
22805
22796
  let diagonalVector = normalize(vec2f(offDiagonal, lambda1 - diagonal1));
22806
22797
 
22807
- let v1 = min(sqrt(2.0 * lambda1), 1024.0) * diagonalVector;
22808
- let v2 = min(sqrt(2.0 * lambda2), 1024.0) * vec2f(diagonalVector.y, -diagonalVector.x);
22798
+ // Calculate axis vectors with size clamping
22799
+ let v1 = min(sqrt(2.0 * lambda1), MAX_SPLAT_SIZE) * diagonalVector;
22800
+ let v2 = min(sqrt(2.0 * lambda2), MAX_SPLAT_SIZE) * vec2f(diagonalVector.y, -diagonalVector.x);
22809
22801
 
22810
22802
  // WebGPU Y-axis flip: WebGPU NDC Y goes from top(-1) to bottom(1), opposite of WebGL
22811
22803
  return vec4f(v1.x, -v1.y, v2.x, -v2.y);
@@ -22819,115 +22811,97 @@ struct InstanceData {
22819
22811
  @builtin(instance_index) iid : u32,
22820
22812
  @location(0) position: vec3<f32> // vertex_position from mesh (x, y, local_index)
22821
22813
  ) -> VSOut {
22822
- var o: VSOut;
22823
- let discardVec = vec4f(0.0, 0.0, 2.0, 1.0);
22824
-
22825
22814
  // Calculate splat ID
22826
- // orderId = vertex_id_attrib + uint(vertex_position.z)
22827
- // In our case: vertex_id_attrib = iid * batchSize
22828
22815
  let batchSize = u32(materialUniform.pixelCull.w);
22829
- let base_splat_index = iid * batchSize;
22830
- let local_splat_index = u32(position.z);
22831
- let orderId = base_splat_index + local_splat_index;
22816
+ let orderId = iid * batchSize + u32(position.z);
22832
22817
 
22833
- // Use vertex position from mesh
22834
- let vertex_pos = position.xy;
22835
-
22836
- // calculate splat uv
22837
- if (!calcSplatUV(orderId)) {
22838
- o.member = discardVec;
22839
- o.vColor = vec4f(0.0);
22840
- o.vTexCoord = vec2f(0.0);
22841
- return o;
22818
+ // Early bounds check
22819
+ let textureWidth = u32(materialUniform.tex_params.y);
22820
+ let numSplats = u32(materialUniform.tex_params.x);
22821
+ if (orderId >= numSplats) {
22822
+ return discardSplat();
22842
22823
  }
22843
22824
 
22844
- // get center
22845
- let center = getCenter();
22825
+ // Calculate splat UV and load all data in one go
22826
+ let splatUV = calcSplatUV(orderId, textureWidth, numSplats);
22827
+ let splatData = getSplatData(splatUV);
22846
22828
 
22847
- // handle transforms
22829
+ // Load color early for alpha test
22830
+ let color = textureLoad(splatColor, splatUV, 0);
22831
+ if (color.a < ALPHA_THRESHOLD) {
22832
+ return discardSplat();
22833
+ }
22834
+
22835
+ // Transform matrices
22836
+ let matrix_model = materialUniform.modelMatrix;
22848
22837
  let matrix_view = globalUniform.viewMat;
22849
22838
  let matrix_projection = globalUniform.projMat;
22850
- let matrix_model = materialUniform.modelMatrix;
22851
-
22852
22839
  let model_view = matrix_view * matrix_model;
22853
- let splat_cam = model_view * vec4f(center, 1.0);
22854
- let splat_proj = matrix_projection * splat_cam;
22855
22840
 
22856
- // Frustum culling: cull splats behind camera
22857
- if (splat_proj.z < 0.0) {
22858
- o.member = discardVec;
22859
- o.vColor = vec4f(0.0);
22860
- o.vTexCoord = vec2f(0.0);
22861
- return o;
22841
+ // Transform center to camera and clip space
22842
+ let splat_cam = model_view * vec4f(splatData.center, 1.0);
22843
+
22844
+ // Early depth culling
22845
+ if (splat_cam.z <= 0.0) {
22846
+ return discardSplat();
22862
22847
  }
22863
22848
 
22864
- // Frustum culling: cull splats outside screen bounds
22865
- let ndc = splat_proj.xyz / splat_proj.w;
22866
- let margin = 0.0;
22867
- if (ndc.x < -1.0 - margin || ndc.x > 1.0 + margin ||
22868
- ndc.y < -1.0 - margin || ndc.y > 1.0 + margin ||
22849
+ let splat_proj = matrix_projection * splat_cam;
22850
+
22851
+ // Frustum culling with NDC check
22852
+ let inv_w = 1.0 / splat_proj.w;
22853
+ let ndc = splat_proj.xyz * inv_w;
22854
+ if (ndc.x < -1.0 || ndc.x > 1.0 ||
22855
+ ndc.y < -1.0 || ndc.y > 1.0 ||
22869
22856
  ndc.z < 0.0 || ndc.z > 1.0) {
22870
- o.member = discardVec;
22871
- o.vColor = vec4f(0.0);
22872
- o.vTexCoord = vec2f(0.0);
22873
- return o;
22857
+ return discardSplat();
22874
22858
  }
22875
22859
 
22876
- // get covariance
22877
- let cov_data = getCovariance();
22878
-
22860
+ // Calculate v1v2 (screen-space ellipse axes)
22879
22861
  let viewport = vec2f(globalUniform.windowWidth, globalUniform.windowHeight);
22880
- 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);
22862
+ let W = transpose(mat3x3f(model_view[0].xyz, model_view[1].xyz, model_view[2].xyz));
22863
+ let v1v2 = calcV1V2(splat_cam.xyz, splatData.covA, splatData.covB, W, viewport, matrix_projection);
22881
22864
 
22882
- // get color
22883
- let color = textureLoad(splatColor, splatUV, 0);
22884
- if (color.a < 1.0 / 255.0) {
22885
- o.member = discardVec;
22886
- o.vColor = vec4f(0.0);
22887
- o.vTexCoord = vec2f(0.0);
22888
- return o;
22889
- }
22865
+ // Calculate scale based on alpha (optimized formula)
22866
+ let scale = min(1.0, sqrt(LOG_255 + log(color.a)) * 0.5);
22890
22867
 
22891
- // calculate scale based on alpha
22892
- let scale = min(1.0, sqrt(-log(1.0 / 255.0 / color.a)) / 2.0);
22893
-
22894
- // apply visBoost (size multiplier)
22868
+ // Apply visBoost (size multiplier)
22895
22869
  let visBoost = materialUniform.tex_params.w;
22896
- var v1v2_scaled = v1v2 * scale * visBoost;
22870
+ let v1v2_scaled = v1v2 * (scale * visBoost);
22897
22871
 
22898
- // Pixel coverage culling
22899
- let v1_len_sq = dot(v1v2_scaled.xy, v1v2_scaled.xy);
22900
- let v2_len_sq = dot(v1v2_scaled.zw, v1v2_scaled.zw);
22872
+ // Pixel coverage culling (vectorized squared length calculation)
22873
+ let v1v2_sq = v1v2_scaled * v1v2_scaled;
22874
+ let v1_len_sq = v1v2_sq.x + v1v2_sq.y;
22875
+ let v2_len_sq = v1v2_sq.z + v1v2_sq.w;
22901
22876
 
22902
22877
  let minPixels = materialUniform.pixelCull.x;
22903
22878
  let maxPixels = materialUniform.pixelCull.y;
22904
- let maxPixelCullDistance = materialUniform.pixelCull.z;
22905
22879
 
22906
22880
  // Early out tiny splats
22907
22881
  if (v1_len_sq < minPixels && v2_len_sq < minPixels) {
22908
- o.member = discardVec;
22909
- o.vColor = vec4f(0.0);
22910
- o.vTexCoord = vec2f(0.0);
22911
- return o;
22882
+ return discardSplat();
22912
22883
  }
22913
22884
 
22914
22885
  // Cull oversized splats
22915
22886
  if (maxPixels > 0.0) {
22887
+ let maxPixelCullDistance = materialUniform.pixelCull.z;
22916
22888
  let splatDistance = length(splat_cam.xyz);
22917
22889
  if (maxPixelCullDistance <= 0.0 || splatDistance < maxPixelCullDistance) {
22918
22890
  let maxAxisSq = maxPixels * maxPixels;
22919
22891
  if (v1_len_sq > maxAxisSq || v2_len_sq > maxAxisSq) {
22920
- o.member = discardVec;
22921
- o.vColor = vec4f(0.0);
22922
- o.vTexCoord = vec2f(0.0);
22923
- return o;
22892
+ return discardSplat();
22924
22893
  }
22925
22894
  }
22926
22895
  }
22927
22896
 
22928
- // Final position
22929
- 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);
22930
- o.vTexCoord = vertex_pos * scale / 2.0;
22897
+ // Final position calculation (optimized)
22898
+ let vertex_pos = position.xy;
22899
+ let inv_viewport = 1.0 / viewport;
22900
+ let offset = (vertex_pos.x * v1v2_scaled.xy + vertex_pos.y * v1v2_scaled.zw) * inv_viewport * splat_proj.w;
22901
+
22902
+ var o: VSOut;
22903
+ o.member = splat_proj + vec4f(offset, 0.0, 0.0);
22904
+ o.vTexCoord = vertex_pos * (scale * 0.5);
22931
22905
  o.vColor = color;
22932
22906
 
22933
22907
  return o;
@@ -22938,20 +22912,20 @@ struct InstanceData {
22938
22912
  /* wgsl */
22939
22913
  `
22940
22914
  #include "FragmentOutput"
22915
+
22916
+ // Constants
22917
+ const ALPHA_THRESHOLD: f32 = 0.00392156863; // 1.0 / 255.0
22918
+ const GAUSSIAN_SCALE: f32 = 4.0;
22941
22919
 
22942
- // === evalSplat() - like PlayCanvas splatCoreFS ===
22920
+ // === evalSplat() - optimized gaussian evaluation ===
22943
22921
  fn evalSplat(texCoord: vec2f, color: vec4f) -> vec4f {
22944
22922
  let A = dot(texCoord, texCoord);
22945
- var B = exp(-A * 4.0) * color.a;
22946
- if (A > 1.0) {
22947
- B = 0.0;
22948
- }
22949
22923
 
22950
- if (B < 1.0 / 255.0) {
22951
- B = 0.0;
22952
- }
22924
+ // Branch-less optimization using select
22925
+ let gaussian = exp(-A * GAUSSIAN_SCALE) * color.a;
22926
+ let alpha = select(gaussian, 0.0, A > 1.0 || gaussian < ALPHA_THRESHOLD);
22953
22927
 
22954
- return vec4f(color.rgb, B);
22928
+ return vec4f(color.rgb, alpha);
22955
22929
  }
22956
22930
 
22957
22931
  @fragment
@@ -23191,6 +23165,7 @@ struct InstanceData {
23191
23165
  pass.shaderState.transparent = true;
23192
23166
  pass.shaderState.blendMode = BlendMode.NORMAL;
23193
23167
  pass.shaderState.writeMasks = [15, 15];
23168
+ pass.shaderState.castReflection = false;
23194
23169
  this.addRenderPass(pass);
23195
23170
  this.setDefault();
23196
23171
  }
@@ -24975,6 +24950,31 @@ struct InstanceData {
24975
24950
  this._sortWorker.terminate();
24976
24951
  this._sortWorker = null;
24977
24952
  }
24953
+ if (this.splatColor) {
24954
+ this.splatColor.destroy(force);
24955
+ this.splatColor = null;
24956
+ }
24957
+ if (this.transformA) {
24958
+ this.transformA.destroy(force);
24959
+ this.transformA = null;
24960
+ }
24961
+ if (this.transformB) {
24962
+ this.transformB.destroy(force);
24963
+ this.transformB = null;
24964
+ }
24965
+ if (this.splatOrder) {
24966
+ this.splatOrder.destroy(force);
24967
+ this.splatOrder = null;
24968
+ }
24969
+ if (this.gsplatMaterial) {
24970
+ this.gsplatMaterial.destroy(force);
24971
+ this.gsplatMaterial = null;
24972
+ }
24973
+ this._positions = null;
24974
+ this._worldPositions = null;
24975
+ this._orderData = null;
24976
+ this.texParams = null;
24977
+ this._mapping = null;
24978
24978
  super.destroy(force);
24979
24979
  }
24980
24980
  };
@@ -26882,6 +26882,43 @@ struct InstanceData {
26882
26882
  usage
26883
26883
  );
26884
26884
  }
26885
+ destroy(force) {
26886
+ if (this._computeConfigBuffer) {
26887
+ this._computeConfigBuffer.destroy(force);
26888
+ this._computeConfigBuffer = null;
26889
+ }
26890
+ if (this._morphInfluenceBuffer) {
26891
+ this._morphInfluenceBuffer.destroy(force);
26892
+ this._morphInfluenceBuffer = null;
26893
+ }
26894
+ if (this._computeShader) {
26895
+ this._computeShader.destroy(force);
26896
+ this._computeShader = null;
26897
+ }
26898
+ if (this._positionAttrDataGroup) {
26899
+ if (this._positionAttrDataGroup.input) {
26900
+ this._positionAttrDataGroup.input.destroy(force);
26901
+ }
26902
+ if (this._positionAttrDataGroup.output) {
26903
+ this._positionAttrDataGroup.output.destroy(force);
26904
+ }
26905
+ this._positionAttrDataGroup = null;
26906
+ }
26907
+ if (this._normalAttrDataGroup) {
26908
+ if (this._normalAttrDataGroup.input) {
26909
+ this._normalAttrDataGroup.input.destroy(force);
26910
+ }
26911
+ if (this._normalAttrDataGroup.output) {
26912
+ this._normalAttrDataGroup.output.destroy(force);
26913
+ }
26914
+ this._normalAttrDataGroup = null;
26915
+ }
26916
+ this._computeConfigArray = null;
26917
+ this._morphInfluenceArray = null;
26918
+ this._collectMorphTargetData = null;
26919
+ this._blendTarget = null;
26920
+ this._computeShaders = null;
26921
+ }
26885
26922
  }
26886
26923
 
26887
26924
  var __defProp = Object.defineProperty;
@@ -26993,6 +27030,10 @@ struct InstanceData {
26993
27030
  super.nodeUpdate(view, passType, renderPassState, clusterLightingBuffer);
26994
27031
  }
26995
27032
  destroy(force) {
27033
+ if (this.morphData) {
27034
+ this.morphData.destroy(force);
27035
+ this.morphData = null;
27036
+ }
26996
27037
  super.destroy(force);
26997
27038
  }
26998
27039
  };
@@ -40898,7 +40939,7 @@ fn CsMain( @builtin(workgroup_id) workgroup_id : vec3<u32> , @builtin(global_inv
40898
40939
  }
40899
40940
  }
40900
40941
 
40901
- const version = "1.0.13";
40942
+ const version = "1.0.14";
40902
40943
 
40903
40944
  class Engine3D {
40904
40945
  /**
@@ -48,5 +48,6 @@ export declare class MorphTargetData {
48
48
  private calcWorkGroup;
49
49
  protected uploadMorphTargetBuffer(): void;
50
50
  protected generateGPUBuffer(): void;
51
+ destroy(force?: boolean): void;
51
52
  }
52
53
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rings-webgpu/core",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Rings webgpu Engine",
5
5
  "main": "index.js",
6
6
  "exports": {