angular-three-soba 4.0.5 → 4.0.7

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.
@@ -1816,13 +1816,11 @@ const defaultOptions = {
1816
1816
  samples: 10,
1817
1817
  focus: 0,
1818
1818
  };
1819
- function pcss(options) {
1819
+ /**
1820
+ * Generates PCSS shader code for Three.js < r182 (uses RGBA-packed depth)
1821
+ */
1822
+ function pcssLegacy(options) {
1820
1823
  const { focus, size, samples } = options;
1821
- // Three.js r182 removed unpackRGBAToDepth and switched to native depth textures
1822
- const useNativeDepth = getVersion() >= 182;
1823
- const sampleDepth = useNativeDepth
1824
- ? 'texture2D( shadowMap, uv + offset ).r'
1825
- : 'unpackRGBAToDepth( texture2D( shadowMap, uv + offset ) )';
1826
1824
  return `
1827
1825
  #define PENUMBRA_FILTER_SIZE float(${size})
1828
1826
  #define RGB_NOISE_FUNCTION(uv) (randRGB(uv))
@@ -1835,8 +1833,6 @@ vec3 randRGB(vec2 uv) {
1835
1833
  }
1836
1834
 
1837
1835
  vec3 lowPassRandRGB(vec2 uv) {
1838
- // 3x3 convolution (average)
1839
- // can be implemented as separable with an extra buffer for a total of 6 samples instead of 9
1840
1836
  vec3 result = vec3(0);
1841
1837
  result += RGB_NOISE_FUNCTION(uv + vec2(-1.0, -1.0));
1842
1838
  result += RGB_NOISE_FUNCTION(uv + vec2(-1.0, 0.0));
@@ -1847,32 +1843,31 @@ vec3 lowPassRandRGB(vec2 uv) {
1847
1843
  result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, -1.0));
1848
1844
  result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, 0.0));
1849
1845
  result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, +1.0));
1850
- result *= 0.111111111; // 1.0 / 9.0
1846
+ result *= 0.111111111;
1851
1847
  return result;
1852
1848
  }
1849
+
1853
1850
  vec3 highPassRandRGB(vec2 uv) {
1854
- // by subtracting the low-pass signal from the original signal, we're being left with the high-pass signal
1855
- // hp(x) = x - lp(x)
1856
1851
  return RGB_NOISE_FUNCTION(uv) - lowPassRandRGB(uv) + 0.5;
1857
1852
  }
1858
1853
 
1859
-
1860
1854
  vec2 vogelDiskSample(int sampleIndex, int sampleCount, float angle) {
1861
- const float goldenAngle = 2.399963f; // radians
1855
+ const float goldenAngle = 2.399963f;
1862
1856
  float r = sqrt(float(sampleIndex) + 0.5f) / sqrt(float(sampleCount));
1863
1857
  float theta = float(sampleIndex) * goldenAngle + angle;
1864
1858
  float sine = sin(theta);
1865
1859
  float cosine = cos(theta);
1866
1860
  return vec2(cosine, sine) * r;
1867
1861
  }
1868
- float penumbraSize( const in float zReceiver, const in float zBlocker ) { // Parallel plane estimation
1862
+
1863
+ float penumbraSize( const in float zReceiver, const in float zBlocker ) {
1869
1864
  return (zReceiver - zBlocker) / zBlocker;
1870
1865
  }
1866
+
1871
1867
  float findBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
1872
1868
  float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
1873
1869
  float blockerDepthSum = float(${focus});
1874
1870
  float blockers = 0.0;
1875
-
1876
1871
  int j = 0;
1877
1872
  vec2 offset = vec2(0.);
1878
1873
  float depth = 0.;
@@ -1880,7 +1875,7 @@ float findBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
1880
1875
  #pragma unroll_loop_start
1881
1876
  for(int i = 0; i < ${samples}; i ++) {
1882
1877
  offset = (vogelDiskSample(j, ${samples}, angle) * texelSize) * 2.0 * PENUMBRA_FILTER_SIZE;
1883
- depth = ${sampleDepth};
1878
+ depth = unpackRGBAToDepth( texture2D( shadowMap, uv + offset ) );
1884
1879
  if (depth < compare) {
1885
1880
  blockerDepthSum += depth;
1886
1881
  blockers++;
@@ -1895,27 +1890,28 @@ float findBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
1895
1890
  return -1.0;
1896
1891
  }
1897
1892
 
1898
-
1899
1893
  float vogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRadius, float angle) {
1900
1894
  float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
1901
1895
  float shadow = 0.0f;
1902
1896
  int j = 0;
1903
1897
  vec2 vogelSample = vec2(0.0);
1904
1898
  vec2 offset = vec2(0.0);
1899
+
1905
1900
  #pragma unroll_loop_start
1906
1901
  for (int i = 0; i < ${samples}; i++) {
1907
1902
  vogelSample = vogelDiskSample(j, ${samples}, angle) * texelSize;
1908
1903
  offset = vogelSample * (1.0 + filterRadius * float(${size}));
1909
- shadow += step( zReceiver, ${sampleDepth} );
1904
+ shadow += step( zReceiver, unpackRGBAToDepth( texture2D( shadowMap, uv + offset ) ) );
1910
1905
  j++;
1911
1906
  }
1912
1907
  #pragma unroll_loop_end
1908
+
1913
1909
  return shadow * 1.0 / ${samples}.0;
1914
1910
  }
1915
1911
 
1916
1912
  float PCSS (sampler2D shadowMap, vec4 coords) {
1917
1913
  vec2 uv = coords.xy;
1918
- float zReceiver = coords.z; // Assumed to be eye-space z in this code
1914
+ float zReceiver = coords.z;
1919
1915
  float angle = highPassRandRGB(gl_FragCoord.xy).r * PI2;
1920
1916
  float avgBlockerDepth = findBlocker(shadowMap, uv, zReceiver, angle);
1921
1917
  if (avgBlockerDepth == -1.0) {
@@ -1925,6 +1921,112 @@ float PCSS (sampler2D shadowMap, vec4 coords) {
1925
1921
  return vogelFilter(shadowMap, uv, zReceiver, 1.25 * penumbraRatio, angle);
1926
1922
  }`;
1927
1923
  }
1924
+ /**
1925
+ * Generates PCSS shader code for Three.js >= r182 (uses native depth textures)
1926
+ */
1927
+ function pcssModern(options) {
1928
+ const { focus, size, samples } = options;
1929
+ return `
1930
+ #define PENUMBRA_FILTER_SIZE float(${size})
1931
+ #define RGB_NOISE_FUNCTION(uv) (randRGB(uv))
1932
+ vec3 randRGB(vec2 uv) {
1933
+ return vec3(
1934
+ fract(sin(dot(uv, vec2(12.75613, 38.12123))) * 13234.76575),
1935
+ fract(sin(dot(uv, vec2(19.45531, 58.46547))) * 43678.23431),
1936
+ fract(sin(dot(uv, vec2(23.67817, 78.23121))) * 93567.23423)
1937
+ );
1938
+ }
1939
+
1940
+ vec3 lowPassRandRGB(vec2 uv) {
1941
+ vec3 result = vec3(0);
1942
+ result += RGB_NOISE_FUNCTION(uv + vec2(-1.0, -1.0));
1943
+ result += RGB_NOISE_FUNCTION(uv + vec2(-1.0, 0.0));
1944
+ result += RGB_NOISE_FUNCTION(uv + vec2(-1.0, +1.0));
1945
+ result += RGB_NOISE_FUNCTION(uv + vec2( 0.0, -1.0));
1946
+ result += RGB_NOISE_FUNCTION(uv + vec2( 0.0, 0.0));
1947
+ result += RGB_NOISE_FUNCTION(uv + vec2( 0.0, +1.0));
1948
+ result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, -1.0));
1949
+ result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, 0.0));
1950
+ result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, +1.0));
1951
+ result *= 0.111111111;
1952
+ return result;
1953
+ }
1954
+
1955
+ vec3 highPassRandRGB(vec2 uv) {
1956
+ return RGB_NOISE_FUNCTION(uv) - lowPassRandRGB(uv) + 0.5;
1957
+ }
1958
+
1959
+ vec2 pcssVogelDiskSample(int sampleIndex, int sampleCount, float angle) {
1960
+ const float goldenAngle = 2.399963f;
1961
+ float r = sqrt(float(sampleIndex) + 0.5f) / sqrt(float(sampleCount));
1962
+ float theta = float(sampleIndex) * goldenAngle + angle;
1963
+ float sine = sin(theta);
1964
+ float cosine = cos(theta);
1965
+ return vec2(cosine, sine) * r;
1966
+ }
1967
+
1968
+ float pcssPenumbraSize( const in float zReceiver, const in float zBlocker ) {
1969
+ return (zReceiver - zBlocker) / zBlocker;
1970
+ }
1971
+
1972
+ float pcssFindBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
1973
+ float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
1974
+ float blockerDepthSum = float(${focus});
1975
+ float blockers = 0.0;
1976
+ int j = 0;
1977
+ vec2 offset = vec2(0.);
1978
+ float depth = 0.;
1979
+
1980
+ #pragma unroll_loop_start
1981
+ for(int i = 0; i < ${samples}; i ++) {
1982
+ offset = (pcssVogelDiskSample(j, ${samples}, angle) * texelSize) * 2.0 * PENUMBRA_FILTER_SIZE;
1983
+ depth = texture2D( shadowMap, uv + offset ).r;
1984
+ if (depth < compare) {
1985
+ blockerDepthSum += depth;
1986
+ blockers++;
1987
+ }
1988
+ j++;
1989
+ }
1990
+ #pragma unroll_loop_end
1991
+
1992
+ if (blockers > 0.0) {
1993
+ return blockerDepthSum / blockers;
1994
+ }
1995
+ return -1.0;
1996
+ }
1997
+
1998
+ float pcssVogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRadius, float angle) {
1999
+ float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
2000
+ float shadow = 0.0f;
2001
+ int j = 0;
2002
+ vec2 vogelSample = vec2(0.0);
2003
+ vec2 offset = vec2(0.0);
2004
+
2005
+ #pragma unroll_loop_start
2006
+ for (int i = 0; i < ${samples}; i++) {
2007
+ vogelSample = pcssVogelDiskSample(j, ${samples}, angle) * texelSize;
2008
+ offset = vogelSample * (1.0 + filterRadius * float(${size}));
2009
+ shadow += step( zReceiver, texture2D( shadowMap, uv + offset ).r );
2010
+ j++;
2011
+ }
2012
+ #pragma unroll_loop_end
2013
+
2014
+ return shadow * 1.0 / ${samples}.0;
2015
+ }
2016
+
2017
+ float PCSS (sampler2D shadowMap, vec4 coords, float shadowIntensity) {
2018
+ vec2 uv = coords.xy;
2019
+ float zReceiver = coords.z;
2020
+ float angle = highPassRandRGB(gl_FragCoord.xy).r * PI2;
2021
+ float avgBlockerDepth = pcssFindBlocker(shadowMap, uv, zReceiver, angle);
2022
+ if (avgBlockerDepth == -1.0) {
2023
+ return 1.0;
2024
+ }
2025
+ float penumbraRatio = pcssPenumbraSize(zReceiver, avgBlockerDepth);
2026
+ float shadow = pcssVogelFilter(shadowMap, uv, zReceiver, 1.25 * penumbraRatio, angle);
2027
+ return mix( 1.0, shadow, shadowIntensity );
2028
+ }`;
2029
+ }
1928
2030
  function reset(gl, scene, camera) {
1929
2031
  scene.traverse((object) => {
1930
2032
  if (object.material) {
@@ -1953,10 +2055,45 @@ class NgtsSoftShadows {
1953
2055
  effect((onCleanup) => {
1954
2056
  const { gl, scene, camera } = store.snapshot;
1955
2057
  const options = this.options();
2058
+ const version = getVersion();
1956
2059
  const original = THREE.ShaderChunk.shadowmap_pars_fragment;
1957
- THREE.ShaderChunk.shadowmap_pars_fragment = THREE.ShaderChunk.shadowmap_pars_fragment
1958
- .replace('#ifdef USE_SHADOWMAP', '#ifdef USE_SHADOWMAP\n' + pcss(options))
1959
- .replace('#if defined( SHADOWMAP_TYPE_PCF )', '\nreturn PCSS(shadowMap, shadowCoord);\n#if defined( SHADOWMAP_TYPE_PCF )');
2060
+ if (version >= 182) {
2061
+ // Three.js r182+ uses native depth textures and has a different shader structure.
2062
+ // The PCF path uses sampler2DShadow, but PCSS needs sampler2D for manual depth comparison.
2063
+ // We inject our PCSS code and replace the BASIC shadow type's getShadow function,
2064
+ // then also replace the PCF uniform declarations to use sampler2D instead of sampler2DShadow.
2065
+ const pcssCode = pcssModern(options);
2066
+ let shader = THREE.ShaderChunk.shadowmap_pars_fragment;
2067
+ // 1. Inject PCSS functions after USE_SHADOWMAP
2068
+ shader = shader.replace('#ifdef USE_SHADOWMAP', '#ifdef USE_SHADOWMAP\n' + pcssCode);
2069
+ // 2. Replace sampler2DShadow with sampler2D for directional lights (PCF path)
2070
+ shader = shader.replace(/#if defined\( SHADOWMAP_TYPE_PCF \)\s+uniform sampler2DShadow directionalShadowMap\[ NUM_DIR_LIGHT_SHADOWS \];/, `#if defined( SHADOWMAP_TYPE_PCF )
2071
+ uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];`);
2072
+ // 3. Replace sampler2DShadow with sampler2D for spot lights (PCF path)
2073
+ shader = shader.replace(/#if defined\( SHADOWMAP_TYPE_PCF \)\s+uniform sampler2DShadow spotShadowMap\[ NUM_SPOT_LIGHT_SHADOWS \];/, `#if defined( SHADOWMAP_TYPE_PCF )
2074
+ uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];`);
2075
+ // 4. Replace the PCF getShadow function to use our PCSS
2076
+ // Match from the function signature to its closing brace
2077
+ const getShadowPCFRegex = /(#if defined\( SHADOWMAP_TYPE_PCF \)\s+float getShadow\( sampler2DShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord \) \{[\s\S]*?return mix\( 1\.0, shadow, shadowIntensity \);\s*\})/;
2078
+ shader = shader.replace(getShadowPCFRegex, `#if defined( SHADOWMAP_TYPE_PCF )
2079
+ float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
2080
+ shadowCoord.xyz /= shadowCoord.w;
2081
+ shadowCoord.z += shadowBias;
2082
+ bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
2083
+ bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
2084
+ if ( frustumTest ) {
2085
+ return PCSS( shadowMap, shadowCoord, shadowIntensity );
2086
+ }
2087
+ return 1.0;
2088
+ }`);
2089
+ THREE.ShaderChunk.shadowmap_pars_fragment = shader;
2090
+ }
2091
+ else {
2092
+ // Three.js < r182 uses RGBA-packed depth
2093
+ THREE.ShaderChunk.shadowmap_pars_fragment = THREE.ShaderChunk.shadowmap_pars_fragment
2094
+ .replace('#ifdef USE_SHADOWMAP', '#ifdef USE_SHADOWMAP\n' + pcssLegacy(options))
2095
+ .replace('#if defined( SHADOWMAP_TYPE_PCF )', '\nreturn PCSS(shadowMap, shadowCoord);\n#if defined( SHADOWMAP_TYPE_PCF )');
2096
+ }
1960
2097
  reset(gl, scene, camera);
1961
2098
  onCleanup(() => {
1962
2099
  THREE.ShaderChunk.shadowmap_pars_fragment = original;