diff options
author | pommicket <pommicket@gmail.com> | 2023-08-02 22:52:45 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2023-08-02 22:52:45 -0400 |
commit | 4215b64f2694a3e4d6fc70315b2abfb37cc36496 (patch) | |
tree | 1f42a2a479d33bb0e9a5c21fd74478716d8b9d21 | |
parent | ba75c5d98896d95f238976a35526f22af77e2a46 (diff) |
better sinusoidal noise among other things
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | fractiform.js | 139 | ||||
-rw-r--r-- | index.html | 10 |
4 files changed, 124 insertions, 44 deletions
diff --git a/Makefile b/Makefile deleted file mode 100644 index ee92e58..0000000 --- a/Makefile +++ /dev/null @@ -1 +0,0 @@ -TSFLAGS=--noImplicitAny --noEmitOnError --strictNullChecks diff --git a/README.md b/README.md new file mode 100644 index 0000000..9b9896a --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +## fractiform + +online shader thingamabob + +### JS features + +i have been pretty liberal about using modern javascript; +even though this could in theory run on IE it doesn't +(in particular it is very nice to have template literals). +that said, try to only use features that have at least as much +support as webgl2 (i.e. >94%). + +no, i do not want to use a "poly-fill". + +we use webgl2 (and consequently GLSL ES 3.00) because: +- having non-constant loops in shaders is nice +- there aren't that many browsers that support webgl and ES6 but not webgl2 (looking at caniuse.com, they probably + make up around 2% of browser usage) diff --git a/fractiform.js b/fractiform.js index 2079552..a4fcb1f 100644 --- a/fractiform.js +++ b/fractiform.js @@ -1,5 +1,18 @@ 'use strict'; +/* +TODO: +- select widget name when it's created +- pause +- settings: + - enable/disable auto-update + - resolution +- widgets: + - white noise + - worley noise + - perlin noise +*/ + const APP_ID = 'fractiform'; let gl; @@ -107,7 +120,7 @@ vec3 last_frame(vec2 pos, int wrap, int sample) { pos = mod(pos, 1.0); if (sample == 1) pos = floor(0.5 + pos * ff_texture_size) * (1.0 / ff_texture_size); - return texture2D(ff_texture, pos).xyz; + return texture(ff_texture, pos).xyz; } `, ` @@ -250,7 +263,7 @@ ${type} compare(float cmp1, float cmp2, ${type} less, ${type} greater) { `, ).join('\n'), ` -//! .name: Wave +//! .name: Sine wave //! .category: curves //! .description: sine, triangle, square, sawtooth waves //! .id: sin @@ -571,65 +584,76 @@ ${type} floorf(${type} x, ${type} stepw, ${type} steph, ${type} phase) { `, ).join('\n'), ` -//! .name: Sinusoidal +//! .name: Sin noise //! .category: noise //! .description: Noise generated from sine waves - -float noise_sin(float x) { - float k = 0.5; +//! x.id: x +//! falloff.description: values closer to 0 will emphasize lower-frequency noise, values towards 1 will emphasize higher-frequency noise +//! falloff.default: 0.5 +//! freqstep.description: ratio between successive frequencies of noise +//! freqstep.default: 2 +//! levels.description: number of frequencies of noises to add together +//! levels.control: int:1|30 +//! levels.default: 8 + +float noise_sin(float x, float falloff, float freqstep, int levels) { + float k = 1.0; float phase = 2.45; + falloff = clamp(falloff, 0.0, 1.0); + float v = 0.0; - for (int i = 0; i < 8; i++) { + int i = 0; + for (i = 0; i < levels; i++) { float s = sin(x + phase); v += k * s * s; - x *= 2.0; - k *= 0.5; + x *= freqstep; + k *= falloff; phase *= 1.7; phase = mod(phase, 6.28); } - return v; + return v * (1.0 - falloff); } -float noise_sin(vec2 x) { +float noise_sin(vec2 x, float falloff, float freqstep, int levels) { float v = 0.0; - float k =0.5; + float k = 1.0; vec2 phase = vec2(1.0, 3.6); float theta = 2.7; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < levels; i++) { v += k * abs(sin(x.x + phase.x) * sin(x.y + phase.y)); phase *= 3.8; phase = mod(phase, 6.28); - x *= 2.0; + x *= freqstep; x = mat2(cos(theta), sin(theta), -sin(theta), cos(theta)) * x; - k *= 0.5; + k *= falloff; theta *= 2.4; theta = mod(theta, 6.28); } - return v; + return v * (1.0 - falloff); } -float noise_sin(vec3 x) { +float noise_sin(vec3 x, float falloff, float freqstep, int levels) { float v = 0.0; - float k = 0.5; + float k = 1.0; vec3 phase = vec3(1.0, 3.6, 2.2); float theta = 2.7; float phi = 4.6; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < levels; i++) { v += k * abs(sin(x.x + phase.x) * sin(x.y + phase.y) * sin(x.z + phase.z)); phase *= 4.7; phase = mod(phase, 6.28); - x *= 2.0; + x *= freqstep; float ct = cos(theta), st = sin(theta); float cp = cos(phi), sp = sin(phi); x = mat3(st*cp, ct*cp, -sp, st*sp, ct*sp, cp, ct, -st, 0.0) * x; - k *= 0.5; + k *= falloff; theta *= 2.4; theta = mod(theta, 6.28); } - return v; + return v * (1.0 - falloff); } -` +`, ]; function auto_update_enabled() { @@ -740,6 +764,8 @@ function control_type(control) { return 'int'; } else if (control === 'slider') { return 'float'; + } else if (control.startsWith('int:')) { + return 'int'; } return null; } @@ -1026,7 +1052,8 @@ function update_shader() { if (source === null) { return; } - const fragment_code = ` + const fragment_code = `#version 300 es + #ifdef GL_ES precision highp float; #endif @@ -1034,17 +1061,18 @@ precision highp float; uniform sampler2D ff_texture; uniform float ff_time; uniform vec2 ff_texture_size; -varying vec2 ff_pos; +in vec2 ff_pos; +out vec4 ff_out_color; ${source} void main() { - gl_FragColor = vec4(ff_get_color(), 1.0); + ff_out_color = vec4(ff_get_color(), 1.0); } `; - const vertex_code = ` -attribute vec2 v_pos; -varying vec2 ff_pos; + const vertex_code = `#version 300 es +in vec2 v_pos; +out vec2 ff_pos; void main() { ff_pos = v_pos; gl_Position = vec4(v_pos, 0.0, 1.0); @@ -1296,10 +1324,10 @@ function add_widget(func) { set_display_output_and_update_shader(root); e.preventDefault(); }); - + title.appendChild(type); title.appendChild(document.createTextNode(' ')); - + const name_input = document.createElement('div'); name_input.contentEditable = true; name_input.spellcheck = false; @@ -1367,6 +1395,21 @@ function add_widget(func) { if (param['default']) { input.value = param['default']; } + } else if (type.startsWith('int:')) { + const range = type.substring('int:'.length).split('|'); + console.assert(range.length === 2, 'bad format for int control'); + const [min, max] = range; + input = document.createElement('input'); + input.dataset.isInt = true; + input.classList.add('entry'); + input.type = 'number'; + input.min = min; + input.max = max; + input.step = 1; + input.value = Math.round((min + max) / 2); + if (param['default']) { + input.value = param['default']; + } } else { console.error('bad control type'); } @@ -1727,10 +1770,17 @@ function get_control_value(widget_id, control_id) { value: input.checked ? 1 : 0, }; } else if (input.tagName === 'INPUT') { - return { - type: 'float', - value: parseFloat(input.value), - }; + if (input.dataset.isInt) { + return { + type: 'int', + value: parseInt(input.value), + }; + } else { + return { + type: 'float', + value: parseFloat(input.value), + }; + } } else if (input.tagName === 'SELECT') { return { type: 'int', @@ -2028,10 +2078,10 @@ function startup() { import_widgets(code_input.value); }); - gl = canvas.getContext('webgl'); + gl = canvas.getContext('webgl2'); if (gl === null) { // support for very-old-but-not-ancient browsers - gl = canvas.getContext('experimental-webgl'); + gl = canvas.getContext('experimental-webgl2'); if (gl === null) { show_error('your browser doesnt support webgl.\noh well.'); return; @@ -2039,22 +2089,25 @@ function startup() { } program_post = compile_program('post', { - vertex: ` -attribute vec2 v_pos; -varying vec2 uv; + vertex: `#version 300 es +in vec2 v_pos; +out vec2 uv; + void main() { uv = v_pos * 0.5 + 0.5; gl_Position = vec4(v_pos, 0.0, 1.0); } `, - fragment: ` + fragment: `#version 300 es #ifdef GL_ES precision highp float; #endif uniform sampler2D u_texture; -varying vec2 uv; +in vec2 uv; +out vec4 color; + void main() { - gl_FragColor = texture2D(u_texture, uv); + color = texture(u_texture, uv); } `, }); @@ -126,6 +126,16 @@ max-height: 1.2em; vertical-align: middle; } + input[type="checkbox"] { + margin: 0; + } + .widget input[data-is-int="true"] { + font-size: 1em; + padding: 0; + line-height: 1; + border-width: 1px; + width: 5em; + } .widget { display: block; border-bottom: 2px solid #777; |