-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
10c0b0f
commit d10f48c
Showing
3 changed files
with
347 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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] | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Spax's Scatter Shader</title> | ||
</head> | ||
<body style="background-color: black;"> | ||
<canvas id="scatter-shader" width="1440px" height="1080px" style="width:100%;max-width:720px;"></canvas> | ||
<script async type="module"> | ||
import { | ||
SimpleShader as Shader | ||
} from '../../js/simple-shader.js'; | ||
import * as frags from '/scatter-frag.glsl.js'; | ||
const def = new Shader('scatter-shader', { | ||
frag: frags.scatter, | ||
sampler2D: { | ||
sampler0: '../../videos/virtual_insanity.mp4', | ||
} | ||
}); | ||
def.play(); | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
`; |