From d10f48cf09f2e64dbd8d661656124de6e5ad55ec Mon Sep 17 00:00:00 2001 From: Spax Date: Sat, 25 Nov 2023 23:36:14 -0500 Subject: [PATCH] first attempt at the scatter shader --- js/simple-shader.js | 256 +++++++++++++++++++++++++++++ tests/scatter/index.html | 22 +++ tests/scatter/scatter-frag.glsl.js | 69 ++++++++ 3 files changed, 347 insertions(+) create mode 100755 js/simple-shader.js create mode 100755 tests/scatter/index.html create mode 100755 tests/scatter/scatter-frag.glsl.js diff --git a/js/simple-shader.js b/js/simple-shader.js new file mode 100755 index 0000000..8dbc437 --- /dev/null +++ b/js/simple-shader.js @@ -0,0 +1,256 @@ +/* + simple-shader.js + written by Sudospective + + honestly do whatever you want with this + just dont sue me and leave my name there really + enjoy frens + + https://sudospective.net +*/ +const src = { +vert: `precision lowp float; +attribute vec2 position; +attribute vec2 texCoord; +uniform vec2 resolution; +varying vec2 imageCoord; +void main() { + vec2 zeroToOne = position / resolution; + vec2 zeroToTwo = zeroToOne * 2.0; + vec2 clipSpace = zeroToTwo - 1.0; + gl_Position = vec4(clipSpace * vec2(1.0, -1.0), 0.0, 1.0); + imageCoord = texCoord; +} +`, +frag: `precision lowp float; +#define PI 3.1415827 +uniform vec2 resolution; +uniform float time; +uniform sampler2D sampler0; +varying vec2 imageCoord; +void main() { + vec2 uv = gl_FragCoord.xy / resolution; + vec4 color = vec4(uv, (0.5 + sin(time - (PI * 0.5)) * 0.5), 1.0); + gl_FragColor = color; +} +`, +}; + +const isImage = function(url) { + return /\.(jpg|jpeg|png|webp|avif|gif|svg)$/.test(url); +} + +export class SimpleShader { + static defaultVertex() { + return src.vert; + }; + static defaultFragment() { + return src.frag; + }; + ready = false; + initTime = Date.now(); + time = 0.0; + uniforms = {}; + constructor(canvasId, data) { + data = data || {}; + const unis = {}; // internal + if (data && data.uniforms) { + Object.entries(data.uniforms).forEach((entry) => { + const type = entry[0]; + Object.entries(entry[1]).forEach((uniform) => { + this.uniforms[type] = uniform[1]; + }); + }); + }; + this.canvas = document.getElementById(canvasId); + if (!this.canvas) { + console.log("Unable to get canvas with ID", canvasId); + return null; + }; + this.context = this.canvas.getContext("webgl"); + if (!this.context) { + console.log("Unable to get GL context from canvas with ID", canvasId); + return null; + }; + const gl = this.context; + const vertSrc = data.vert || SimpleShader.defaultVertex(); + const fragSrc = data.frag || SimpleShader.defaultFragment(); + const vert = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vert, vertSrc); + gl.compileShader(vert); + if (!gl.getShaderParameter(vert, gl.COMPILE_STATUS)) { + console.log(gl.getShaderInfoLog(vert)); + gl.deleteShader(vert); + return null; + } + const frag = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(frag, fragSrc); + gl.compileShader(frag); + if (!gl.getShaderParameter(frag, gl.COMPILE_STATUS)) { + console.log(gl.getShaderInfoLog(frag)); + gl.deleteShader(frag); + return null; + } + const program = gl.createProgram(); + gl.attachShader(program, vert); + gl.attachShader(program, frag); + gl.linkProgram(program); + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + console.log(gl.getProgramInfoLog(program)); + return null; + }; + gl.validateProgram(program); + if (!gl.getProgramParameter(program, gl.VALIDATE_STATUS)) { + console.log(gl.getProgramInfoLog(program)); + return null; + }; + this.program = program; + const posLoc = gl.getAttribLocation(this.program, "position"); + const texLoc = gl.getAttribLocation(this.program, "texCoord"); + const posBuf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, posBuf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 0.0, 0.0, + this.canvas.width, 0.0, + 0.0, this.canvas.height, + 0.0, this.canvas.height, + this.canvas.width, 0.0, + this.canvas.width, this.canvas.height, + ]), gl.DYNAMIC_DRAW); + const texBuf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texBuf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 1.0, 1.0, + ]), gl.STATIC_DRAW); + if (data.sampler2D) { + var texId = 0; + Object.entries(data.sampler2D).forEach((sampler2D) => { + unis[sampler2D[0]] = { textureIndex: texId++ }; + const image = isImage() ? new Image() : document.createElement('video'); + image.src = sampler2D[1]; + if (!isImage()) { + image.autoplay = true; + image.playsInline = true; + image.loop = true; + } + const assignTexture = function(obj) { + const tex = gl.createTexture(); + const data = isImage() ? image : new Uint8Array([0, 255, 0, 255]); + obj.texture = tex; + return (function () { + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data); + }); + }; + image.onload = assignTexture(unis[sampler2D[0]]); + unis[sampler2D[0]].image = image; + }); + }; + gl.viewport(0, 0, this.canvas.width, this.canvas.height); + const uniformFunc = { + int: "uniform1i", + ivec2: "uniform2iv", + ivec3: "uniform3iv", + ivec4: "uniform4iv", + float: "uniform1f", + vec2: "uniform2fv", + vec3: "uniform3fv", + vec4: "uniform4fv", + }; + const prog = this.program; + const render = function(timestamp) { + if (this.ready) + this.time = (Date.now() - this.startTime); + else + this.time = 0.0; + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(prog); + gl.enableVertexAttribArray(posLoc); + gl.bindBuffer(gl.ARRAY_BUFFER, posBuf); + gl.vertexAttribPointer( + posLoc, + 2, + gl.FLOAT, + false, + 0, + 0 + ); + gl.enableVertexAttribArray(texLoc); + gl.bindBuffer(gl.ARRAY_BUFFER, texBuf); + gl.vertexAttribPointer( + texLoc, + 2, + gl.FLOAT, + false, + 0, + 0 + ); + const res = [this.canvas.width, this.canvas.height]; + const userUnis = this.uniforms; + Object.entries(data).forEach((uniformType) => { + const key = uniformType[0]; + if (uniformFunc[key]) { + Object.entries(uniformType[1]).forEach((uniform) => { + unis[uniform[0]] = uniform[1]; + const uniformLoc = gl.getUniformLocation(prog, uniform[0]); + gl[uniformFunc[key]](uniformLoc, uniform[1]); + }); + } else if (key == 'sampler2D') { + Object.entries(uniformType[1]).forEach((uniform) => { + const image = unis[uniform[0]].image; + if (image.readyState !== undefined && image.readyState === 0) return; + image.src = userUnis[uniform[0]] || image.src; + image.width = res[0]; + image.height = res[1]; + const texLoc = gl.getUniformLocation(prog, uniform[0]); + const idx = unis[uniform[0]].textureIndex; + gl.activeTexture(gl.TEXTURE0 + idx); + gl.bindTexture(gl.TEXTURE_2D, unis[uniform[0]].texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + gl.uniform1i(texLoc, idx); + }); + }; + }); + gl.uniform2fv(gl.getUniformLocation(prog, "resolution"), [res[0], res[1]]); + gl.uniform1f(gl.getUniformLocation(prog, "time"), this.time * 0.001); + gl.drawArrays(gl.TRIANGLES, 0, 6); + gl.flush(); + if (this.ready) + window.requestAnimationFrame(this.render); + else + window.cancelAnimationFrame(this.render); + }; + this.render = render.bind(this); + this.time = 0.0; + this.startTime = Date.now(); + this.render(); + } + play() { + this.ready = true; + this.startTime = Date.now(); + this.render(); + } + stop() { + this.ready = false; + this.time = 0.0; + this.StartTime = Date.now(); + this.render(); + } + set(uniform, value) { + this.uniforms[uniform] = value; + } + get(uniform) { + return this.uniforms[uniform] + } + }; diff --git a/tests/scatter/index.html b/tests/scatter/index.html new file mode 100755 index 0000000..494012d --- /dev/null +++ b/tests/scatter/index.html @@ -0,0 +1,22 @@ + + + + Spax's Scatter Shader + + + + + + diff --git a/tests/scatter/scatter-frag.glsl.js b/tests/scatter/scatter-frag.glsl.js new file mode 100755 index 0000000..06bfaeb --- /dev/null +++ b/tests/scatter/scatter-frag.glsl.js @@ -0,0 +1,69 @@ +export const +scatter = ` +precision lowp float; + +uniform vec2 resolution; +uniform float time; +uniform sampler2D sampler0; + +//rgb2hsv and hsv2rgb written by XeroOl +//All components are in the range [0...1], including hue. +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +float random3d(vec3 seed) { + return fract(sin(dot(seed, vec3(12.9898, 78.233, 45.543))) * 43758.5453); +} + +float random2d(vec2 seed) { + return fract(sin(dot(seed, vec2(12.9898, 78.233))) * 43758.5453); +} + +float random1d(float seed) { + return fract(sin(seed) * 43758.5453); +} + +void main() { + //speed is like this cuz I was syncing it to Virtual Insanity + float speed = 46.0/60.0; + float offset = -3.65; + float seed = max(0.0, floor(time*speed+offset)); + vec2 st = gl_FragCoord.xy/resolution.xy; + + vec4 image = texture2D(sampler0, st); + vec3 colorrgb = image.xyz; + vec3 colorhsv = rgb2hsv(image.xyz); + + /*vec3 randColor = vec3( + colorhsv.x*((random3d(colorhsv+seed)*0.75)+0.5), + colorhsv.y*((random3d(colorhsv+seed)*0.75)+0.5), + colorhsv.z*((random3d(colorhsv+seed)*0.75)+0.5) + );*/ + /*vec3 randColor = vec3( + colorhsv.x, + random1d(seed + colorhsv.y), + abs(random2d(fract(time*st)) + colorhsv.z) + );*/ + vec3 randColor = vec3( + random3d((seed + 0.01) * (colorrgb+0.5)), //colorrgb.x, + random3d((seed + 0.33) * (colorrgb+0.5)), //colorrgb.y, + random3d((seed + 0.67) * (colorrgb+0.5)) //colorrgb.z + ); + + //gl_FragColor = vec4(image.xyz,1.0); + gl_FragColor = vec4(randColor,1.0); + //gl_FragColor = vec4(hsv2rgb(randColor),1.0); +} +`;