@woosh/meep-engine 2.33.62 → 2.33.67

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 (48) hide show
  1. package/core/FilePath.js +28 -2
  2. package/core/binary/BinaryBuffer.js +13 -0
  3. package/core/geom/2d/quad-tree/QuadTreeNode.js +3 -2
  4. package/ecs/grid/pick.js +44 -0
  5. package/editor/tools/GridPaintTool.js +1 -1
  6. package/editor/tools/paint/TerrainPaintTool.js +1 -1
  7. package/editor/view/GridPickCoordinateView.js +1 -1
  8. package/engine/asset/AssetManager.js +1 -1
  9. package/engine/asset/AssetTransformer.js +2 -1
  10. package/engine/asset/loaders/GLTFAssetLoader.js +5 -21
  11. package/engine/asset/loaders/async_traverse_three_object.js +24 -0
  12. package/engine/ecs/attachment/AttachmentSystem.js +3 -51
  13. package/engine/ecs/fow/FogOfWarRevealerSystem.js +1 -1
  14. package/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js +2 -10
  15. package/engine/ecs/grid/NormalMap2AOMap.js +10 -13
  16. package/engine/ecs/team/Team.js +82 -0
  17. package/engine/ecs/terrain/ecs/BuildLightTexture.js +106 -0
  18. package/engine/ecs/terrain/ecs/PromiseSamplerHeight.js +59 -0
  19. package/engine/ecs/terrain/ecs/Terrain.js +14 -153
  20. package/engine/ecs/tooltip/TooltipComponentSystem.js +1 -1
  21. package/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +10 -0
  22. package/engine/graphics/filter/ImageFilter.js +6 -2
  23. package/engine/graphics/geometry/AttributeSpec.d.ts +8 -0
  24. package/engine/graphics/geometry/VertexDataSpec.d.ts +15 -0
  25. package/engine/graphics/geometry/buffered/deinterleaveBufferAttribute.js +39 -0
  26. package/engine/graphics/geometry/buffered/deinterleaveBufferGeometry.js +50 -0
  27. package/engine/graphics/micron/MICRON_GEOMETRY_FIELD.js +1 -0
  28. package/engine/graphics/micron/MICRON_URI_FIELD.js +1 -0
  29. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +10 -7
  30. package/engine/graphics/micron/convert_three_object_to_micron.js +31 -5
  31. package/engine/graphics/micron/format/MicronGeometry.d.ts +4 -0
  32. package/engine/graphics/micron/format/MicronGeometry.js +6 -0
  33. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +110 -6
  34. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +4 -2
  35. package/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +1 -1
  36. package/engine/graphics/render/webgpu/sample/MeshInstance.js +21 -0
  37. package/engine/graphics/render/webgpu/sample/main.js +2 -2
  38. package/engine/graphics/shaders/AmbientOcclusionShader.js +57 -77
  39. package/engine/graphics/shaders/DenoiseShader.js +119 -0
  40. package/engine/graphics/texture/noise_octaves.js +33 -0
  41. package/engine/graphics/texture/sampler/Sampler2D2Texture.js +7 -18
  42. package/engine/graphics/texture/sampler/debug/paintSamplerOnHTML.js +8 -5
  43. package/engine/graphics/texture/sampler/sampler2DToFloat32Texture.js +10 -3
  44. package/engine/graphics/util/modelHitTest.js +63 -0
  45. package/generation/example/filters/SampleGroundMoistureFilter.js +2 -1
  46. package/generation/filtering/numeric/complex/CellFilterGaussianBlur.js +29 -13
  47. package/generation/grid/GridData.js +17 -2
  48. package/package.json +1 -1
@@ -13,6 +13,13 @@ import { AsyncRemoteHashMap } from "../../../../core/collection/map/AsyncRemoteH
13
13
  import { BufferGeometrySerializationAdapter } from "./serialization/BufferGeometrySerializationAdapter.js";
14
14
  import { MicronGeometryBinarySerializationAdapter } from "../format/serialization/MicronGeometryBinarySerializationAdapter.js";
15
15
  import { AsyncMapWrapper } from "../../../../core/collection/map/AsyncMapWrapper.js";
16
+ import { async_traverse_three_object } from "../../../asset/loaders/async_traverse_three_object.js";
17
+ import { computePathDirectory } from "../../../../core/FilePath.js";
18
+ import { BinaryBuffer } from "../../../../core/binary/BinaryBuffer.js";
19
+ import { MicronGeometry } from "../format/MicronGeometry.js";
20
+ import { ArrayBufferLoader } from "../../../asset/loaders/ArrayBufferLoader.js";
21
+ import { MICRON_URI_FIELD } from "../MICRON_URI_FIELD.js";
22
+ import { MICRON_GEOMETRY_FIELD } from "../MICRON_GEOMETRY_FIELD.js";
16
23
 
17
24
  /**
18
25
  *
@@ -20,6 +27,7 @@ import { AsyncMapWrapper } from "../../../../core/collection/map/AsyncMapWrapper
20
27
  */
21
28
  const STORAGE_KEY = '@plugin/virtual-geometry-micron/geometry-cache';
22
29
 
30
+ const ASSET_TYPE_ARRAY_BUFFER = "arraybuffer";
23
31
 
24
32
  class GLTFAssetTransformer extends AssetTransformer {
25
33
  /**
@@ -35,17 +43,100 @@ class GLTFAssetTransformer extends AssetTransformer {
35
43
  * @private
36
44
  */
37
45
  this.__cache = cache;
46
+
47
+ /**
48
+ *
49
+ * @type {AssetManager|null}
50
+ * @private
51
+ */
52
+ this.__am = null;
53
+
54
+ /**
55
+ *
56
+ * @type {MicronGeometryBinarySerializationAdapter}
57
+ * @private
58
+ */
59
+ this.__serde = new MicronGeometryBinarySerializationAdapter();
38
60
  }
39
61
 
40
- async transform(source) {
62
+ /**
63
+ *
64
+ * @param {AssetManager|null} am
65
+ */
66
+ set assetManager(am) {
67
+ this.__am = am;
68
+ }
69
+
70
+
71
+ /**
72
+ * @param {THREE.Object3D} object
73
+ * @param {{asset:AssetDescription}} ctx
74
+ * @return {Promise<void>}
75
+ * @private
76
+ */
77
+ async __visitObjectToLoadMicron(object, ctx) {
78
+ if (!object.isMesh) {
79
+ return;
80
+ }
81
+
82
+ /**
83
+ * @type {THREE.BufferGeometry}
84
+ */
85
+ const geometry = object.geometry;
86
+
87
+ const micron_uri = geometry.userData[MICRON_URI_FIELD];
88
+
89
+ if (typeof micron_uri !== "string") {
90
+ return;
91
+ }
92
+
93
+
94
+ /**
95
+ * @type {AssetDescription}
96
+ */
97
+ const ad = ctx.asset;
98
+
99
+ const base = computePathDirectory(ad.path);
100
+
101
+ const micron_data = await this.__am.promise(`${base}/${micron_uri}`, ASSET_TYPE_ARRAY_BUFFER);
102
+
103
+ // deserialize micron data
104
+ const buffer = BinaryBuffer.fromArrayBuffer(micron_data.create());
105
+ const micron_geometry = new MicronGeometry();
106
+
107
+ this.__serde.deserialize(buffer, micron_geometry);
108
+
109
+ // set proxy for raycasting
110
+ micron_geometry.proxy = geometry;
111
+
112
+ geometry[MICRON_GEOMETRY_FIELD] = micron_geometry;
113
+ }
114
+
115
+
116
+ /**
117
+ *
118
+ * @param {Asset} source
119
+ * @param asset_description
120
+ * @return {Promise<Asset>}
121
+ */
122
+ async transform(source, asset_description) {
41
123
  const animationClips = source.animations;
124
+
42
125
  if (animationClips !== undefined && animationClips.length > 0) {
43
126
  // not suitable
44
127
  return source;
45
128
  }
46
129
 
130
+ /**
131
+ * @type {THREE.Object3D}
132
+ */
47
133
  const root = source.create();
48
134
 
135
+ // attempt to load existing micron geometries
136
+ await async_traverse_three_object(root, this.__visitObjectToLoadMicron, this, {
137
+ asset: asset_description
138
+ });
139
+
49
140
  const micron_root = await convert_three_object_to_micron(root.clone(), this.__cache);
50
141
  micron_root.boundingSphere = root.boundingSphere;
51
142
 
@@ -117,7 +208,7 @@ export class MicronRenderPlugin extends EnginePlugin {
117
208
 
118
209
  /**
119
210
  *
120
- * @type {AssetTransformer}
211
+ * @type {GLTFAssetTransformer}
121
212
  * @private
122
213
  */
123
214
  this.__asset_transformer = new GLTFAssetTransformer(this.__cache);
@@ -180,8 +271,17 @@ export class MicronRenderPlugin extends EnginePlugin {
180
271
 
181
272
  const engine = this.engine;
182
273
 
183
- engine.assetManager.registerTransformer("model/gltf", this.__asset_transformer);
184
- engine.assetManager.registerTransformer("model/gltf+json", this.__asset_transformer);
274
+ const am = engine.assetManager;
275
+
276
+ // ensure that "arraybuffer" asset loader is present
277
+ if (am.getLoaderByType(ASSET_TYPE_ARRAY_BUFFER) === undefined) {
278
+ am.registerLoader(ASSET_TYPE_ARRAY_BUFFER, new ArrayBufferLoader());
279
+ }
280
+
281
+ this.__asset_transformer.assetManager = am;
282
+
283
+ am.registerTransformer("model/gltf", this.__asset_transformer);
284
+ am.registerTransformer("model/gltf+json", this.__asset_transformer);
185
285
 
186
286
  /**
187
287
  *
@@ -207,8 +307,12 @@ export class MicronRenderPlugin extends EnginePlugin {
207
307
 
208
308
  this.__persisted_geometry.detach_storage();
209
309
 
210
- engine.assetManager.unregisterTransformer("model/gltf", this.__asset_transformer);
211
- engine.assetManager.unregisterTransformer("model/gltf+json", this.__asset_transformer);
310
+ const am = engine.assetManager;
311
+
312
+ am.unregisterTransformer("model/gltf", this.__asset_transformer);
313
+ am.unregisterTransformer("model/gltf+json", this.__asset_transformer);
314
+
315
+ this.__asset_transformer.assetManager = null;
212
316
 
213
317
  const ge = engine.graphics;
214
318
 
@@ -42,7 +42,6 @@ import { RepeatBehavior } from "../../../../../../intelligence/behavior/decorato
42
42
  import { ActionBehavior } from "../../../../../../intelligence/behavior/primitive/ActionBehavior.js";
43
43
  import { pingpong } from "../../../../../../../core/math/pingpong.js";
44
44
  import { ParameterLookupTable } from "../../parameter/ParameterLookupTable.js";
45
- import { pick } from "../../../../../../../../model/game/util/ScreenGridPicker.js";
46
45
  import GUIElement from "../../../../../../ecs/gui/GUIElement.js";
47
46
  import EmptyView from "../../../../../../../view/elements/EmptyView.js";
48
47
  import HeadsUpDisplay from "../../../../../../ecs/gui/hud/HeadsUpDisplay.js";
@@ -77,6 +76,7 @@ import TopDownCameraController from "../../../../../ecs/camera/topdown/TopDownCa
77
76
  import { randomFloatBetween } from "../../../../../../../core/math/random/randomFloatBetween.js";
78
77
  import { MicronRenderPlugin } from "../../../../../micron/plugin/MicronRenderPlugin.js";
79
78
  import { obtainTerrain } from "../../../../../../ecs/terrain/util/obtainTerrain.js";
79
+ import { pick } from "../../../../../../../ecs/grid/pick.js";
80
80
 
81
81
  const engineHarness = new EngineHarness();
82
82
 
@@ -433,7 +433,9 @@ async function sample_scene_nam(engine) {
433
433
  "distanceMax": 1000
434
434
  });
435
435
 
436
- load_model('moicon/20_Dec_2021_Nammo/with_assets(for_alex)/model.gltf', ecd)
436
+ // load_model('moicon/20_Dec_2021_Nammo/with_assets(for_alex)/model.gltf', ecd)
437
+ load_model('moicon/20_Dec_2021_Nammo/with_assets_micron_conveted/model.gltf', ecd)
438
+ // load_model('moicon/micron_container/model.gltf', ecd)
437
439
 
438
440
  const P_SIZE_SMALL = 0.15;
439
441
  const P_SIZE_MEDIUM = 0.6;
@@ -80,7 +80,7 @@ import { ParticleEmitterSystem } from "../../../particles/ecs/ParticleEmitterSys
80
80
  import { ShadedGeometrySystem } from "../../../ecs/mesh-v2/ShadedGeometrySystem.js";
81
81
  import { FogOfWar } from "../../../../ecs/fow/FogOfWar.js";
82
82
  import { FogOfWarRevealer } from "../../../../ecs/fow/FogOfWarRevealer.js";
83
- import Team from "../../../../../../model/game/ecs/component/Team.js";
83
+ import Team from "../../../../ecs/team/Team.js";
84
84
  import { AmbientOcclusionPostProcessEffect } from "../../buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js";
85
85
  import { obtainTerrain } from "../../../../ecs/terrain/util/obtainTerrain.js";
86
86
 
@@ -1,5 +1,7 @@
1
1
  import { array_copy } from "../../../../../core/collection/array/copyArray.js";
2
2
 
3
+ const BIND_GROUP_MATERIAL = 1;
4
+
3
5
  export class MeshInstance {
4
6
  constructor() {
5
7
 
@@ -9,6 +11,12 @@ export class MeshInstance {
9
11
  */
10
12
  this.geometry = null;
11
13
 
14
+ /**
15
+ *
16
+ * @type {null}
17
+ */
18
+ this.material = null;
19
+
12
20
  /**
13
21
  *
14
22
  * @type {GPUBuffer|null}
@@ -75,13 +83,26 @@ export class MeshInstance {
75
83
 
76
84
  }
77
85
 
86
+ /**
87
+ *
88
+ * @param {GPURenderPassEncoder} pass
89
+ */
90
+ preparePass(pass) {
91
+
92
+ }
93
+
78
94
  /**
79
95
  *
80
96
  * @param {GPURenderPassEncoder} pass
81
97
  */
82
98
  draw(pass) {
99
+ // set material bindings
100
+
101
+ // pass.setBindGroup(BIND_GROUP_MATERIAL, {});
102
+
83
103
  pass.setVertexBuffer(0, this.vertex_buffer);
84
104
  pass.setIndexBuffer(this.index_buffer, 'uint32');
105
+
85
106
  pass.drawIndexed(this.geometry.getIndex().array.length);
86
107
  }
87
108
  }
@@ -323,7 +323,7 @@ const init = async ({ canvasRef, gui }) => {
323
323
  };
324
324
  const settings = {
325
325
  mode: 'rendering',
326
- numLights: 128,
326
+ numLights:1,
327
327
  };
328
328
  const configUniformBuffer = (() => {
329
329
  const buffer = device.createBuffer({
@@ -541,7 +541,7 @@ const init = async ({ canvasRef, gui }) => {
541
541
  gBufferPass.setPipeline(writeGBuffersPipeline);
542
542
  gBufferPass.setBindGroup(0, sceneUniformBindGroup);
543
543
 
544
- m_instance.draw(gBufferPass);
544
+ m_instance.draw(gBufferPass);
545
545
 
546
546
  gBufferPass.endPass();
547
547
  }
@@ -3,9 +3,6 @@
3
3
  */
4
4
  import { Vector2 } from "three";
5
5
 
6
-
7
- const samplesPerPixel = 37;
8
-
9
6
  const AmbientOcclusionShader = function () {
10
7
  return {
11
8
 
@@ -13,11 +10,16 @@ const AmbientOcclusionShader = function () {
13
10
 
14
11
  "normalMap": { type: "t", value: null },
15
12
  "heightMap": { type: "t", value: null },
16
- "resolution": { type: "v2", value: new Vector2(512, 512) },
13
+ "world_size": { type: "v2", value: new Vector2(512, 512) },
17
14
  "rayLength": { type: 'f', value: 17 }
18
15
 
19
16
  },
20
17
 
18
+ defines: {
19
+ 'NUM_SAMPLES': 64,
20
+ 'NUM_RINGS': 7,
21
+ },
22
+
21
23
  vertexShader: [
22
24
 
23
25
  "varying vec2 vUv;",
@@ -33,17 +35,17 @@ const AmbientOcclusionShader = function () {
33
35
 
34
36
  fragmentShader: [
35
37
  `
36
- uniform vec2 resolution;
37
38
  uniform sampler2D normalMap;
38
39
  uniform sampler2D heightMap;
39
40
  uniform float rayLength;
41
+ uniform vec2 world_size;
40
42
 
41
43
  varying vec2 vUv;
42
44
 
43
45
  vec3 get(float x, float y){
44
- vec2 _uv = vUv.xy + vec2(x,y)/resolution;
46
+ vec2 _uv = vUv.xy + vec2(x,y) / world_size;
45
47
  float h = texture2D(heightMap, _uv).x;
46
- return vec3(_uv.x, h, _uv.y);
48
+ return vec3( _uv.x * world_size.x, h, _uv.y * world_size.y );
47
49
  }
48
50
 
49
51
  float hash1( float n )
@@ -56,92 +58,70 @@ const AmbientOcclusionShader = function () {
56
58
  p = 50.0*fract( p*0.3183099 );
57
59
  return fract( p.x*p.y*(p.x+p.y) );
58
60
  }
59
-
60
61
 
61
- float noise( in vec2 x )
62
- {
63
- vec2 p = floor(x);
64
- vec2 w = fract(x);
65
- vec2 u = w*w*w*(w*(w*6.0-15.0)+10.0);
66
-
67
- float a = hash1(p+vec2(0,0));
68
- float b = hash1(p+vec2(1,0));
69
- float c = hash1(p+vec2(0,1));
70
- float d = hash1(p+vec2(1,1));
71
-
72
- return -1.0+2.0*( a + (b-a)*u.x + (c-a)*u.y + (a - b - c + d)*u.x*u.y );
73
- }
74
-
75
- float noise_01(in vec2 x){
76
- return (noise(x) + 1.0)*0.5;
62
+ // Non-sin based hash function, fast and has good randomness
63
+ float hash12(vec2 p){
64
+ vec3 p3 = fract(vec3(p.xyx) * .1031);
65
+ p3 += dot(p3, p3.yzx + 33.33);
66
+ return fract((p3.x + p3.y) * p3.z);
77
67
  }
78
68
 
79
- vec3 sampleDirectionFromConicRay(float angle, vec3 direction, vec2 seed){
80
- float random_0 = noise_01( seed + vec2(1.123, 3.1) );
81
- float random_1 = noise_01( seed + vec2(3.123, 7.8913) );
82
-
83
-
84
- float angleCosine = cos(angle);
85
-
86
- float z = random_0 * (1.0 - angleCosine) + angleCosine;
69
+ const float bias = 0.001;
87
70
 
88
- float phi = random_1 * 6.28318530718; //2*pi
71
+ float pow2(float x){
72
+ return x*x;
73
+ }
74
+
75
+ float getOcclusion(vec3 origin, vec3 normal, vec3 hit_position){
76
+ vec3 viewDelta = hit_position - origin;
89
77
 
90
- float zSqr = z*z;
78
+ float viewDistance = length( viewDelta );
91
79
 
92
- float zSqrtNeg = sqrt(1.0 - zSqr);
80
+ float vn = dot( normal, viewDelta );
81
+ float a2 = (vn) / viewDistance - bias;
82
+ float a1 = (1.0 + pow2( viewDistance ) );
93
83
 
94
- float x = zSqrtNeg * cos(phi);
95
- float y = zSqrtNeg * sin(phi);
84
+ return max(0.0, a2) / a1;
85
+ }
96
86
 
97
- float dX = direction.x;
98
- float dY = direction.y;
99
- float dZ = direction.z;
87
+ const float PI2 = 6.28318530717958;
88
+ const float kernelRadius = 100.0;
89
+
90
+ const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );
91
+ const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );
92
+
93
+ float getAmbientOcclusion(vec3 world_position, vec3 world_normal){
94
+ // jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/
95
+ float angle = hash12( vUv ) * PI2;
96
+ vec2 radius = vec2( rayLength * INV_NUM_SAMPLES );
100
97
 
101
- float rot = acos(dZ);
102
-
103
- float c = dZ;
104
- float s = sin(rot);
105
- float t = 1.0 - c;
106
- float tx = -t * dY;
107
- float ty = t * dX;
98
+ float occlusionSum = 0.0;
99
+ float weightSum = 0.0;
108
100
 
109
- float n11 = c - tx * dY;
110
- float n12 = tx * dX;
111
- float n22 = ty * dX + c;
112
- float n13 = s * dX;
113
- float n32 = -s * dY;
101
+ for( int i = 0; i < NUM_SAMPLES; i ++ ) {
102
+ vec2 sampleUv = vec2( cos( angle ), sin( angle ) ) * radius * float(i+1);
103
+
104
+ angle += ANGLE_STEP;
105
+
106
+ vec3 sample_pos = get(sampleUv.x, sampleUv.y);
114
107
 
115
- float _x = n11 * x + n12 * y + n13 * z;
116
- float _y = n12 * x + n22 * y + -n32 * z;
117
- float _z = -n13 * x + n32 * y + c * z;
108
+ occlusionSum += getOcclusion(world_position, world_normal, sample_pos);
109
+ weightSum += 1.0;
110
+ }
118
111
 
119
- return vec3(_x, _y, _z);
112
+ return occlusionSum/weightSum;
120
113
  }
121
-
114
+
122
115
  void main() {
123
- float occlusion = 0.0;
124
116
  vec3 pos = get(0.0, 0.0);
125
- vec3 normal = texture2D( normalMap, vUv ).xyz;
126
-
127
- for(int i=1; i< ${samplesPerPixel}; i++){
128
- float s = float(i)/${samplesPerPixel - 1}.0;
129
- float a = sqrt(s*512.0);
130
- float b = sqrt(s)*rayLength;
131
- float x = sin(a)*b;
132
- float y = cos(a)*b;
133
-
134
- vec3 sample_pos = get(x, y);
135
-
136
- vec3 sample_dir = normalize(sample_pos - pos);
137
- float vDot = dot(normal, sample_dir);
138
- float lambert = clamp(vDot, 0.0, 1.0);
139
- float dist_factor = 0.23/sqrt(length(sample_pos - pos));
140
- occlusion += dist_factor*lambert;
141
- }
142
117
 
143
- float incident = 1.0 - occlusion/${samplesPerPixel - 1}.0;
144
- gl_FragColor = vec4(incident, incident, incident, 1.0);
118
+ vec3 normal = texture2D( normalMap, vUv ).xzy;
119
+
120
+ float occlusion = getAmbientOcclusion(pos, normal);
121
+
122
+ float incident = 1.0 - occlusion;
123
+
124
+ gl_FragColor = vec4(pow(incident,10.0), 0.0, 0.0, 1.0);
145
125
  }
146
126
  `
147
127
  ].join('\n')
@@ -0,0 +1,119 @@
1
+ import { Vector2 } from "three";
2
+
3
+ const KERNEL_SIZE = 15;
4
+
5
+ const fragment = `
6
+ // bilateral filter, based on https://www.shadertoy.com/view/4dfGDH# and
7
+ // http://people.csail.mit.edu/sparis/bf_course/course_notes.pdf
8
+ // A bilateral filter is a non-linear, edge-preserving, and noise-reducing smoothing filter for images.
9
+ // It replaces the intensity of each pixel with a weighted average of intensity values from nearby pixels.
10
+ // This weight can be based on a Gaussian distribution. Crucially, the weights depend not only on
11
+ // Euclidean distance of pixels, but also on the radiometric differences (e.g., range differences, such
12
+ // as color intensity, depth distance, etc.). This preserves sharp edges.
13
+
14
+ float normpdf3(in vec3 v, in float sigma) {
15
+ return 0.39894 * exp(-0.5 * dot(v, v) / (sigma * sigma)) / sigma;
16
+ }
17
+
18
+ // filter size
19
+ #define MSIZE ${KERNEL_SIZE}
20
+ varying vec2 vUv;
21
+ uniform sampler2D source;
22
+ uniform vec2 pixelOffset;
23
+ uniform vec2 sigmas;
24
+ uniform float bZnorm;
25
+ uniform float kernel[MSIZE];
26
+
27
+ void main(void) {
28
+
29
+ // range sigma - controls blurriness based on a pixel distance
30
+ float sigma = sigmas.x;
31
+
32
+ // domain sigma - controls blurriness based on a pixel similarity (to preserve edges)
33
+ float bSigma = sigmas.y;
34
+ vec4 pixel = texture2D(source, vUv);
35
+ vec4 accumulatedColor = vec4(0.0);
36
+ float accumulatedFactor = 0.0;
37
+
38
+ // read out the texels
39
+ const int kSize = (MSIZE-1)/2;
40
+
41
+ for (int i = -kSize; i <= kSize; ++i) {
42
+ for (int j = -kSize; j <= kSize; ++j) {
43
+
44
+ // sample the pixel with offset
45
+ vec2 coord = vUv + vec2(float(i), float(j)) * pixelOffset;
46
+ vec4 rgba = texture2D(source, coord);
47
+
48
+ // bilateral factors
49
+ float factor = kernel[kSize + j] * kernel[kSize + i];
50
+ factor *= normpdf3(rgba.rgb - pixel.rgb, bSigma) * bZnorm;
51
+
52
+ // accumulate
53
+ accumulatedColor += factor * rgba;
54
+ accumulatedFactor += factor;
55
+ }
56
+ }
57
+
58
+ gl_FragColor = accumulatedColor / accumulatedFactor;
59
+ }
60
+ `;
61
+
62
+ /**
63
+ *
64
+ * @param {number} x
65
+ * @param {number} sigma
66
+ * @return {number}
67
+ */
68
+ function normpdf(x, sigma) {
69
+ return 0.39894 * Math.exp(-0.5 * x * x / (sigma * sigma)) / sigma;
70
+ }
71
+
72
+ /**
73
+ *
74
+ * @param {number} sigma
75
+ * @param {number} filterSmoothness
76
+ * @constructor
77
+ */
78
+ export function DenoiseShader(sigma = 10, filterSmoothness = 0.2) {
79
+ // compute zNorm
80
+ const zNorm = 1 / normpdf(0, filterSmoothness);
81
+
82
+ // build kernel
83
+ const kernel = new Float32Array(KERNEL_SIZE);
84
+ const kSize = Math.floor((KERNEL_SIZE - 1) / 2);
85
+ for (let i = 0; i <= kSize; i++) {
86
+ const v = normpdf(i, sigma);
87
+
88
+ kernel[kSize + i] = v;
89
+ kernel[kSize - i] = v;
90
+ }
91
+
92
+ return {
93
+ uniforms: {
94
+ source: {
95
+ type: 't',
96
+ value: null
97
+ },
98
+ pixelOffset: {
99
+ type: 'v2',
100
+ /**
101
+ * Holds single pixel size in UV space ( 1 / texture_size )
102
+ */
103
+ value: new Vector2(1, 1)
104
+ },
105
+ sigmas: {
106
+ type: "v2", value: new Vector2(sigma, sigma)
107
+ },
108
+ bZnorm: {
109
+ type: 'f',
110
+ value: zNorm
111
+ },
112
+ kernel: {
113
+ type: 'f',
114
+ value: kernel
115
+ }
116
+ },
117
+ fragmentShader: fragment
118
+ };
119
+ }
@@ -0,0 +1,33 @@
1
+ export function noise_octaves(
2
+ noise,
3
+ noise_ctx,
4
+ x, y, z,
5
+ octaves,
6
+ persistence = 0.5,
7
+ lacunarity = 2
8
+ ) {
9
+ let total = 0;
10
+ let frequency = 1;
11
+ let amplitude = 1;
12
+
13
+ let totalAmplitude = 0; // Used for normalizing result to 0.0 - 1.0
14
+ let i = 0;
15
+
16
+ for (; i < octaves; i++) {
17
+
18
+ const noise_value = noise.call(noise_ctx,
19
+ x * frequency,
20
+ y * frequency,
21
+ z * frequency
22
+ );
23
+
24
+ total += noise_value * amplitude;
25
+
26
+ totalAmplitude += amplitude;
27
+ amplitude *= persistence;
28
+ frequency *= lacunarity;
29
+ }
30
+
31
+ return total / totalAmplitude;
32
+ }
33
+
@@ -3,43 +3,32 @@
3
3
  */
4
4
  import { ClampToEdgeWrapping, DataTexture, RGBAFormat, UnsignedByteType } from 'three';
5
5
  import { assert } from "../../../../core/assert.js";
6
+ import { copy_Sampler2D_channel_data } from "./copy_Sampler2D_channel_data.js";
7
+ import { Sampler2D } from "./Sampler2D.js";
6
8
 
7
9
 
8
10
  /**
9
11
  *
10
12
  * @param {Sampler2D} sampler
11
- * @param {Number} scale
12
- * @param {Number} offset
13
13
  * @return {DataTexture}
14
14
  */
15
- function sampler2D2Texture(sampler, scale = 1, offset = 0) {
15
+ function sampler2D2Texture(sampler) {
16
16
  assert.notEqual(sampler, undefined, 'sampler is undefined');
17
- assert.equal(typeof scale, "number", `scale must be a number, instead was '${typeof scale}'`);
18
- assert.equal(typeof offset, "number", `scale must be a number, instead was '${typeof offset}'`);
19
17
 
20
18
  const result = new DataTexture();
21
19
  result.format = RGBAFormat;
22
20
 
23
21
  let data;
24
22
 
25
- if (scale === 1 && offset === 0 && sampler.itemSize === 4) {
23
+ if (sampler.itemSize === 4) {
26
24
  //no transformation, use a direct reference
27
25
  data = sampler.data;
28
26
 
29
27
  } else {
30
- data = new Uint8Array(sampler.width * sampler.height * 4);
31
- //slow process with mutation
32
- const arrayFiller = sampler.makeArrayFiller(scale, offset);
28
+ const dest = Sampler2D.uint8(4, sampler.width, sampler.height);
29
+ copy_Sampler2D_channel_data(sampler, dest);
33
30
 
34
- let index = 0;
35
- let x, y;
36
-
37
- for (y = 0; y < sampler.height; y++) {
38
- for (x = 0; x < sampler.width; x++) {
39
- arrayFiller(index, data, x, y);
40
- index += 4;
41
- }
42
- }
31
+ data = dest.data;
43
32
  }
44
33
 
45
34
  result.type = UnsignedByteType;