Skip to content

Commit 7b721a8

Browse files
feat: Add Simplex Noise Filter (#482)
1 parent c70cb94 commit 7b721a8

File tree

10 files changed

+299
-0
lines changed

10 files changed

+299
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ If all else failes, you can manually download the bundled file from the [release
7070
| **RGBSplitFilter**<br>_pixi-filters/rgb-split_<br>[View demo][RGBSplit_demo] | ![rgb split](https://filters.pixijs.download/main/screenshots/rgb.png?v=3) | |
7171
| **ShockwaveFilter**<br>_pixi-filters/shockwave_<br>[View demo][Shockwave_demo] | ![shockwave](https://filters.pixijs.download/main/screenshots/shockwave.gif?v=3) |
7272
| **SimpleLightmapFilter**<br>_pixi-filters/simple-lightmap_<br>[View demo][SimpleLightmap_demo] | ![simple-lightmap](https://filters.pixijs.download/main/screenshots/simple-lightmap.png?v=3) |
73+
| **SimplexNoiseFilter**<br>_pixi-filters/simplex-noise_<br>[View demo][SimplexNoise_demo] | ![simplex-noise](https://filters.pixijs.download/main/screenshots/simplex-noise.png?v=3) |
7374
| **TiltShiftFilter**<br>_pixi-filters/tilt-shift_<br>[View demo][TiltShift_demo] | ![tilt-shift](https://filters.pixijs.download/main/screenshots/tilt-shift.png?v=3) |
7475
| **TwistFilter**<br>_pixi-filters/twist_<br>[View demo][Twist_demo] | ![twist](https://filters.pixijs.download/main/screenshots/twist.png?v=3) |
7576
| **ZoomBlurFilter**<br>_pixi-filters/zoom-blur_<br>[View demo][ZoomBlur_demo] | ![zoom-blur](https://filters.pixijs.download/main/screenshots/zoom-blur.png?v=4) |
@@ -152,6 +153,7 @@ API documention can be found [here](http://pixijs.io/filters/docs/).
152153
[RGBSplit_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=RGBSplitFilter
153154
[Shockwave_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=ShockwaveFilter
154155
[SimpleLightmap_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=SimpleLightmapFilter
156+
[SimplexNoise_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=SimplexNoiseFilter
155157
[TiltShift_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=TiltShiftFilter
156158
[Twist_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=TwistFilter
157159
[ZoomBlur_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=ZoomBlurFilter

examples/src/filters/index.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export { default as reflection } from './reflection.mjs';
3838
export { default as rgb } from './rgb.mjs';
3939
export { default as shockwave } from './shockwave.mjs';
4040
export { default as simpleLightmap } from './lightmap.mjs';
41+
export { default as simplexNoise } from './simplex-noise.mjs';
4142
export { default as tiltShift } from './tilt-shift.mjs';
4243
export { default as twist } from './twist.mjs';
4344
export { default as zoomBlur } from './zoom-blur.mjs';
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export default function ()
2+
{
3+
const app = this;
4+
5+
app.addFilter('SimplexNoiseFilter', {
6+
oncreate(folder)
7+
{
8+
folder.add(this, 'strength', 0, 1).name('strength');
9+
folder.add(this, 'noiseScale', 0, 50).name('noise scale');
10+
folder.add(this, 'offsetX', 0, 5).name('offsetX');
11+
folder.add(this, 'offsetY', 0, 5).name('offsetY');
12+
folder.add(this, 'offsetZ', 0, 5).name('offsetZ');
13+
folder.add(this, 'step', -1, 1).name('step');
14+
},
15+
});
16+
}

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@
168168
"require": "./lib/simple-lightmap/index.js",
169169
"types": "./lib/simple-lightmap/index.d.ts"
170170
},
171+
"./simplex-noise": {
172+
"import": "./lib/simplex-noise/index.mjs",
173+
"require": "./lib/simplex-noise/index.js",
174+
"types": "./lib/simplex-noise/index.d.ts"
175+
},
171176
"./tilt-shift": {
172177
"import": "./lib/tilt-shift/index.mjs",
173178
"require": "./lib/tilt-shift/index.js",

scripts/screenshots/config.json

+8
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,14 @@
585585
"arguments": {
586586
"strength": 4
587587
}
588+
},
589+
{
590+
"name": "SimplexNoiseFilter",
591+
"filename": "simplex-noise",
592+
"arguments": {
593+
"strength": 0.5,
594+
"noiseScale": 10
595+
}
588596
}
589597
]
590598
}

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export * from './reflection';
3232
export * from './rgb-split';
3333
export * from './shockwave';
3434
export * from './simple-lightmap';
35+
export * from './simplex-noise';
3536
export * from './tilt-shift';
3637
export * from './twist';
3738
export * from './zoom-blur';
+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { Filter, GlProgram, GpuProgram } from 'pixi.js';
2+
import { vertex, wgslVertex } from '../defaults';
3+
import fragment from './simplex.frag';
4+
import source from './simplex.wgsl';
5+
6+
/** Options for the SimplexNoiseFilter constructor. */
7+
export interface SimplexNoiseFilterOptions
8+
{
9+
/**
10+
* Noise map strength.
11+
* @default 0.5
12+
*/
13+
strength?: number;
14+
/**
15+
* Noise map scale.
16+
* @default 10.0
17+
*/
18+
noiseScale?: number;
19+
/**
20+
* Horizontal offset for the noise map.
21+
* @default 0
22+
*/
23+
offsetX?: number;
24+
/**
25+
* Vertical offset for the noise map.
26+
* @default 0
27+
*/
28+
offsetY?: number;
29+
/**
30+
* Depth offset for the noise map.
31+
* @default 0
32+
*/
33+
offsetZ?: number;
34+
/**
35+
* The threshold used with the step function to create a blocky effect in the noise pattern.
36+
* When this is greater than 0, the step function is used to compare the noise value to this threshold.
37+
* @default -1
38+
*/
39+
step?: number;
40+
}
41+
42+
/**
43+
* The SimplexNoiseFilter multiplies simplex noise with the current texture data. <br>
44+
* ![original](../screenshots/original.png)![filter](../screenshots/simplex-noise.png)
45+
* @class
46+
* @extends Filter
47+
* @see {@link https://www.npmjs.com/package/pixi-filters|pixi-filters}
48+
*/
49+
export class SimplexNoiseFilter extends Filter
50+
{
51+
/** Default constructor options. */
52+
public static readonly defaults: SimplexNoiseFilterOptions = {
53+
strength: 0.5,
54+
noiseScale: 10.0,
55+
offsetX: 0,
56+
offsetY: 0,
57+
offsetZ: 0,
58+
step: -1,
59+
};
60+
61+
/**
62+
* @param options - Options for the SimplexNoise constructor.
63+
*/
64+
constructor(options?: SimplexNoiseFilterOptions)
65+
{
66+
options = { ...SimplexNoiseFilter.defaults, ...options };
67+
68+
const gpuProgram = GpuProgram.from({
69+
vertex: {
70+
source: wgslVertex,
71+
entryPoint: 'mainVertex',
72+
},
73+
fragment: {
74+
source,
75+
entryPoint: 'mainFragment',
76+
},
77+
});
78+
79+
const glProgram = GlProgram.from({
80+
vertex,
81+
fragment,
82+
name: 'simplex-filter',
83+
});
84+
85+
super({
86+
gpuProgram,
87+
glProgram,
88+
resources: {
89+
simplexUniforms: {
90+
uStrength: { value: options?.strength ?? 0, type: 'f32' },
91+
uNoiseScale: { value: options?.noiseScale ?? 0, type: 'f32' },
92+
uOffsetX: { value: options?.offsetX ?? 0, type: 'f32' },
93+
uOffsetY: { value: options?.offsetY ?? 0, type: 'f32' },
94+
uOffsetZ: { value: options?.offsetZ ?? 0, type: 'f32' },
95+
uStep: { value: options?.step ?? 0, type: 'f32' },
96+
}
97+
}
98+
});
99+
}
100+
101+
/**
102+
* Strength of the noise (color = (noiseMap + strength) * texture)
103+
* @default 0.5
104+
*/
105+
get strength(): number { return this.resources.simplexUniforms.uniforms.uStrength; }
106+
set strength(value: number) { this.resources.simplexUniforms.uniforms.uStrength = value; }
107+
108+
/**
109+
* Noise map scale.
110+
* @default 10
111+
*/
112+
get noiseScale(): number { return this.resources.simplexUniforms.uniforms.uNoiseScale; }
113+
set noiseScale(value: number) { this.resources.simplexUniforms.uniforms.uNoiseScale = value; }
114+
115+
/**
116+
* Horizontal offset for the noise map.
117+
* @default 0
118+
*/
119+
get offsetX(): number { return this.resources.simplexUniforms.uniforms.uOffsetX; }
120+
set offsetX(value: number) { this.resources.simplexUniforms.uniforms.uOffsetX = value; }
121+
122+
/**
123+
* Vertical offset for the noise map.
124+
* @default 0
125+
*/
126+
get offsetY(): number { return this.resources.simplexUniforms.uniforms.uOffsetY; }
127+
set offsetY(value: number) { this.resources.simplexUniforms.uniforms.uOffsetY = value; }
128+
129+
/**
130+
* Depth offset for the noise map.
131+
* @default 0
132+
*/
133+
get offsetZ(): number { return this.resources.simplexUniforms.uniforms.uOffsetZ; }
134+
set offsetZ(value: number) { this.resources.simplexUniforms.uniforms.uOffsetZ = value; }
135+
136+
/**
137+
* The threshold used with the step function to create a blocky effect in the noise pattern.
138+
* When this is greater than 0, the step function is used to compare the noise value to this threshold.
139+
* @default -1
140+
*/
141+
get step(): number { return this.resources.simplexUniforms.uniforms.uStep; }
142+
set step(value: number) { this.resources.simplexUniforms.uniforms.uStep = value; }
143+
}

src/simplex-noise/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './SimplexNoiseFilter';

src/simplex-noise/simplex.frag

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
precision highp float;
2+
in vec2 vTextureCoord;
3+
out vec4 finalColor;
4+
5+
uniform sampler2D uTexture;
6+
uniform float uStrength;
7+
uniform float uNoiseScale;
8+
uniform float uOffsetX;
9+
uniform float uOffsetY;
10+
uniform float uOffsetZ;
11+
uniform float uStep;
12+
13+
uniform vec4 uInputSize;
14+
uniform vec4 uInputClamp;
15+
16+
//Noise from: https://www.shadertoy.com/view/4sc3z2
17+
const vec3 MOD3 = vec3(.1031,.11369,.13787);
18+
vec3 hash33(vec3 p3)
19+
{
20+
p3 = fract(p3 * MOD3);
21+
p3 += dot(p3, p3.yxz+19.19);
22+
return -1.0 + 2.0 * fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
23+
}
24+
25+
float simplex_noise(vec3 p)
26+
{
27+
const float K1 = 0.333333333;
28+
const float K2 = 0.166666667;
29+
30+
vec3 i = floor(p + (p.x + p.y + p.z) * K1);
31+
vec3 d0 = p - (i - (i.x + i.y + i.z) * K2);
32+
33+
vec3 e = step(vec3(0.0), d0 - d0.yzx);
34+
vec3 i1 = e * (1.0 - e.zxy);
35+
vec3 i2 = 1.0 - e.zxy * (1.0 - e);
36+
37+
vec3 d1 = d0 - (i1 - 1.0 * K2);
38+
vec3 d2 = d0 - (i2 - 2.0 * K2);
39+
vec3 d3 = d0 - (1.0 - 3.0 * K2);
40+
41+
vec4 h = max(0.6 - vec4(dot(d0, d0), dot(d1, d1), dot(d2, d2), dot(d3, d3)), 0.0);
42+
vec4 n = h * h * h * h * vec4(dot(d0, hash33(i)), dot(d1, hash33(i + i1)), dot(d2, hash33(i + i2)), dot(d3, hash33(i + 1.0)));
43+
44+
return dot(vec4(31.316), n);
45+
}
46+
47+
void main(void)
48+
{
49+
float noise = simplex_noise(
50+
vec3(vTextureCoord*uNoiseScale+vec2(uOffsetX, uOffsetY), uOffsetZ)
51+
) * 0.5 + 0.5;
52+
53+
noise += 2.0 * uStrength - 1.0;
54+
noise = clamp(noise, 0.0, 1.0);
55+
56+
if (uStep > 0.0) { //step > 0.5
57+
noise = 1.0 - step(noise, uStep);
58+
}
59+
60+
finalColor = texture(uTexture, vTextureCoord) * noise;
61+
}

src/simplex-noise/simplex.wgsl

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
struct SimplexUniforms {
2+
uStrength:f32,
3+
uNoiseScale:f32,
4+
uOffsetX:f32,
5+
uOffsetY:f32,
6+
uOffsetZ:f32,
7+
uStep:f32
8+
};
9+
10+
struct GlobalFilterUniforms {
11+
uInputSize:vec4<f32>,
12+
uInputPixel:vec4<f32>,
13+
uInputClamp:vec4<f32>,
14+
uOutputFrame:vec4<f32>,
15+
uGlobalFrame:vec4<f32>,
16+
uOutputTexture:vec4<f32>,
17+
};
18+
19+
@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
20+
21+
@group(0) @binding(1) var uTexture: texture_2d<f32>;
22+
@group(0) @binding(2) var uSampler: sampler;
23+
@group(1) @binding(0) var<uniform> simplexUniforms : SimplexUniforms;
24+
25+
@fragment
26+
fn mainFragment(
27+
@location(0) uv: vec2<f32>,
28+
@builtin(position) position: vec4<f32>
29+
) -> @location(0) vec4<f32> {
30+
var noise: f32 = simplex_noise(vec3<f32>(uv * simplexUniforms.uNoiseScale + vec2<f32>(simplexUniforms.uOffsetX, simplexUniforms.uOffsetY), simplexUniforms.uOffsetZ)) * 0.5 + 0.5;
31+
noise = noise + (2. * simplexUniforms.uStrength - 1.);
32+
noise = clamp(noise, 0.0, 1.0);
33+
if (simplexUniforms.uStep > 0.0) {
34+
noise = 1. - step(noise, simplexUniforms.uStep);
35+
}
36+
return textureSample(uTexture, uSampler, uv) * noise;
37+
}
38+
39+
const MOD3: vec3<f32> = vec3<f32>(0.1031, 0.11369, 0.13787);
40+
fn hash33(p3: vec3<f32>) -> vec3<f32> {
41+
var p3_var = p3;
42+
p3_var = fract(p3_var * MOD3);
43+
p3_var = p3_var + (dot(p3_var, p3_var.yxz + 19.19));
44+
return -1. + 2. * fract(vec3<f32>((p3_var.x + p3_var.y) * p3_var.z, (p3_var.x + p3_var.z) * p3_var.y, (p3_var.y + p3_var.z) * p3_var.x));
45+
}
46+
47+
fn simplex_noise(p: vec3<f32>) -> f32 {
48+
let K1: f32 = 0.33333334;
49+
let K2: f32 = 0.16666667;
50+
let i: vec3<f32> = floor(p + (p.x + p.y + p.z) * K1);
51+
let d0: vec3<f32> = p - (i - (i.x + i.y + i.z) * K2);
52+
let e: vec3<f32> = step(vec3<f32>(0.), d0 - d0.yzx);
53+
let i1: vec3<f32> = e * (1. - e.zxy);
54+
let i2: vec3<f32> = 1. - e.zxy * (1. - e);
55+
let d1: vec3<f32> = d0 - (i1 - 1. * K2);
56+
let d2: vec3<f32> = d0 - (i2 - 2. * K2);
57+
let d3: vec3<f32> = d0 - (1. - 3. * K2);
58+
let h: vec4<f32> = max(vec4<f32>(0.6) - vec4<f32>(dot(d0, d0), dot(d1, d1), dot(d2, d2), dot(d3, d3)), vec4<f32>(0.0));
59+
let n: vec4<f32> = h * h * h * h * vec4<f32>(dot(d0, hash33(i)), dot(d1, hash33(i + i1)), dot(d2, hash33(i + i2)), dot(d3, hash33(i + 1.)));
60+
return dot(vec4<f32>(31.316), n);
61+
}

0 commit comments

Comments
 (0)