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.
- package/fesm2022/angular-three-soba-misc.mjs +159 -22
- package/fesm2022/angular-three-soba-misc.mjs.map +1 -1
- package/package.json +1 -1
- package/types/angular-three-soba-abstractions.d.ts +71 -71
- package/types/angular-three-soba-materials.d.ts +6 -6
- package/types/angular-three-soba-staging.d.ts +9 -9
|
@@ -1816,13 +1816,11 @@ const defaultOptions = {
|
|
|
1816
1816
|
samples: 10,
|
|
1817
1817
|
focus: 0,
|
|
1818
1818
|
};
|
|
1819
|
-
|
|
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;
|
|
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;
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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;
|
|
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
|
-
|
|
1958
|
-
.
|
|
1959
|
-
|
|
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;
|