@woosh/meep-engine 2.141.0 → 2.143.0

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.
Files changed (59) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts +1 -1
  3. package/src/core/geom/3d/shape/CapsuleShape3D.js +1 -1
  4. package/src/core/geom/3d/shape/SphereShape3D.d.ts +47 -0
  5. package/src/core/geom/3d/shape/SphereShape3D.d.ts.map +1 -0
  6. package/src/core/geom/3d/shape/SphereShape3D.js +127 -0
  7. package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts +30 -18
  8. package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts.map +1 -1
  9. package/src/core/geom/3d/shape/UnitSphereShape3D.js +44 -92
  10. package/src/core/geom/3d/shape/json/shape_to_type.d.ts.map +1 -1
  11. package/src/core/geom/3d/shape/json/shape_to_type.js +4 -2
  12. package/src/core/geom/3d/shape/json/type_adapters.d.ts +12 -3
  13. package/src/core/geom/3d/shape/json/type_adapters.d.ts.map +1 -1
  14. package/src/core/geom/3d/shape/json/type_adapters.js +16 -4
  15. package/src/core/geom/3d/shape/util/shape_to_visual_entity.js +2 -2
  16. package/src/engine/control/first-person/DESIGN_COLLISION.md +255 -0
  17. package/src/engine/control/first-person/prototype_first_person_controller.js +5 -0
  18. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map +1 -1
  19. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +70 -43
  20. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.d.ts +12 -22
  21. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.d.ts.map +1 -1
  22. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.js +345 -186
  23. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.d.ts +44 -0
  24. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.d.ts.map +1 -0
  25. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.js +151 -0
  26. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.d.ts +14 -0
  27. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.d.ts.map +1 -0
  28. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.js +78 -0
  29. package/src/engine/physics/PLAN.md +705 -578
  30. package/src/engine/physics/REVIEW_003.md +166 -0
  31. package/src/engine/physics/constraint/solve_constraints.d.ts +24 -2
  32. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  33. package/src/engine/physics/constraint/solve_constraints.js +402 -165
  34. package/src/engine/physics/ecs/Joint.d.ts +115 -0
  35. package/src/engine/physics/ecs/Joint.d.ts.map +1 -1
  36. package/src/engine/physics/ecs/Joint.js +168 -0
  37. package/src/engine/physics/ecs/JointSerializationAdapter.d.ts +29 -0
  38. package/src/engine/physics/ecs/JointSerializationAdapter.d.ts.map +1 -0
  39. package/src/engine/physics/ecs/JointSerializationAdapter.js +72 -0
  40. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  41. package/src/engine/physics/narrowphase/narrowphase_step.js +20 -13
  42. package/src/engine/physics/narrowphase/ray_shapes.d.ts +66 -0
  43. package/src/engine/physics/narrowphase/ray_shapes.d.ts.map +1 -0
  44. package/src/engine/physics/narrowphase/ray_shapes.js +187 -0
  45. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +16 -0
  46. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -0
  47. package/src/engine/physics/narrowphase/refine_ray_concave.js +145 -0
  48. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +39 -0
  49. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -0
  50. package/src/engine/physics/narrowphase/refine_ray_hit.js +78 -0
  51. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts +8 -7
  52. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts.map +1 -1
  53. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +8 -7
  54. package/src/engine/physics/queries/raycast.d.ts +11 -9
  55. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  56. package/src/engine/physics/queries/raycast.js +108 -159
  57. package/src/engine/physics/vehicle/RaycastVehicle.d.ts +114 -0
  58. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -0
  59. package/src/engine/physics/vehicle/RaycastVehicle.js +333 -0
@@ -1,186 +1,345 @@
1
- import { Matrix4, Vector2 } from 'three';
2
-
3
- /**
4
- * TODO
5
- */
6
-
7
- const SAOShader = {
8
- defines: {
9
- 'NUM_SAMPLES': 7,
10
- 'NUM_RINGS': 4,
11
- 'NORMAL_TEXTURE': 0,
12
- 'DIFFUSE_TEXTURE': 0,
13
- 'DEPTH_PACKING': 1,
14
- 'PERSPECTIVE_CAMERA': 1
15
- },
16
- uniforms: {
17
-
18
- 'tDepth': { value: null },
19
- 'tNormal': { value: null },
20
- 'size': { value: new Vector2(512, 512) },
21
-
22
- 'cameraNear': { value: 1 },
23
- 'cameraFar': { value: 100 },
24
- 'cameraProjectionMatrix': { value: new Matrix4() },
25
- 'cameraInverseProjectionMatrix': { value: new Matrix4() },
26
-
27
- 'intensity': { value: 0.1 },
28
- 'bias': { value: 0.5 },
29
-
30
- 'minResolution': { value: 0.0 },
31
- 'kernelRadius': { value: 100.0 },
32
- 'randomSeed': { value: 0.0 }
33
- },
34
- vertexShader: /* glsl */`
35
-
36
- varying vec2 vUv;
37
-
38
- void main() {
39
- vUv = uv;
40
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
41
- }`,
42
-
43
- fragmentShader: /* glsl */`
44
-
45
- #include <common>
46
-
47
- varying vec2 vUv;
48
-
49
- uniform sampler2D tDepth;
50
-
51
- #if NORMAL_TEXTURE == 1
52
- uniform sampler2D tNormal;
53
- #endif
54
-
55
- uniform float cameraNear;
56
- uniform float cameraFar;
57
- uniform mat4 cameraProjectionMatrix;
58
- uniform mat4 cameraInverseProjectionMatrix;
59
-
60
- uniform float intensity;
61
- uniform float bias;
62
- uniform float kernelRadius;
63
- uniform float minResolution;
64
- uniform vec2 size;
65
- uniform float randomSeed;
66
-
67
- // RGBA depth
68
-
69
- #include <packing>
70
-
71
- out float out_occlusion;
72
-
73
- float getDepth( const in vec2 screenPosition ) {
74
- #if DEPTH_PACKING == 1
75
- return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );
76
- #else
77
- return texture2D( tDepth, screenPosition ).x;
78
- #endif
79
- }
80
-
81
- float getViewZ( const in float depth ) {
82
- #if PERSPECTIVE_CAMERA == 1
83
- return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );
84
- #else
85
- return orthographicDepthToViewZ( depth, cameraNear, cameraFar );
86
- #endif
87
- }
88
-
89
- vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) {
90
- float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3];
91
- vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 );
92
- clipPosition *= clipW; // unprojection.
93
-
94
- return ( cameraInverseProjectionMatrix * clipPosition ).xyz;
95
- }
96
-
97
- vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition ) {
98
- #if NORMAL_TEXTURE == 1
99
- return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz );
100
- #else
101
- return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) );
102
- #endif
103
- }
104
-
105
- // Non-sin based hash function, fast and has good randomness
106
- float hash12(vec2 p){
107
- vec3 p3 = fract(vec3(p.xyx) * .1031);
108
- p3 += dot(p3, p3.yzx + 33.33);
109
- return fract((p3.x + p3.y) * p3.z);
110
- }
111
-
112
- float minResolutionMultipliedByCameraFar;
113
-
114
- float getOcclusion( const in vec3 centerViewPosition, const in vec3 centerViewNormal, const in vec3 sampleViewPosition ) {
115
- vec3 viewDelta = sampleViewPosition - centerViewPosition;
116
-
117
- float viewDistance = length( viewDelta );
118
-
119
- float vn = dot( centerViewNormal, viewDelta );
120
- float a2 = (vn - minResolutionMultipliedByCameraFar) / viewDistance - bias;
121
- float a1 = (1.0 + pow2( viewDistance ) );
122
-
123
- return max(0.0, a2) / a1;
124
- }
125
-
126
- // moving costly divides into consts
127
- const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );
128
- const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );
129
-
130
- float getAmbientOcclusion( const in vec3 centerViewPosition ) {
131
- // precompute some variables require in getOcclusion.
132
- minResolutionMultipliedByCameraFar = minResolution * cameraFar;
133
- vec3 centerViewNormal = getViewNormal( centerViewPosition, vUv );
134
-
135
- // jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/
136
- float angle = hash12( vUv + randomSeed ) * PI2;
137
- vec2 radius = vec2( kernelRadius * INV_NUM_SAMPLES ) / size;
138
-
139
- float occlusionSum = 0.0;
140
- float weightSum = 0.0;
141
-
142
- for( int i = 0; i < NUM_SAMPLES; i ++ ) {
143
- vec2 sampleUv = vUv + vec2( cos( angle ), sin( angle ) ) * radius * float(i+1);
144
-
145
- angle += ANGLE_STEP;
146
-
147
- float sampleDepth = getDepth( sampleUv );
148
-
149
- if( sampleDepth >= ( 1.0 - EPSILON ) ) {
150
- // skip, too far
151
- continue;
152
- }
153
-
154
- float sampleViewZ = getViewZ( sampleDepth );
155
-
156
- vec3 sampleViewPosition = getViewPosition( sampleUv, sampleDepth, sampleViewZ );
157
-
158
- occlusionSum += getOcclusion( centerViewPosition, centerViewNormal, sampleViewPosition );
159
-
160
- weightSum += 1.0;
161
- }
162
-
163
- if( weightSum == 0.0 ) discard;
164
-
165
- return occlusionSum * ( intensity / weightSum );
166
- }
167
-
168
- void main() {
169
- float centerDepth = getDepth( vUv );
170
- if( centerDepth >= ( 1.0 - EPSILON ) ) {
171
- discard;
172
- }
173
-
174
- float centerViewZ = getViewZ( centerDepth );
175
- vec3 viewPosition = getViewPosition( vUv, centerDepth, centerViewZ );
176
-
177
- float ambientOcclusion = getAmbientOcclusion( viewPosition );
178
-
179
- float invOcclusion = 1.0 - ambientOcclusion;
180
-
181
- out_occlusion = invOcclusion;
182
- }`
183
-
184
- };
185
-
186
- export { SAOShader };
1
+ import { Matrix4, Vector2 } from 'three';
2
+
3
+ /**
4
+ * Point-sampled screen-space volumetric obscurance.
5
+ *
6
+ * A hemisphere of radius `kernelRadius` is sampled in the volume above the surface (oriented by the
7
+ * surface normal, lifted slightly off it to avoid self-occlusion). Each sample is counted as
8
+ * occluded when it lies behind the depth buffer *and* the surface it tapped is itself within
9
+ * `kernelRadius` (so distant geometry seen through the depth buffer does not occlude). The
10
+ * occluded fraction is a physically plausible obscurance estimate, so `intensity = 1.0` produces a
11
+ * correct-looking result with no scene-dependent multiplier; higher values are an artistic
12
+ * darkening control.
13
+ *
14
+ * Sample placement uses a fixed R3 low-discrepancy kernel rotated per pixel by an R2 blue-noise
15
+ * dither (interleaved sampling). The blue-noise rotation pushes the sampling error into high
16
+ * frequencies that the edge-aware upscale downstream removes cleanly, leaving little residual.
17
+ */
18
+
19
+ const SAOShader = {
20
+ defines: {
21
+ 'NUM_SAMPLES': 7,
22
+ 'NORMAL_TEXTURE': 0,
23
+ 'DEPTH_PACKING': 1,
24
+ 'PERSPECTIVE_CAMERA': 1
25
+ },
26
+ uniforms: {
27
+
28
+ 'tDepth': { value: null },
29
+ 'tNormal': { value: null },
30
+ 'tHilbert': { value: null },
31
+ 'size': { value: new Vector2(512, 512) },
32
+
33
+ 'cameraNear': { value: 1 },
34
+ 'cameraFar': { value: 100 },
35
+ 'cameraProjectionMatrix': { value: new Matrix4() },
36
+ 'cameraInverseProjectionMatrix': { value: new Matrix4() },
37
+
38
+ // artistic darkening control; 1.0 == physically plausible
39
+ 'intensity': { value: 1.0 },
40
+
41
+ // radius of the sampling hemisphere, in world/view-space units; also the maximum distance at
42
+ // which a tapped surface counts as an occluder
43
+ 'kernelRadius': { value: 0.5 }
44
+ },
45
+ vertexShader: /* glsl */`
46
+
47
+ varying vec2 vUv;
48
+
49
+ void main() {
50
+ vUv = uv;
51
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
52
+ }`,
53
+
54
+ fragmentShader: /* glsl */`
55
+
56
+ #include <common>
57
+
58
+ varying vec2 vUv;
59
+
60
+ uniform sampler2D tDepth;
61
+
62
+ #if NORMAL_TEXTURE == 1
63
+ uniform sampler2D tNormal;
64
+ #endif
65
+
66
+ uniform highp usampler2D tHilbert;
67
+
68
+ uniform float cameraNear;
69
+ uniform float cameraFar;
70
+ uniform mat4 cameraProjectionMatrix;
71
+ uniform mat4 cameraInverseProjectionMatrix;
72
+
73
+ uniform float intensity;
74
+ uniform float kernelRadius;
75
+ uniform vec2 size;
76
+
77
+ // RGBA depth
78
+
79
+ #include <packing>
80
+
81
+ out float out_occlusion;
82
+
83
+ float getDepth( const in vec2 screenPosition ) {
84
+ #if DEPTH_PACKING == 1
85
+ return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );
86
+ #else
87
+ return texture2D( tDepth, screenPosition ).x;
88
+ #endif
89
+ }
90
+
91
+ float getViewZ( const in float depth ) {
92
+ #if PERSPECTIVE_CAMERA == 1
93
+ return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );
94
+ #else
95
+ return orthographicDepthToViewZ( depth, cameraNear, cameraFar );
96
+ #endif
97
+ }
98
+
99
+ vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) {
100
+ float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3];
101
+ vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 );
102
+ clipPosition *= clipW; // unprojection.
103
+
104
+ return ( cameraInverseProjectionMatrix * clipPosition ).xyz;
105
+ }
106
+
107
+ vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition ) {
108
+ #if NORMAL_TEXTURE == 1
109
+ return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz );
110
+ #else
111
+ // Accurate normal reconstruction (Drobot): for each axis pick the nearer-in-depth
112
+ // neighbour, so the tangent basis never straddles a silhouette. dFdx-based normals read
113
+ // garbage across depth discontinuities, which mis-orients the sampling hemisphere and
114
+ // makes flat, camera-facing surfaces self-occlude along their edges.
115
+ vec2 texel = 1.0 / size;
116
+
117
+ float dc = getDepth( screenPosition );
118
+ float dl = getDepth( screenPosition - vec2( texel.x, 0.0 ) );
119
+ float dr = getDepth( screenPosition + vec2( texel.x, 0.0 ) );
120
+ float dd = getDepth( screenPosition - vec2( 0.0, texel.y ) );
121
+ float du = getDepth( screenPosition + vec2( 0.0, texel.y ) );
122
+
123
+ vec3 dpdx;
124
+ if ( abs( dl - dc ) < abs( dr - dc ) ) {
125
+ vec3 left = getViewPosition( screenPosition - vec2( texel.x, 0.0 ), dl, getViewZ( dl ) );
126
+ dpdx = viewPosition - left;
127
+ } else {
128
+ vec3 right = getViewPosition( screenPosition + vec2( texel.x, 0.0 ), dr, getViewZ( dr ) );
129
+ dpdx = right - viewPosition;
130
+ }
131
+
132
+ vec3 dpdy;
133
+ if ( abs( dd - dc ) < abs( du - dc ) ) {
134
+ vec3 down = getViewPosition( screenPosition - vec2( 0.0, texel.y ), dd, getViewZ( dd ) );
135
+ dpdy = viewPosition - down;
136
+ } else {
137
+ vec3 up = getViewPosition( screenPosition + vec2( 0.0, texel.y ), du, getViewZ( du ) );
138
+ dpdy = up - viewPosition;
139
+ }
140
+
141
+ return normalize( cross( dpdx, dpdy ) );
142
+ #endif
143
+ }
144
+
145
+ // Marty's R2 low-discrepancy sequence: maps a permutation index to a 2D blue-noise-like point.
146
+ // (GLSL ES has no fma; the expanded multiply-add is equivalent here.)
147
+ vec2 r2_marty( uint index ) {
148
+ return fract( vec2( float( index ) ) * vec2( 0.245122333753, 0.430159709002 ) + 0.5 );
149
+ }
150
+
151
+ // Concentric disk mapping (Shirley & Chiu): low-distortion uniform disk sample from [0,1)^2.
152
+ vec2 sampleUniformDiskConcentric( const in vec2 u ) {
153
+ vec2 offset = 2.0 * u - 1.0;
154
+
155
+ if ( offset.x == 0.0 && offset.y == 0.0 ) return vec2( 0.0 );
156
+
157
+ float r;
158
+ float theta;
159
+
160
+ if ( abs( offset.x ) > abs( offset.y ) ) {
161
+ r = offset.x;
162
+ theta = ( PI * 0.25 ) * ( offset.y / offset.x );
163
+ } else {
164
+ r = offset.y;
165
+ theta = ( PI * 0.5 ) - ( PI * 0.25 ) * ( offset.x / offset.y );
166
+ }
167
+
168
+ return r * vec2( cos( theta ), sin( theta ) );
169
+ }
170
+
171
+ // Cosine-weighted hemisphere direction in tangent space (z is up), via Malley's method.
172
+ vec3 sampleCosineWeightedHemisphere( const in vec2 u ) {
173
+ vec2 d = sampleUniformDiskConcentric( u );
174
+ float z = sqrt( max( 0.0, 1.0 - dot( d, d ) ) );
175
+ return vec3( d.x, d.y, z );
176
+ }
177
+
178
+ // Branchless orthonormal basis from a unit normal (Duff et al. 2017). Columns are T, B, N, so
179
+ // basis * v maps a tangent-space vector (z up) into the normal's frame.
180
+ mat3 buildOrthonormalBasis( const in vec3 n ) {
181
+ vec3 T;
182
+ vec3 B;
183
+
184
+ if ( n.z < 0.0 ) {
185
+ float a = 1.0 / ( 1.0 - n.z );
186
+ float b = n.x * n.y * a;
187
+ T = vec3( 1.0 - n.x * n.x * a, -b, n.x );
188
+ B = vec3( b, n.y * n.y * a - 1.0, -n.y );
189
+ } else {
190
+ float a = 1.0 / ( 1.0 + n.z );
191
+ float b = -n.x * n.y * a;
192
+ T = vec3( 1.0 - n.x * n.x * a, b, -n.x );
193
+ B = vec3( b, 1.0 - n.y * n.y * a, -n.y );
194
+ }
195
+
196
+ return mat3( T, B, n );
197
+ }
198
+
199
+ // Roberts' R3 low-discrepancy additive recurrence: ( 1/g, 1/g^2, 1/g^3 ), g^4 = g + 1
200
+ const vec3 R3 = vec3( 0.8191725134, 0.6710436067, 0.5497004779 );
201
+
202
+ const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );
203
+
204
+ // normal-bias as a fraction of kernelRadius: lifts the sample origin off the surface to avoid
205
+ // self-occlusion. Larger removes acne but lets near-surface contact occlusion leak.
206
+ const float SURFACE_BIAS = 0.05;
207
+
208
+ // push each sample this many pixels further along its screen-space direction so it never
209
+ // reads the centre pixel (self-occlusion); folded into the offset instead of a hard reject
210
+ const float SELF_OCCLUSION_MARGIN_PIXELS = 2.0;
211
+
212
+ // radial distribution bias: normalized radius -> pow( radius, power ) pulls samples toward
213
+ // the origin, where occlusion contributes most (small crevices over large open surfaces)
214
+ const float sampleDistributionPower = 2.0;
215
+
216
+ // distant samples contribute less (less reliable / more likely to be noise): fraction of the
217
+ // radius over which an occluder's contribution fades to zero at the edge. Must be in [0, 1]. (GTAO)
218
+ const float GTAO_EFFECT_FALLOFF_RANGE = 0.615;
219
+
220
+ float getOccludedFraction( const in vec3 centerViewPosition, const in vec2 centerUv ) {
221
+ vec3 centerViewNormal = getViewNormal( centerViewPosition, centerUv );
222
+
223
+ // sample origin lifted slightly off the surface along the normal (see SURFACE_BIAS)
224
+ vec3 sampleOrigin = centerViewPosition + centerViewNormal * ( kernelRadius * SURFACE_BIAS );
225
+
226
+ // tangent->view basis, built once and reused for every sample
227
+ mat3 tbn = buildOrthonormalBasis( centerViewNormal );
228
+
229
+ // per-pixel 2D blue noise: a Hilbert-curve permutation index (spatially well distributed)
230
+ // fed through Marty's R2 sequence. .x rotates the kernel around the normal, .y offsets the
231
+ // sampling radius -- decorrelating neighbours so the edge-aware upscale resolves them.
232
+ uint hilbertIndex = texelFetch( tHilbert, ivec2( gl_FragCoord.xy ) & 63, 0 ).x;
233
+ vec2 noise = r2_marty( hilbertIndex );
234
+
235
+ float ca = cos( noise.x * PI2 );
236
+ float sa = sin( noise.x * PI2 );
237
+ vec3 rT = tbn[0] * ca + tbn[1] * sa;
238
+ vec3 rB = tbn[1] * ca - tbn[0] * sa;
239
+ tbn[0] = rT;
240
+ tbn[1] = rB;
241
+
242
+ // distance falloff (GTAO): full weight until falloffFrom, then linear to zero at the radius
243
+ float falloffRange = GTAO_EFFECT_FALLOFF_RANGE * kernelRadius;
244
+ float falloffFrom = kernelRadius - falloffRange;
245
+ float falloffMul = -1.0 / falloffRange;
246
+ float falloffAdd = falloffFrom / falloffRange + 1.0;
247
+
248
+ float occlusion = 0.0;
249
+
250
+ for ( int i = 0; i < NUM_SAMPLES; i ++ ) {
251
+ // fixed R3 low-discrepancy point; per-pixel variation comes from the rotated frame
252
+ // and the radius offset below
253
+ vec3 q = fract( float( i ) * R3 );
254
+
255
+ // cosine-weighted hemisphere direction, oriented to the (rotated) surface frame:
256
+ // concentrates samples near the normal where occlusion matters most
257
+ vec3 dir = normalize( tbn * sampleCosineWeightedHemisphere( q.xy ) );
258
+
259
+ // normalized radius with a per-pixel offset, biased toward the origin (see
260
+ // sampleDistributionPower) so the near occluders that dominate AO are favoured
261
+ float radius = pow( fract( q.z + noise.y ), sampleDistributionPower );
262
+
263
+ vec3 samplePositionVS = sampleOrigin + dir * ( radius * kernelRadius );
264
+
265
+ // project the sample into screen space
266
+ vec4 clip = cameraProjectionMatrix * vec4( samplePositionVS, 1.0 );
267
+ vec2 sampleUv = ( clip.xy / clip.w ) * 0.5 + 0.5;
268
+
269
+ // screen-space offset from the centre, in pixels
270
+ vec2 offsetPixels = ( sampleUv - centerUv ) * size;
271
+
272
+ // push forward along the screen direction by the self-occlusion margin (extends the
273
+ // screen-space radius), then snap to the nearest pixel centre so the depth fetch is
274
+ // exact and neighbouring pixels stay coherent (GTAO)
275
+ float offsetLength = length( offsetPixels );
276
+ offsetPixels += offsetPixels / max( offsetLength, EPSILON ) * SELF_OCCLUSION_MARGIN_PIXELS;
277
+ offsetPixels = round( offsetPixels );
278
+
279
+ sampleUv = centerUv + offsetPixels / size;
280
+
281
+ // samples outside the screen have no depth to test against
282
+ if ( sampleUv.x < 0.0 || sampleUv.x > 1.0 || sampleUv.y < 0.0 || sampleUv.y > 1.0 ) {
283
+ continue;
284
+ }
285
+
286
+ float sampleDepth = getDepth( sampleUv );
287
+
288
+ // skip the sky / far plane
289
+ if ( sampleDepth >= ( 1.0 - EPSILON ) ) {
290
+ continue;
291
+ }
292
+
293
+ float sceneViewZ = getViewZ( sampleDepth );
294
+
295
+ // the tapped surface must lie in front of (closer than) the probe to occlude it; if the
296
+ // probe is in front of the surface it sits in open space and nothing is occluded
297
+ // (view Z grows more negative away from the camera)
298
+ if ( samplePositionVS.z >= sceneViewZ ) {
299
+ continue;
300
+ }
301
+
302
+ // ...and the tapped surface must actually be within the sampling radius, otherwise we
303
+ // have hit distant geometry through the depth buffer that does not occlude this point
304
+ vec3 occluderVS = getViewPosition( sampleUv, sampleDepth, sceneViewZ );
305
+ float sampleDist = distance( centerViewPosition, occluderVS );
306
+ if ( sampleDist > kernelRadius ) {
307
+ continue;
308
+ }
309
+
310
+ // distant samples contribute less: linear fade from full weight at falloffFrom to zero
311
+ // at the radius (GTAO)
312
+ occlusion += saturate( sampleDist * falloffMul + falloffAdd );
313
+ }
314
+
315
+ return occlusion * INV_NUM_SAMPLES;
316
+ }
317
+
318
+ void main() {
319
+ // snap to the centre of the full-res depth texel we sample. The half-res pass otherwise
320
+ // lands on texel boundaries, where NEAREST biases consistently to one side and the
321
+ // reconstructed position disagrees with the sampled depth -> asymmetric edge self-occlusion.
322
+ vec2 centerUv = ( floor( vUv * size ) + 0.5 ) / size;
323
+
324
+ float centerDepth = getDepth( centerUv );
325
+ if( centerDepth >= ( 1.0 - EPSILON ) ) {
326
+ discard;
327
+ }
328
+
329
+ float centerViewZ = getViewZ( centerDepth );
330
+ vec3 viewPosition = getViewPosition( centerUv, centerDepth, centerViewZ );
331
+
332
+ float occlusion = getOccludedFraction( viewPosition, centerUv );
333
+
334
+ // intensity is an artistic power: 1.0 leaves the physical estimate untouched, higher
335
+ // darkens occluded areas while leaving fully-lit areas at 1.0
336
+ float visibility = pow( 1.0 - occlusion, intensity );
337
+
338
+ // never fully occlude: if the receiver can see a sample at all it is not truly black, and
339
+ // the R8 target cannot represent anything below 1/255 anyway
340
+ out_occlusion = max( 1.0 / 255.0, visibility );
341
+ }`
342
+
343
+ };
344
+
345
+ export { SAOShader };
@@ -0,0 +1,44 @@
1
+ export namespace SAOUpscaleShader {
2
+ namespace defines {
3
+ let NUM_TAPS: number;
4
+ let DEPTH_PACKING: number;
5
+ let PERSPECTIVE_CAMERA: number;
6
+ }
7
+ namespace uniforms {
8
+ namespace tAO {
9
+ let value: any;
10
+ }
11
+ namespace tDepth {
12
+ let value_1: any;
13
+ export { value_1 as value };
14
+ }
15
+ namespace tHilbert {
16
+ let value_2: any;
17
+ export { value_2 as value };
18
+ }
19
+ namespace size {
20
+ let value_3: Vector2;
21
+ export { value_3 as value };
22
+ }
23
+ namespace cameraNear {
24
+ let value_4: number;
25
+ export { value_4 as value };
26
+ }
27
+ namespace cameraFar {
28
+ let value_5: number;
29
+ export { value_5 as value };
30
+ }
31
+ namespace blurRadius {
32
+ let value_6: number;
33
+ export { value_6 as value };
34
+ }
35
+ namespace depthSigma {
36
+ let value_7: number;
37
+ export { value_7 as value };
38
+ }
39
+ }
40
+ let vertexShader: string;
41
+ let fragmentShader: string;
42
+ }
43
+ import { Vector2 } from 'three';
44
+ //# sourceMappingURL=SAOUpscaleShader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SAOUpscaleShader.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAAwB,OAAO"}