mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 12:29:30 -07:00
Replacing the old project with the new project, using the "unraveled" WebGL calls
This commit is contained in:
128
js/bloomPass.js
128
js/bloomPass.js
@@ -1,128 +0,0 @@
|
||||
import { loadText, makePassFBO, makePass } from "./utils.js";
|
||||
|
||||
const pyramidHeight = 5;
|
||||
|
||||
const makePyramid = (regl, height, halfFloat) =>
|
||||
Array(height)
|
||||
.fill()
|
||||
.map((_) => makePassFBO(regl, halfFloat));
|
||||
|
||||
const resizePyramid = (pyramid, vw, vh, scale) =>
|
||||
pyramid.forEach((fbo, index) => fbo.resize(Math.floor((vw * scale) / 2 ** index), Math.floor((vh * scale) / 2 ** index)));
|
||||
|
||||
export default ({ regl }, inputs) => {
|
||||
const bloomStrength = 0.7; // The intensity of the bloom
|
||||
const bloomSize = 0.4; // The amount the bloom calculation is scaled
|
||||
const highPassThreshold = 0.1; // The minimum brightness that is still blurred
|
||||
|
||||
const highPassPyramid = makePyramid(regl, pyramidHeight);
|
||||
const hBlurPyramid = makePyramid(regl, pyramidHeight);
|
||||
const vBlurPyramid = makePyramid(regl, pyramidHeight);
|
||||
const output = makePassFBO(regl);
|
||||
|
||||
const highPass = regl({
|
||||
frag: `
|
||||
precision mediump float;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float highPassThreshold;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture2D(tex, vUV);
|
||||
if (color.r < highPassThreshold) color.r = 0.0;
|
||||
if (color.g < highPassThreshold) color.g = 0.0;
|
||||
if (color.b < highPassThreshold) color.b = 0.0;
|
||||
gl_FragColor = color;
|
||||
}
|
||||
`,
|
||||
uniforms: {
|
||||
highPassThreshold,
|
||||
tex: regl.prop("tex"),
|
||||
},
|
||||
framebuffer: regl.prop("fbo"),
|
||||
});
|
||||
|
||||
const blur = regl({
|
||||
frag: `
|
||||
precision mediump float;
|
||||
|
||||
uniform float width, height;
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 direction;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
vec2 size = width > height ? vec2(width / height, 1.) : vec2(1., height / width);
|
||||
gl_FragColor =
|
||||
texture2D(tex, vUV) * 0.442 +
|
||||
(
|
||||
texture2D(tex, vUV + direction / max(width, height) * size) +
|
||||
texture2D(tex, vUV - direction / max(width, height) * size)
|
||||
) * 0.279;
|
||||
}
|
||||
`,
|
||||
uniforms: {
|
||||
tex: regl.prop("tex"),
|
||||
direction: regl.prop("direction"),
|
||||
height: regl.context("viewportWidth"),
|
||||
width: regl.context("viewportHeight"),
|
||||
},
|
||||
framebuffer: regl.prop("fbo"),
|
||||
});
|
||||
|
||||
// The pyramid of textures gets flattened (summed) into a final blurry "bloom" texture
|
||||
const combine = regl({
|
||||
frag: `
|
||||
precision mediump float;
|
||||
|
||||
uniform sampler2D pyr_0, pyr_1, pyr_2, pyr_3, pyr_4;
|
||||
uniform float bloomStrength;
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
vec4 total = vec4(0.);
|
||||
total += texture2D(pyr_0, vUV) * 0.96549;
|
||||
total += texture2D(pyr_1, vUV) * 0.92832;
|
||||
total += texture2D(pyr_2, vUV) * 0.88790;
|
||||
total += texture2D(pyr_3, vUV) * 0.84343;
|
||||
total += texture2D(pyr_4, vUV) * 0.79370;
|
||||
gl_FragColor = total * bloomStrength;
|
||||
}
|
||||
`,
|
||||
uniforms: {
|
||||
bloomStrength,
|
||||
...Object.fromEntries(vBlurPyramid.map((fbo, index) => [`pyr_${index}`, fbo])),
|
||||
},
|
||||
framebuffer: output,
|
||||
});
|
||||
|
||||
return makePass(
|
||||
{
|
||||
primary: inputs.primary,
|
||||
bloom: output,
|
||||
},
|
||||
null,
|
||||
(w, h) => {
|
||||
// The blur pyramids can be lower resolution than the screen.
|
||||
resizePyramid(highPassPyramid, w, h, bloomSize);
|
||||
resizePyramid(hBlurPyramid, w, h, bloomSize);
|
||||
resizePyramid(vBlurPyramid, w, h, bloomSize);
|
||||
output.resize(w, h);
|
||||
},
|
||||
() => {
|
||||
for (let i = 0; i < pyramidHeight; i++) {
|
||||
const highPassFBO = highPassPyramid[i];
|
||||
const hBlurFBO = hBlurPyramid[i];
|
||||
const vBlurFBO = vBlurPyramid[i];
|
||||
highPass({ fbo: highPassFBO, tex: i === 0 ? inputs.primary : highPassPyramid[i - 1] });
|
||||
blur({ fbo: hBlurFBO, tex: highPassFBO, direction: [1, 0] });
|
||||
blur({ fbo: vBlurFBO, tex: hBlurFBO, direction: [0, 1] });
|
||||
}
|
||||
|
||||
combine();
|
||||
}
|
||||
);
|
||||
};
|
||||
365
js/main.js
365
js/main.js
@@ -1,15 +1,11 @@
|
||||
import { init, load, resize, draw } from "./unraveled.js";
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
document.body.appendChild(canvas);
|
||||
document.addEventListener("touchmove", (e) => e.preventDefault(), {
|
||||
passive: false,
|
||||
});
|
||||
|
||||
import { makeFullScreenQuad, makePipeline } from "./utils.js";
|
||||
|
||||
import makeRain from "./rainPass.js";
|
||||
import makeBloomPass from "./bloomPass.js";
|
||||
import makePalettePass from "./palettePass.js";
|
||||
|
||||
const dimensions = { width: 1, height: 1 };
|
||||
|
||||
const loadJS = (src) =>
|
||||
@@ -21,321 +17,64 @@ const loadJS = (src) =>
|
||||
document.body.appendChild(tag);
|
||||
});
|
||||
|
||||
const init = async () => {
|
||||
await loadJS("lib/regl.js");
|
||||
await loadJS("lib/webgl-debug.js");
|
||||
const start = async () => {
|
||||
|
||||
const resize = () => {
|
||||
const resizeViewport = () => {
|
||||
const devicePixelRatio = window.devicePixelRatio ?? 1;
|
||||
canvas.width = Math.ceil(canvas.clientWidth * devicePixelRatio * 0.75);
|
||||
canvas.height = Math.ceil(canvas.clientHeight * devicePixelRatio * 0.75);
|
||||
};
|
||||
window.onresize = resize;
|
||||
if (document.fullscreenEnabled || document.webkitFullscreenEnabled) {
|
||||
window.ondblclick = () => {
|
||||
if (document.fullscreenElement == null) {
|
||||
if (canvas.webkitRequestFullscreen != null) {
|
||||
canvas.webkitRequestFullscreen();
|
||||
} else {
|
||||
canvas.requestFullscreen();
|
||||
}
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
};
|
||||
window.onresize = resizeViewport;
|
||||
resizeViewport();
|
||||
|
||||
const gl = canvas.getContext("webgl");
|
||||
|
||||
const image = new Image();
|
||||
image.crossOrigin = "anonymous";
|
||||
image.src = "./assets/matrixcode_msdf.png";
|
||||
await image.decode();
|
||||
|
||||
const palette = [
|
||||
[ 0, 0, 0, 255, ],
|
||||
[ 7, 33, 0, 255, ],
|
||||
[ 15, 63, 2, 255, ],
|
||||
[ 22, 96, 5, 255, ],
|
||||
[ 38, 117, 17, 255, ],
|
||||
[ 53, 137, 33, 255, ],
|
||||
[ 71, 160, 48, 255, ],
|
||||
[ 86, 181, 63, 255, ],
|
||||
[ 104, 204, 79, 255, ],
|
||||
[ 119, 224, 94, 255, ],
|
||||
[ 135, 247, 109, 255, ],
|
||||
[ 155, 247, 132, 255, ],
|
||||
[ 175, 249, 158, 255, ],
|
||||
[ 175, 249, 158, 255, ],
|
||||
[ 175, 249, 158, 255, ],
|
||||
[ 175, 249, 158, 255, ],
|
||||
].flat();
|
||||
|
||||
init(gl);
|
||||
load(gl, image, palette);
|
||||
|
||||
let tick = 0;
|
||||
const start = Date.now();
|
||||
|
||||
const update = () => {
|
||||
tick++;
|
||||
|
||||
if (dimensions.width !== canvas.width || dimensions.height !== canvas.height) {
|
||||
dimensions.width = canvas.width;
|
||||
dimensions.height = canvas.height;
|
||||
resize(gl, dimensions.width, dimensions.height);
|
||||
}
|
||||
|
||||
draw(gl, tick, (Date.now() - start) / 1000);
|
||||
requestAnimationFrame(update);
|
||||
}
|
||||
resize();
|
||||
update();
|
||||
|
||||
const extensions = ["OES_texture_half_float", "OES_texture_half_float_linear"];
|
||||
// These extensions are also needed, but Safari misreports that they are missing
|
||||
const optionalExtensions = ["EXT_color_buffer_half_float", "WEBGL_color_buffer_float", "OES_standard_derivatives"];
|
||||
|
||||
const { makeDebugContext } = WebGLDebugUtils;
|
||||
|
||||
const glConsts = {
|
||||
DEPTH_TEST: 0x0B71,
|
||||
BLEND: 0x0BE2,
|
||||
UNPACK_ALIGNMENT: 0x0CF5,
|
||||
TEXTURE_2D: 0x0DE1,
|
||||
RGBA: 0x1908,
|
||||
LUMINANCE: 0x1909,
|
||||
NEAREST: 0x2600,
|
||||
LINEAR: 0x2601,
|
||||
TEXTURE_MAG_FILTER: 0x2800,
|
||||
TEXTURE_MIN_FILTER: 0x2801,
|
||||
TEXTURE_WRAP_S: 0x2802,
|
||||
TEXTURE_WRAP_T: 0x2803,
|
||||
CLAMP_TO_EDGE: 0x812F,
|
||||
DEPTH_STENCIL_ATTACHMENT: 0x821A,
|
||||
TEXTURE0: 0x84C0,
|
||||
DEPTH_STENCIL: 0x84F9,
|
||||
RGBA16F_EXT: 0x881A,
|
||||
STATIC_DRAW: 0x88E4,
|
||||
ACTIVE_UNIFORMS: 0x8B86,
|
||||
ACTIVE_ATTRIBUTES: 0x8B89,
|
||||
COLOR_ATTACHMENT0: 0x8CE0,
|
||||
DEPTH_ATTACHMENT: 0x8D00,
|
||||
STENCIL_ATTACHMENT: 0x8D20,
|
||||
FRAMEBUFFER: 0x8D40,
|
||||
RENDERBUFFER: 0x8D41,
|
||||
HALF_FLOAT_OES: 0x8D61,
|
||||
UNPACK_FLIP_Y_WEBGL: 0x9240,
|
||||
UNPACK_PREMULTIPLY_ALPHA_WEBGL: 0x9241,
|
||||
UNPACK_COLORSPACE_CONVERSION_WEBGL: 0x9243,
|
||||
BROWSER_DEFAULT_WEBGL: 0x9244,
|
||||
|
||||
ARRAY_BUFFER: 34962,
|
||||
FRAGMENT_SHADER: 35632,
|
||||
FRAGMENT_SHADER: 35632,
|
||||
VERTEX_SHADER: 35633,
|
||||
COMPILE_STATUS: 35713,
|
||||
LINK_STATUS: 35714,
|
||||
TRIANGLES: 4,
|
||||
UNSIGNED_BYTE: 5121,
|
||||
FLOAT: 5126,
|
||||
};
|
||||
|
||||
for (let i = 1; i < 32; i++) {
|
||||
glConsts[`TEXTURE${i}`] = 0x84C0 + i;
|
||||
}
|
||||
|
||||
const betterNames = {
|
||||
[11]: "defaultFragShader",
|
||||
[12]: "defaultVertShader",
|
||||
[13]: "defaultProgram",
|
||||
[17]: "fullscreen_geometry",
|
||||
[18]: "rain_compute_doublebuffer_1_texture", [19]: "rain_compute_doublebuffer_1_framebuffer",
|
||||
[20]: "rain_compute_doublebuffer_2_texture", [21]: "rain_compute_doublebuffer_2_framebuffer",
|
||||
[22]: "rain_compute_shader",
|
||||
[23]: "placeholder_texture",
|
||||
[24]: "rain_output_texture", [25]: "rain_output_framebuffer", [26]: "rain_output_renderbuffer",
|
||||
[27]: "rain_frag_shader",
|
||||
[28]: "rain_vert_shader",
|
||||
[29]: "rain_program",
|
||||
[47]: "rain_geometry",
|
||||
|
||||
[48]: "bloom_high_pass_pyr_0_texture", [49]: "bloom_high_pass_pyr_0_framebuffer", [50]: "bloom_high_pass_pyr_0_renderbuffer",
|
||||
[51]: "bloom_high_pass_pyr_1_texture", [52]: "bloom_high_pass_pyr_1_framebuffer", [53]: "bloom_high_pass_pyr_1_renderbuffer",
|
||||
[54]: "bloom_high_pass_pyr_2_texture", [55]: "bloom_high_pass_pyr_2_framebuffer", [56]: "bloom_high_pass_pyr_2_renderbuffer",
|
||||
[57]: "bloom_high_pass_pyr_3_texture", [58]: "bloom_high_pass_pyr_3_framebuffer", [59]: "bloom_high_pass_pyr_3_renderbuffer",
|
||||
[60]: "bloom_high_pass_pyr_4_texture", [61]: "bloom_high_pass_pyr_4_framebuffer", [62]: "bloom_high_pass_pyr_4_renderbuffer",
|
||||
|
||||
[63]: "bloom_h_blur_pyr_0_texture", [64]: "bloom_h_blur_pyr_0_framebuffer", [65]: "bloom_h_blur_pyr_0_renderbuffer",
|
||||
[66]: "bloom_h_blur_pyr_1_texture", [67]: "bloom_h_blur_pyr_1_framebuffer", [68]: "bloom_h_blur_pyr_1_renderbuffer",
|
||||
[69]: "bloom_h_blur_pyr_2_texture", [70]: "bloom_h_blur_pyr_2_framebuffer", [71]: "bloom_h_blur_pyr_2_renderbuffer",
|
||||
[72]: "bloom_h_blur_pyr_3_texture", [73]: "bloom_h_blur_pyr_3_framebuffer", [74]: "bloom_h_blur_pyr_3_renderbuffer",
|
||||
[75]: "bloom_h_blur_pyr_4_texture", [76]: "bloom_h_blur_pyr_4_framebuffer", [77]: "bloom_h_blur_pyr_4_renderbuffer",
|
||||
|
||||
[78]: "bloom_v_blur_pyr_0_texture", [79]: "bloom_v_blur_pyr_0_framebuffer", [80]: "bloom_v_blur_pyr_0_renderbuffer",
|
||||
[81]: "bloom_v_blur_pyr_1_texture", [82]: "bloom_v_blur_pyr_1_framebuffer", [83]: "bloom_v_blur_pyr_1_renderbuffer",
|
||||
[84]: "bloom_v_blur_pyr_2_texture", [85]: "bloom_v_blur_pyr_2_framebuffer", [86]: "bloom_v_blur_pyr_2_renderbuffer",
|
||||
[87]: "bloom_v_blur_pyr_3_texture", [88]: "bloom_v_blur_pyr_3_framebuffer", [89]: "bloom_v_blur_pyr_3_renderbuffer",
|
||||
[90]: "bloom_v_blur_pyr_4_texture", [91]: "bloom_v_blur_pyr_4_framebuffer", [92]: "bloom_v_blur_pyr_4_renderbuffer",
|
||||
|
||||
[93]: "bloom_output_texture", [94]: "bloom_output_framebuffer", [95]: "bloom_output_renderbuffer",
|
||||
[96]: "bloom_high_pass_shader",
|
||||
[97]: "bloom_blur_shader",
|
||||
[98]: "bloom_combine_shader",
|
||||
[99]: "palette_output_texture", [100]: "palette_output_framebuffer", [101]: "palette_output_renderbuffer",
|
||||
[102]: "palette_texture",
|
||||
[103]: "palette_shader",
|
||||
[104]: "msdf_texture",
|
||||
[105]: "rain_compute_program",
|
||||
[125]: "bloom_high_pass_program",
|
||||
[131]: "bloom_blur_program",
|
||||
[141]: "bloom_combine_program",
|
||||
[155]: "palette_program",
|
||||
};
|
||||
|
||||
const returnedValueNames = [];
|
||||
|
||||
const glConstsByID = {};
|
||||
Object.entries(glConsts).forEach(([key, value]) => {
|
||||
if (glConstsByID[value] == null) {
|
||||
glConstsByID[value] = [];
|
||||
}
|
||||
glConstsByID[value].push(key);
|
||||
});
|
||||
|
||||
const returnedValues = [];
|
||||
const returnedValueUsages = [];
|
||||
const commands = [];
|
||||
|
||||
const log = [];
|
||||
|
||||
const printCommands = (label) => {
|
||||
const printedCommands = [];
|
||||
for (const {name, args, retIndex} of commands) {
|
||||
const printedArgs = [];
|
||||
for (const [type, value] of args) {
|
||||
if (value == null) {
|
||||
printedArgs.push(`null`);
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(value) || ArrayBuffer.isView(value)) {
|
||||
printedArgs.push(`[${value.join(", ")}]`);
|
||||
continue;
|
||||
}
|
||||
switch (type) {
|
||||
case "string":
|
||||
printedArgs.push(`\`${value}\``);
|
||||
break;
|
||||
case "GLenum":
|
||||
printedArgs.push(`gl.${value}`);
|
||||
break;
|
||||
case "object":
|
||||
printedArgs.push(`${value}`);
|
||||
break;
|
||||
case "returnedValue":
|
||||
printedArgs.push(`state.${returnedValueNames[value]}`);
|
||||
break;
|
||||
default:
|
||||
printedArgs.push(`${value}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (retIndex != -1 && returnedValueUsages[retIndex] > 0) {
|
||||
printedCommands.push(`state.${returnedValueNames[retIndex]} = gl.${name}(${printedArgs.join(", ")});`);
|
||||
} else {
|
||||
printedCommands.push(`gl.${name}(${printedArgs.join(", ")});`);
|
||||
}
|
||||
}
|
||||
log.push(`// ${label}`);
|
||||
log.push(printedCommands.join("\n"));
|
||||
commands.length = 0;
|
||||
};
|
||||
|
||||
const rawGL = canvas.getContext("webgl");
|
||||
|
||||
const gl = new Proxy(makeDebugContext(
|
||||
rawGL,
|
||||
null,
|
||||
(...a) => {
|
||||
const name = a[0];
|
||||
const args = Array.from(a[1]);
|
||||
const ret = a[2];
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
let value = args[i];
|
||||
let type = typeof value;
|
||||
if (type === "number" && glConstsByID[value] != null) {
|
||||
type = "GLenum";
|
||||
value = glConstsByID[value][0];
|
||||
}
|
||||
if (returnedValues.includes(value)) {
|
||||
type = "returnedValue";
|
||||
value = returnedValues.indexOf(value);
|
||||
returnedValueUsages[value]++;
|
||||
}
|
||||
args[i] = [type, value];
|
||||
}
|
||||
|
||||
let retIndex = -1;
|
||||
if (typeof ret === "object" && !returnedValues.includes(ret)) {
|
||||
returnedValues.push(ret);
|
||||
returnedValueUsages.push(0);
|
||||
retIndex = returnedValues.length - 1;
|
||||
let glType = (ret[Symbol.toStringTag] ?? "object").replaceAll("WebGL", "").toLowerCase();
|
||||
let retName = glType + "_" + retIndex;
|
||||
switch (name) {
|
||||
case "getExtension":
|
||||
retName = glType;
|
||||
break;
|
||||
}
|
||||
switch (glType) {
|
||||
case "texture":
|
||||
break;
|
||||
case "framebuffer":
|
||||
break;
|
||||
case "renderbuffer":
|
||||
break;
|
||||
case "program":
|
||||
break;
|
||||
case "shader":
|
||||
if (args[0][1].toLowerCase().includes("fragment")) {
|
||||
retName = "fragment_shader" + "_" + retIndex;
|
||||
} else {
|
||||
retName = "vertex_shader" + "_" + retIndex;;
|
||||
}
|
||||
break;
|
||||
case "activeinfo":
|
||||
retName = returnedValueNames[args[0][1]] + "_a_" + ret.name;
|
||||
break;
|
||||
case "uniformlocation":
|
||||
retName = returnedValueNames[args[0][1]] + "_u_" + args[1][1];
|
||||
break;
|
||||
}
|
||||
if (returnedValueNames.includes(retName)) {
|
||||
retName = retName + "_" + retIndex;
|
||||
}
|
||||
|
||||
if (betterNames[retIndex] != null) {
|
||||
retName = betterNames[retIndex];
|
||||
}
|
||||
|
||||
returnedValueNames[retIndex] = retName;
|
||||
}
|
||||
|
||||
commands.push({ name, args, retIndex });
|
||||
}
|
||||
), {
|
||||
/*
|
||||
get(target, prop, receiver) {
|
||||
const ret = Reflect.get(...arguments);
|
||||
if (typeof ret !== "function") {
|
||||
console.log("GET", prop, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
*/
|
||||
});
|
||||
|
||||
const regl = createREGL({ gl, pixelRatio: 1, extensions, optionalExtensions });
|
||||
|
||||
printCommands("INIT");
|
||||
returnedValueUsages.fill(0);
|
||||
|
||||
// All this takes place in a full screen quad.
|
||||
const fullScreenQuad = makeFullScreenQuad(regl);
|
||||
const pipeline = makePipeline({ regl }, [makeRain, makeBloomPass, makePalettePass]);
|
||||
const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary };
|
||||
const drawToScreen = regl({ uniforms: screenUniforms });
|
||||
await Promise.all(pipeline.map((step) => step.ready));
|
||||
|
||||
printCommands("LOAD");
|
||||
|
||||
const render = ({ viewportWidth, viewportHeight }) => {
|
||||
const now = regl.now() * 1000;
|
||||
|
||||
if (dimensions.width !== viewportWidth || dimensions.height !== viewportHeight) {
|
||||
dimensions.width = viewportWidth;
|
||||
dimensions.height = viewportHeight;
|
||||
for (const step of pipeline) {
|
||||
step.setSize(viewportWidth, viewportHeight);
|
||||
}
|
||||
printCommands("RESIZE");
|
||||
}
|
||||
fullScreenQuad(() => {
|
||||
for (const step of pipeline) {
|
||||
step.execute();
|
||||
}
|
||||
drawToScreen();
|
||||
});
|
||||
printCommands("DRAW");
|
||||
};
|
||||
|
||||
render({ viewportWidth: 640, viewportHeight: 480 });
|
||||
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => resolve(), 1000)
|
||||
});
|
||||
|
||||
render({ viewportWidth: 640, viewportHeight: 480 });
|
||||
|
||||
console.log(log.join("\n"));
|
||||
|
||||
// const tick = regl.frame(render);
|
||||
};
|
||||
|
||||
document.body.onload = () => {
|
||||
init();
|
||||
start();
|
||||
};
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import { make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||
|
||||
export default ({ regl }, inputs) => {
|
||||
const output = makePassFBO(regl);
|
||||
const render = regl({
|
||||
frag: `
|
||||
precision mediump float;
|
||||
#define PI 3.14159265359
|
||||
|
||||
uniform sampler2D tex, bloomTex, paletteTex;
|
||||
uniform float time;
|
||||
varying vec2 vUV;
|
||||
|
||||
highp float rand( const in vec2 uv, const in float t ) {
|
||||
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
|
||||
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
|
||||
return fract(sin(sn) * c + t);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 primary = texture2D(tex, vUV);
|
||||
vec4 bloom = texture2D(bloomTex, vUV);
|
||||
vec4 brightness = primary + bloom - rand( gl_FragCoord.xy, time ) * 0.0167;
|
||||
gl_FragColor = vec4(
|
||||
texture2D( paletteTex, vec2(brightness.r, 0.0)).rgb
|
||||
+ min(vec3(0.756, 1.0, 0.46) * brightness.g * 2.0, vec3(1.0)),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
`,
|
||||
uniforms: {
|
||||
tex: inputs.primary,
|
||||
bloomTex: inputs.bloom,
|
||||
paletteTex: make1DTexture(regl, [
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
[0.03, 0.13, 0.0, 1.0],
|
||||
[0.06, 0.25, 0.01, 1.0],
|
||||
[0.09, 0.38, 0.02, 1.0],
|
||||
[0.15, 0.46, 0.07, 1.0],
|
||||
[0.21, 0.54, 0.13, 1.0],
|
||||
[0.28, 0.63, 0.19, 1.0],
|
||||
[0.34, 0.71, 0.25, 1.0],
|
||||
[0.41, 0.8, 0.31, 1.0],
|
||||
[0.47, 0.88, 0.37, 1.0],
|
||||
[0.53, 0.97, 0.43, 1.0],
|
||||
[0.61, 0.97, 0.52, 1.0],
|
||||
[0.69, 0.98, 0.62, 1.0],
|
||||
[0.69, 0.98, 0.62, 1.0],
|
||||
[0.69, 0.98, 0.62, 1.0],
|
||||
[0.69, 0.98, 0.62, 1.0],
|
||||
]),
|
||||
},
|
||||
framebuffer: output,
|
||||
});
|
||||
|
||||
return makePass(
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
null,
|
||||
(w, h) => output.resize(w, h),
|
||||
() => render()
|
||||
);
|
||||
};
|
||||
256
js/rainPass.js
256
js/rainPass.js
@@ -1,256 +0,0 @@
|
||||
import { loadImage, makePassFBO, makeDoubleBuffer, makePass } from "./utils.js";
|
||||
|
||||
const numVerticesPerQuad = 2 * 3;
|
||||
const tlVert = [0, 0];
|
||||
const trVert = [0, 1];
|
||||
const blVert = [1, 0];
|
||||
const brVert = [1, 1];
|
||||
const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert];
|
||||
|
||||
export default ({ regl }) => {
|
||||
const size = 80; // The maximum dimension of the glyph grid
|
||||
|
||||
const commonUniforms = {
|
||||
glyphSequenceLength: 57,
|
||||
glyphTextureGridSize: [8, 8],
|
||||
numColumns: size,
|
||||
numRows: size,
|
||||
};
|
||||
|
||||
const computeDoubleBuffer = makeDoubleBuffer(regl, {
|
||||
width: size,
|
||||
height: size,
|
||||
wrapT: "clamp",
|
||||
type: "half float",
|
||||
});
|
||||
|
||||
const compute = regl({
|
||||
frag: `
|
||||
precision highp float;
|
||||
|
||||
#define PI 3.14159265359
|
||||
#define SQRT_2 1.4142135623730951
|
||||
#define SQRT_5 2.23606797749979
|
||||
|
||||
uniform sampler2D previousComputeState;
|
||||
|
||||
uniform float numColumns, numRows;
|
||||
uniform float time, tick;
|
||||
uniform float fallSpeed, cycleSpeed;
|
||||
uniform float glyphSequenceLength;
|
||||
uniform float raindropLength;
|
||||
|
||||
// Helper functions for generating randomness, borrowed from elsewhere
|
||||
|
||||
highp float randomFloat( const in vec2 uv ) {
|
||||
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
|
||||
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
|
||||
return fract(sin(sn) * c);
|
||||
}
|
||||
|
||||
float wobble(float x) {
|
||||
return x + 0.3 * sin(SQRT_2 * x) + 0.2 * sin(SQRT_5 * x);
|
||||
}
|
||||
|
||||
float getRainBrightness(float simTime, vec2 glyphPos) {
|
||||
float columnTimeOffset = randomFloat(vec2(glyphPos.x, 0.)) * 1000.;
|
||||
float columnSpeedOffset = randomFloat(vec2(glyphPos.x + 0.1, 0.)) * 0.5 + 0.5;
|
||||
float columnTime = columnTimeOffset + simTime * fallSpeed * columnSpeedOffset;
|
||||
float rainTime = (glyphPos.y * 0.01 + columnTime) / raindropLength;
|
||||
rainTime = wobble(rainTime);
|
||||
return 1.0 - fract(rainTime);
|
||||
}
|
||||
|
||||
vec2 computeRaindrop(float simTime, vec2 glyphPos) {
|
||||
float brightness = getRainBrightness(simTime, glyphPos);
|
||||
float brightnessBelow = getRainBrightness(simTime, glyphPos + vec2(0., -1.));
|
||||
bool cursor = brightness > brightnessBelow;
|
||||
return vec2(brightness, cursor);
|
||||
}
|
||||
|
||||
vec2 computeSymbol(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenPos, vec4 previous) {
|
||||
|
||||
float previousSymbol = previous.r;
|
||||
float previousAge = previous.g;
|
||||
bool resetGlyph = isFirstFrame;
|
||||
if (resetGlyph) {
|
||||
previousAge = randomFloat(screenPos + 0.5);
|
||||
previousSymbol = floor(glyphSequenceLength * randomFloat(screenPos));
|
||||
}
|
||||
float age = previousAge;
|
||||
float symbol = previousSymbol;
|
||||
if (mod(tick, 1.0) == 0.) {
|
||||
age += cycleSpeed;
|
||||
if (age >= 1.) {
|
||||
symbol = floor(glyphSequenceLength * randomFloat(screenPos + simTime));
|
||||
age = fract(age);
|
||||
}
|
||||
}
|
||||
|
||||
return vec2(symbol, age);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 glyphPos = gl_FragCoord.xy;
|
||||
vec2 screenPos = glyphPos / vec2(numColumns, numRows);
|
||||
|
||||
vec2 raindrop = computeRaindrop(time, glyphPos);
|
||||
|
||||
bool isFirstFrame = tick <= 1.;
|
||||
vec4 previous = texture2D( previousComputeState, screenPos );
|
||||
vec4 previousSymbol = vec4(previous.ba, 0.0, 0.0);
|
||||
vec2 symbol = computeSymbol(time, isFirstFrame, glyphPos, screenPos, previousSymbol);
|
||||
gl_FragColor = vec4(raindrop, symbol);
|
||||
}
|
||||
|
||||
`,
|
||||
uniforms: {
|
||||
...commonUniforms,
|
||||
cycleSpeed: 0.03, // The speed glyphs change
|
||||
fallSpeed: 0.3, // The speed the raindrops progress downwards
|
||||
raindropLength: 0.75, // Adjusts the frequency of raindrops (and their length) in a column
|
||||
previousComputeState: computeDoubleBuffer.back,
|
||||
},
|
||||
|
||||
framebuffer: computeDoubleBuffer.front,
|
||||
});
|
||||
|
||||
// We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
|
||||
const glyphMSDF = loadImage(regl, "assets/matrixcode_msdf.png");
|
||||
const output = makePassFBO(regl);
|
||||
const render = regl({
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
src: "one",
|
||||
dst: "one",
|
||||
},
|
||||
},
|
||||
vert: `
|
||||
precision lowp float;
|
||||
|
||||
attribute vec2 aPosition;
|
||||
uniform vec2 screenSize;
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
vUV = aPosition;
|
||||
gl_Position = vec4((aPosition - 0.5) * 2.0 * screenSize, 0.0, 1.0);
|
||||
}
|
||||
`,
|
||||
frag: `
|
||||
#define PI 3.14159265359
|
||||
#ifdef GL_OES_standard_derivatives
|
||||
#extension GL_OES_standard_derivatives: enable
|
||||
#endif
|
||||
precision lowp float;
|
||||
|
||||
uniform sampler2D computeState;
|
||||
uniform float numColumns, numRows;
|
||||
uniform sampler2D glyphMSDF;
|
||||
uniform float msdfPxRange;
|
||||
uniform vec2 glyphMSDFSize;
|
||||
uniform float glyphSequenceLength;
|
||||
uniform vec2 glyphTextureGridSize;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
float median3(vec3 i) {
|
||||
return max(min(i.r, i.g), min(max(i.r, i.g), i.b));
|
||||
}
|
||||
|
||||
float modI(float a, float b) {
|
||||
float m = a - floor((a + 0.5) / b) * b;
|
||||
return floor(m + 0.5);
|
||||
}
|
||||
|
||||
vec3 getBrightness(vec2 raindrop, vec2 uv) {
|
||||
|
||||
float base = raindrop.r;
|
||||
bool isCursor = bool(raindrop.g);
|
||||
float glint = base;
|
||||
|
||||
base = base * 1.1 - 0.5;
|
||||
glint = glint * 2.5 - 1.5;
|
||||
|
||||
return vec3(
|
||||
(isCursor ? vec2(0.0, 1.0) : vec2(1.0, 0.0)) * base,
|
||||
glint
|
||||
);
|
||||
}
|
||||
|
||||
vec2 getSymbolUV(float index) {
|
||||
float symbolX = modI(index, glyphTextureGridSize.x);
|
||||
float symbolY = (index - symbolX) / glyphTextureGridSize.x;
|
||||
symbolY = glyphTextureGridSize.y - symbolY - 1.;
|
||||
return vec2(symbolX, symbolY);
|
||||
}
|
||||
|
||||
vec2 getSymbol(vec2 uv, float index) {
|
||||
// resolve UV to cropped position of glyph in MSDF texture
|
||||
uv = fract(uv * vec2(numColumns, numRows));
|
||||
uv = (uv + getSymbolUV(index)) / glyphTextureGridSize;
|
||||
|
||||
// MSDF: calculate brightness of fragment based on distance to shape
|
||||
vec2 symbol;
|
||||
{
|
||||
vec2 unitRange = vec2(msdfPxRange) / glyphMSDFSize;
|
||||
vec2 screenTexSize = vec2(1.0) / fwidth(uv);
|
||||
float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0);
|
||||
|
||||
float signedDistance = median3(texture2D(glyphMSDF, uv).rgb);
|
||||
float screenPxDistance = screenPxRange * (signedDistance - 0.5);
|
||||
symbol.r = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 data = texture2D(computeState, vUV);
|
||||
vec3 brightness = getBrightness(data.rg, vUV);
|
||||
vec2 symbol = getSymbol(vUV, data.b);
|
||||
gl_FragColor = vec4(brightness.rg * symbol.r, brightness.b * symbol.g, 0.);
|
||||
}
|
||||
`,
|
||||
|
||||
uniforms: {
|
||||
...commonUniforms,
|
||||
computeState: computeDoubleBuffer.front,
|
||||
glyphMSDF: glyphMSDF.texture,
|
||||
msdfPxRange: 4.0,
|
||||
glyphMSDFSize: () => [glyphMSDF.width(), glyphMSDF.height()],
|
||||
screenSize: regl.prop("screenSize"),
|
||||
},
|
||||
|
||||
attributes: {
|
||||
aPosition: quadVertices,
|
||||
},
|
||||
count: numVerticesPerQuad,
|
||||
|
||||
framebuffer: output,
|
||||
});
|
||||
|
||||
const screenSize = [1, 1];
|
||||
|
||||
return makePass(
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
Promise.all([glyphMSDF.loaded]),
|
||||
(w, h) => {
|
||||
output.resize(w, h);
|
||||
const aspectRatio = w / h;
|
||||
[screenSize[0], screenSize[1]] = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
|
||||
},
|
||||
() => {
|
||||
compute();
|
||||
regl.clear({
|
||||
depth: 1,
|
||||
color: [0, 0, 0, 1],
|
||||
framebuffer: output,
|
||||
});
|
||||
render({ screenSize });
|
||||
}
|
||||
);
|
||||
};
|
||||
809
js/unraveled.js
Normal file
809
js/unraveled.js
Normal file
@@ -0,0 +1,809 @@
|
||||
const extendedContext = {};
|
||||
const state = {};
|
||||
const textures = {};
|
||||
const textureSizes = { fullscreen: {scale: 1}};
|
||||
|
||||
const extensionNames = [
|
||||
"oes_texture_half_float",
|
||||
"oes_texture_half_float_linear",
|
||||
"ext_color_buffer_half_float",
|
||||
"webgl_color_buffer_float",
|
||||
"oes_standard_derivatives",
|
||||
];
|
||||
|
||||
const fullscreen_frag_shader_source = `
|
||||
precision mediump float;
|
||||
varying vec2 vUV;
|
||||
uniform sampler2D tex;
|
||||
void main() {
|
||||
gl_FragColor = texture2D(tex, vUV);
|
||||
}
|
||||
`;
|
||||
|
||||
const fullscreen_vert_shader_source = `
|
||||
precision mediump float;
|
||||
attribute vec2 aPosition;
|
||||
varying vec2 vUV;
|
||||
void main() {
|
||||
vUV = 0.5 * (aPosition + 1.0);
|
||||
gl_Position = vec4(aPosition, 0, 1);
|
||||
}
|
||||
`;
|
||||
|
||||
const rain_compute_shader_source = `
|
||||
precision highp float;
|
||||
|
||||
#define PI 3.14159265359
|
||||
#define SQRT_2 1.4142135623730951
|
||||
#define SQRT_5 2.23606797749979
|
||||
|
||||
uniform sampler2D previousComputeState;
|
||||
|
||||
uniform float numColumns, numRows;
|
||||
uniform float time, tick;
|
||||
uniform float fallSpeed, cycleSpeed;
|
||||
uniform float glyphSequenceLength;
|
||||
uniform float raindropLength;
|
||||
|
||||
highp float randomFloat( const in vec2 uv ) {
|
||||
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
|
||||
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
|
||||
return fract(sin(sn) * c);
|
||||
}
|
||||
|
||||
float wobble(float x) {
|
||||
return x + 0.3 * sin(SQRT_2 * x) + 0.2 * sin(SQRT_5 * x);
|
||||
}
|
||||
|
||||
float getRainBrightness(float simTime, vec2 glyphPos) {
|
||||
float columnTimeOffset = randomFloat(vec2(glyphPos.x, 0.)) * 1000.;
|
||||
float columnSpeedOffset = randomFloat(vec2(glyphPos.x + 0.1, 0.)) * 0.5 + 0.5;
|
||||
float columnTime = columnTimeOffset + simTime * fallSpeed * columnSpeedOffset;
|
||||
float rainTime = (glyphPos.y * 0.01 + columnTime) / raindropLength;
|
||||
rainTime = wobble(rainTime);
|
||||
return 1.0 - fract(rainTime);
|
||||
}
|
||||
|
||||
vec2 computeRaindrop(float simTime, vec2 glyphPos) {
|
||||
float brightness = getRainBrightness(simTime, glyphPos);
|
||||
float brightnessBelow = getRainBrightness(simTime, glyphPos + vec2(0., -1.));
|
||||
bool cursor = brightness > brightnessBelow;
|
||||
return vec2(brightness, cursor);
|
||||
}
|
||||
|
||||
vec2 computeSymbol(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenPos, vec4 previous) {
|
||||
|
||||
float previousSymbol = previous.r;
|
||||
float previousAge = previous.g;
|
||||
bool resetGlyph = isFirstFrame;
|
||||
if (resetGlyph) {
|
||||
previousAge = randomFloat(screenPos + 0.5);
|
||||
previousSymbol = floor(glyphSequenceLength * randomFloat(screenPos));
|
||||
}
|
||||
float age = previousAge;
|
||||
float symbol = previousSymbol;
|
||||
if (mod(tick, 1.0) == 0.) {
|
||||
age += cycleSpeed;
|
||||
if (age >= 1.) {
|
||||
symbol = floor(glyphSequenceLength * randomFloat(screenPos + simTime));
|
||||
age = fract(age);
|
||||
}
|
||||
}
|
||||
|
||||
return vec2(symbol, age);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 glyphPos = gl_FragCoord.xy;
|
||||
vec2 screenPos = glyphPos / vec2(numColumns, numRows);
|
||||
|
||||
vec2 raindrop = computeRaindrop(time, glyphPos);
|
||||
|
||||
bool isFirstFrame = tick <= 1.;
|
||||
vec4 previous = texture2D( previousComputeState, screenPos );
|
||||
vec4 previousSymbol = vec4(previous.ba, 0.0, 0.0);
|
||||
vec2 symbol = computeSymbol(time, isFirstFrame, glyphPos, screenPos, previousSymbol);
|
||||
gl_FragColor = vec4(raindrop, symbol);
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const rain_vert_shader_source = `
|
||||
precision lowp float;
|
||||
|
||||
attribute vec2 aPosition;
|
||||
uniform vec2 screenSize;
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
vUV = aPosition;
|
||||
gl_Position = vec4((aPosition - 0.5) * 2.0 * screenSize, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const rain_frag_shader_source = `
|
||||
#define PI 3.14159265359
|
||||
#ifdef GL_OES_standard_derivatives
|
||||
#extension GL_OES_standard_derivatives: enable
|
||||
#endif
|
||||
precision lowp float;
|
||||
|
||||
uniform sampler2D computeState;
|
||||
uniform float numColumns, numRows;
|
||||
uniform sampler2D glyphMSDF;
|
||||
uniform float msdfPxRange;
|
||||
uniform vec2 glyphMSDFSize;
|
||||
uniform float glyphSequenceLength;
|
||||
uniform vec2 glyphTextureGridSize;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
float median3(vec3 i) {
|
||||
return max(min(i.r, i.g), min(max(i.r, i.g), i.b));
|
||||
}
|
||||
|
||||
float modI(float a, float b) {
|
||||
float m = a - floor((a + 0.5) / b) * b;
|
||||
return floor(m + 0.5);
|
||||
}
|
||||
|
||||
vec3 getBrightness(vec2 raindrop, vec2 uv) {
|
||||
|
||||
float base = raindrop.r;
|
||||
bool isCursor = bool(raindrop.g);
|
||||
float glint = base;
|
||||
|
||||
base = base * 1.1 - 0.5;
|
||||
glint = glint * 2.5 - 1.5;
|
||||
|
||||
return vec3(
|
||||
(isCursor ? vec2(0.0, 1.0) : vec2(1.0, 0.0)) * base,
|
||||
glint
|
||||
);
|
||||
}
|
||||
|
||||
vec2 getSymbolUV(float index) {
|
||||
float symbolX = modI(index, glyphTextureGridSize.x);
|
||||
float symbolY = (index - symbolX) / glyphTextureGridSize.x;
|
||||
symbolY = glyphTextureGridSize.y - symbolY - 1.;
|
||||
return vec2(symbolX, symbolY);
|
||||
}
|
||||
|
||||
vec2 getSymbol(vec2 uv, float index) {
|
||||
uv = fract(uv * vec2(numColumns, numRows));
|
||||
uv = (uv + getSymbolUV(index)) / glyphTextureGridSize;
|
||||
|
||||
vec2 symbol;
|
||||
{
|
||||
vec2 unitRange = vec2(msdfPxRange) / glyphMSDFSize;
|
||||
vec2 screenTexSize = vec2(1.0) / fwidth(uv);
|
||||
float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0);
|
||||
|
||||
float signedDistance = median3(texture2D(glyphMSDF, uv).rgb);
|
||||
float screenPxDistance = screenPxRange * (signedDistance - 0.5);
|
||||
symbol.r = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 data = texture2D(computeState, vUV);
|
||||
vec3 brightness = getBrightness(data.rg, vUV);
|
||||
vec2 symbol = getSymbol(vUV, data.b);
|
||||
gl_FragColor = vec4(brightness.rg * symbol.r, brightness.b * symbol.g, 0.);
|
||||
}
|
||||
`;
|
||||
|
||||
const bloom_high_pass_shader_source = `
|
||||
precision mediump float;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float highPassThreshold;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture2D(tex, vUV);
|
||||
|
||||
if (color.r < highPassThreshold) color.r = 0.0;
|
||||
if (color.g < highPassThreshold) color.g = 0.0;
|
||||
if (color.b < highPassThreshold) color.b = 0.0;
|
||||
gl_FragColor = color;
|
||||
}
|
||||
`;
|
||||
|
||||
const bloom_blur_shader_source = `
|
||||
precision mediump float;
|
||||
|
||||
uniform float width, height;
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 direction;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
vec2 size = height > width ? vec2(height / width, 1.) : vec2(1., width / height);
|
||||
gl_FragColor =
|
||||
texture2D(tex, vUV) * 0.442 +
|
||||
(
|
||||
texture2D(tex, vUV + direction / max(height, width) * size) +
|
||||
texture2D(tex, vUV - direction / max(height, width) * size)
|
||||
) * 0.279;
|
||||
}
|
||||
`;
|
||||
|
||||
const bloom_combine_shader_source = `
|
||||
precision mediump float;
|
||||
|
||||
uniform sampler2D pyr_0, pyr_1, pyr_2, pyr_3, pyr_4;
|
||||
uniform float bloomStrength;
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
vec4 total = vec4(0.);
|
||||
total += texture2D(pyr_0, vUV) * 0.96549;
|
||||
total += texture2D(pyr_1, vUV) * 0.92832;
|
||||
total += texture2D(pyr_2, vUV) * 0.88790;
|
||||
total += texture2D(pyr_3, vUV) * 0.84343;
|
||||
total += texture2D(pyr_4, vUV) * 0.79370;
|
||||
gl_FragColor = total * bloomStrength;
|
||||
}
|
||||
`;
|
||||
|
||||
const palette_shader_source = `
|
||||
precision mediump float;
|
||||
#define PI 3.14159265359
|
||||
|
||||
uniform sampler2D tex, bloomTex, paletteTex;
|
||||
uniform float time;
|
||||
varying vec2 vUV;
|
||||
|
||||
highp float rand( const in vec2 uv, const in float t ) {
|
||||
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
|
||||
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
|
||||
return fract(sin(sn) * c + t);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 primary = texture2D(tex, vUV);
|
||||
vec4 bloom = texture2D(bloomTex, vUV);
|
||||
vec4 brightness = primary + bloom - rand( gl_FragCoord.xy, time ) * 0.0167;
|
||||
gl_FragColor = vec4(
|
||||
texture2D( paletteTex, vec2(brightness.r, 0.0)).rgb
|
||||
+ min(vec3(0.756, 1.0, 0.46) * brightness.g * 2.0, vec3(1.0)),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
`;
|
||||
|
||||
const init = (gl) => Object.assign(extendedContext, ...extensionNames.map(name => Object.getPrototypeOf(gl.getExtension(name))));
|
||||
|
||||
const load = (gl, msdfImage, palette) => {
|
||||
|
||||
state.fullscreen_frag_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(state.fullscreen_frag_shader, fullscreen_frag_shader_source); gl.compileShader(state.fullscreen_frag_shader);
|
||||
|
||||
state.fullscreen_vert_shader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(state.fullscreen_vert_shader, fullscreen_vert_shader_source); gl.compileShader(state.fullscreen_vert_shader);
|
||||
|
||||
state.rain_compute_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(state.rain_compute_shader, rain_compute_shader_source); gl.compileShader(state.rain_compute_shader);
|
||||
|
||||
state.rain_frag_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(state.rain_frag_shader, rain_frag_shader_source); gl.compileShader(state.rain_frag_shader);
|
||||
|
||||
state.rain_vert_shader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(state.rain_vert_shader, rain_vert_shader_source); gl.compileShader(state.rain_vert_shader);
|
||||
|
||||
state.bloom_high_pass_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(state.bloom_high_pass_shader, bloom_high_pass_shader_source); gl.compileShader(state.bloom_high_pass_shader);
|
||||
|
||||
state.bloom_blur_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(state.bloom_blur_shader, bloom_blur_shader_source); gl.compileShader(state.bloom_blur_shader);
|
||||
|
||||
state.bloom_combine_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(state.bloom_combine_shader, bloom_combine_shader_source); gl.compileShader(state.bloom_combine_shader);
|
||||
|
||||
state.palette_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(state.palette_shader, palette_shader_source); gl.compileShader(state.palette_shader);
|
||||
|
||||
state.fullscreen_program = gl.createProgram(); gl.attachShader(state.fullscreen_program, state.fullscreen_vert_shader); gl.attachShader(state.fullscreen_program, state.fullscreen_frag_shader); gl.linkProgram(state.fullscreen_program);
|
||||
state.rain_program_u_tex = gl.getUniformLocation(state.fullscreen_program, "tex");
|
||||
state.fullscreen_program_a_aPosition = gl.getAttribLocation(state.fullscreen_program, "aPosition");
|
||||
|
||||
state.rain_compute_program = gl.createProgram(); gl.attachShader(state.rain_compute_program, state.fullscreen_vert_shader); gl.attachShader(state.rain_compute_program, state.rain_compute_shader); gl.linkProgram(state.rain_compute_program);
|
||||
state.rain_compute_program_a_aPosition = gl.getAttribLocation(state.rain_compute_program, "aPosition");
|
||||
state.rain_compute_program_u_numColumns = gl.getUniformLocation(state.rain_compute_program, "numColumns");
|
||||
state.rain_compute_program_u_glyphSequenceLength = gl.getUniformLocation(state.rain_compute_program, "glyphSequenceLength");
|
||||
state.rain_compute_program_u_numRows = gl.getUniformLocation(state.rain_compute_program, "numRows");
|
||||
state.rain_compute_program_u_fallSpeed = gl.getUniformLocation(state.rain_compute_program, "fallSpeed");
|
||||
state.rain_compute_program_u_time = gl.getUniformLocation(state.rain_compute_program, "time");
|
||||
state.rain_compute_program_u_raindropLength = gl.getUniformLocation(state.rain_compute_program, "raindropLength");
|
||||
state.rain_compute_program_u_previousComputeState = gl.getUniformLocation(state.rain_compute_program, "previousComputeState");
|
||||
state.rain_compute_program_u_tick = gl.getUniformLocation(state.rain_compute_program, "tick");
|
||||
state.rain_compute_program_u_cycleSpeed = gl.getUniformLocation(state.rain_compute_program, "cycleSpeed");
|
||||
|
||||
gl.useProgram(state.rain_compute_program);
|
||||
gl.uniform1f(state.rain_compute_program_u_numColumns, 80);
|
||||
gl.uniform1f(state.rain_compute_program_u_glyphSequenceLength, 57);
|
||||
gl.uniform1f(state.rain_compute_program_u_numRows, 80);
|
||||
gl.uniform1f(state.rain_compute_program_u_fallSpeed, 0.3);
|
||||
gl.uniform1f(state.rain_compute_program_u_raindropLength, 0.75);
|
||||
gl.uniform1f(state.rain_compute_program_u_cycleSpeed, 0.03);
|
||||
|
||||
state.rain_program = gl.createProgram(); gl.attachShader(state.rain_program, state.rain_vert_shader); gl.attachShader(state.rain_program, state.rain_frag_shader); gl.linkProgram(state.rain_program);
|
||||
state.rain_program_a_aPosition = gl.getAttribLocation(state.rain_program, "aPosition");
|
||||
state.rain_program_u_glyphTextureGridSize = gl.getUniformLocation(state.rain_program, "glyphTextureGridSize");
|
||||
state.rain_program_u_numColumns = gl.getUniformLocation(state.rain_program, "numColumns");
|
||||
state.rain_program_u_glyphMSDFSize = gl.getUniformLocation(state.rain_program, "glyphMSDFSize");
|
||||
state.rain_program_u_numRows = gl.getUniformLocation(state.rain_program, "numRows");
|
||||
state.rain_program_u_msdfPxRange = gl.getUniformLocation(state.rain_program, "msdfPxRange");
|
||||
state.rain_program_u_screenSize = gl.getUniformLocation(state.rain_program, "screenSize");
|
||||
state.rain_program_u_computeState = gl.getUniformLocation(state.rain_program, "computeState");
|
||||
state.rain_program_u_glyphMSDF = gl.getUniformLocation(state.rain_program, "glyphMSDF");
|
||||
|
||||
gl.useProgram(state.rain_program);
|
||||
gl.uniform2f(state.rain_program_u_glyphTextureGridSize, 8, 8);
|
||||
gl.uniform1f(state.rain_program_u_numColumns, 80);
|
||||
gl.uniform2f(state.rain_program_u_glyphMSDFSize, 512, 512);
|
||||
gl.uniform1f(state.rain_program_u_numRows, 80);
|
||||
gl.uniform1f(state.rain_program_u_msdfPxRange, 4);
|
||||
|
||||
state.bloom_high_pass_program = gl.createProgram(); gl.attachShader(state.bloom_high_pass_program, state.fullscreen_vert_shader); gl.attachShader(state.bloom_high_pass_program, state.bloom_high_pass_shader); gl.linkProgram(state.bloom_high_pass_program);
|
||||
state.bloom_high_pass_program_a_aPosition = gl.getAttribLocation(state.bloom_high_pass_program, "aPosition");
|
||||
state.bloom_high_pass_program_u_tex = gl.getUniformLocation(state.bloom_high_pass_program, "tex");
|
||||
state.bloom_high_pass_program_u_highPassThreshold = gl.getUniformLocation(state.bloom_high_pass_program, "highPassThreshold");
|
||||
|
||||
gl.useProgram(state.bloom_high_pass_program);
|
||||
gl.uniform1f(state.bloom_high_pass_program_u_highPassThreshold, 0.1);
|
||||
|
||||
state.bloom_blur_program = gl.createProgram(); gl.attachShader(state.bloom_blur_program, state.fullscreen_vert_shader); gl.attachShader(state.bloom_blur_program, state.bloom_blur_shader); gl.linkProgram(state.bloom_blur_program);
|
||||
state.bloom_blur_program_a_aPosition = gl.getAttribLocation(state.bloom_blur_program, "aPosition");
|
||||
state.bloom_blur_program_u_tex = gl.getUniformLocation(state.bloom_blur_program, "tex");
|
||||
state.bloom_blur_program_u_width = gl.getUniformLocation(state.bloom_blur_program, "width");
|
||||
state.bloom_blur_program_u_height = gl.getUniformLocation(state.bloom_blur_program, "height");
|
||||
state.bloom_blur_program_u_direction = gl.getUniformLocation(state.bloom_blur_program, "direction");
|
||||
|
||||
state.bloom_combine_program = gl.createProgram(); gl.attachShader(state.bloom_combine_program, state.fullscreen_vert_shader); gl.attachShader(state.bloom_combine_program, state.bloom_combine_shader); gl.linkProgram(state.bloom_combine_program);
|
||||
state.bloom_combine_program_a_aPosition = gl.getAttribLocation(state.bloom_combine_program, "aPosition");
|
||||
state.bloom_combine_program_u_pyr_0 = gl.getUniformLocation(state.bloom_combine_program, "pyr_0");
|
||||
state.bloom_combine_program_u_pyr_1 = gl.getUniformLocation(state.bloom_combine_program, "pyr_1");
|
||||
state.bloom_combine_program_u_pyr_2 = gl.getUniformLocation(state.bloom_combine_program, "pyr_2");
|
||||
state.bloom_combine_program_u_pyr_3 = gl.getUniformLocation(state.bloom_combine_program, "pyr_3");
|
||||
state.bloom_combine_program_u_pyr_4 = gl.getUniformLocation(state.bloom_combine_program, "pyr_4");
|
||||
state.bloom_combine_program_u_bloomStrength = gl.getUniformLocation(state.bloom_combine_program, "bloomStrength");
|
||||
|
||||
state.palette_program = gl.createProgram(); gl.attachShader(state.palette_program, state.fullscreen_vert_shader); gl.attachShader(state.palette_program, state.palette_shader); gl.linkProgram(state.palette_program);
|
||||
state.palette_program_a_aPosition = gl.getAttribLocation(state.palette_program, "aPosition");
|
||||
state.palette_program_u_tex = gl.getUniformLocation(state.palette_program, "tex");
|
||||
state.palette_program_u_bloomTex = gl.getUniformLocation(state.palette_program, "bloomTex");
|
||||
state.palette_program_u_time = gl.getUniformLocation(state.palette_program, "time");
|
||||
state.palette_program_u_paletteTex = gl.getUniformLocation(state.palette_program, "paletteTex");
|
||||
|
||||
state.rain_geometry = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.rain_geometry);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, Float32Array.from([0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0]), gl.STATIC_DRAW);
|
||||
|
||||
state.fullscreen_geometry = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.fullscreen_geometry);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, Float32Array.from([-4, -4, 4, -4, 0, 4]), gl.STATIC_DRAW);
|
||||
|
||||
textures.rain_compute_doublebuffer_1 = gl.createTexture();
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.rain_compute_doublebuffer_1);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 80, 80, 0, gl.RGBA, extendedContext.HALF_FLOAT_OES, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
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);
|
||||
|
||||
state.rain_compute_doublebuffer_1_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.rain_compute_doublebuffer_1_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.rain_compute_doublebuffer_1, 0);
|
||||
|
||||
textures.rain_compute_doublebuffer_2 = gl.createTexture();
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.rain_compute_doublebuffer_2);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 80, 80, 0, gl.RGBA, extendedContext.HALF_FLOAT_OES, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
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);
|
||||
|
||||
state.rain_compute_doublebuffer_2_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.rain_compute_doublebuffer_2_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.rain_compute_doublebuffer_2, 0);
|
||||
|
||||
textures.rain_output = gl.createTexture();
|
||||
textureSizes.rain_output = {scale: 1};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.rain_output);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.rain_output_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.rain_output_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.rain_output, 0);
|
||||
|
||||
const bloomSize = 0.4;
|
||||
|
||||
textures.bloom_high_pass_pyr_0 = gl.createTexture();
|
||||
textureSizes.bloom_high_pass_pyr_0 = {scale: bloomSize / (2 ** 0)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_0);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_high_pass_pyr_0_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_0_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_high_pass_pyr_0, 0);
|
||||
|
||||
textures.bloom_high_pass_pyr_1 = gl.createTexture();
|
||||
textureSizes.bloom_high_pass_pyr_1 = {scale: bloomSize / (2 ** 1)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_1);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_high_pass_pyr_1_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_1_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_high_pass_pyr_1, 0);
|
||||
|
||||
textures.bloom_high_pass_pyr_2 = gl.createTexture();
|
||||
textureSizes.bloom_high_pass_pyr_2 = {scale: bloomSize / (2 ** 2)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_2);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_high_pass_pyr_2_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_2_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_high_pass_pyr_2, 0);
|
||||
|
||||
textures.bloom_high_pass_pyr_3 = gl.createTexture();
|
||||
textureSizes.bloom_high_pass_pyr_3 = {scale: bloomSize / (2 ** 3)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_3);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_high_pass_pyr_3_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_3_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_high_pass_pyr_3, 0);
|
||||
|
||||
textures.bloom_high_pass_pyr_4 = gl.createTexture();
|
||||
textureSizes.bloom_high_pass_pyr_4 = {scale: bloomSize / (2 ** 4)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_4);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_high_pass_pyr_4_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_4_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_high_pass_pyr_4, 0);
|
||||
|
||||
textures.bloom_h_blur_pyr_0 = gl.createTexture();
|
||||
textureSizes.bloom_h_blur_pyr_0 = {scale: bloomSize / (2 ** 0)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_0);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_h_blur_pyr_0_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_0_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_h_blur_pyr_0, 0);
|
||||
|
||||
textures.bloom_h_blur_pyr_1 = gl.createTexture();
|
||||
textureSizes.bloom_h_blur_pyr_1 = {scale: bloomSize / (2 ** 1)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_1);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_h_blur_pyr_1_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_1_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_h_blur_pyr_1, 0);
|
||||
|
||||
textures.bloom_h_blur_pyr_2 = gl.createTexture();
|
||||
textureSizes.bloom_h_blur_pyr_2 = {scale: bloomSize / (2 ** 2)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_2);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_h_blur_pyr_2_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_2_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_h_blur_pyr_2, 0);
|
||||
|
||||
textures.bloom_h_blur_pyr_3 = gl.createTexture();
|
||||
textureSizes.bloom_h_blur_pyr_3 = {scale: bloomSize / (2 ** 3)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_3);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_h_blur_pyr_3_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_3_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_h_blur_pyr_3, 0);
|
||||
|
||||
textures.bloom_h_blur_pyr_4 = gl.createTexture();
|
||||
textureSizes.bloom_h_blur_pyr_4 = {scale: bloomSize / (2 ** 4)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_4);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_h_blur_pyr_4_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_4_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_h_blur_pyr_4, 0);
|
||||
|
||||
textures.bloom_v_blur_pyr_0 = gl.createTexture();
|
||||
textureSizes.bloom_v_blur_pyr_0 = {scale: bloomSize / (2 ** 0)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_0);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_v_blur_pyr_0_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_0_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_v_blur_pyr_0, 0);
|
||||
|
||||
textures.bloom_v_blur_pyr_1 = gl.createTexture();
|
||||
textureSizes.bloom_v_blur_pyr_1 = {scale: bloomSize / (2 ** 1)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_1);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_v_blur_pyr_1_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_1_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_v_blur_pyr_1, 0);
|
||||
|
||||
textures.bloom_v_blur_pyr_2 = gl.createTexture();
|
||||
textureSizes.bloom_v_blur_pyr_2 = {scale: bloomSize / (2 ** 2)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_2);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_v_blur_pyr_2_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_2_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_v_blur_pyr_2, 0);
|
||||
|
||||
textures.bloom_v_blur_pyr_3 = gl.createTexture();
|
||||
textureSizes.bloom_v_blur_pyr_3 = {scale: bloomSize / (2 ** 3)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_3);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_v_blur_pyr_3_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_3_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_v_blur_pyr_3, 0);
|
||||
|
||||
textures.bloom_v_blur_pyr_4 = gl.createTexture();
|
||||
textureSizes.bloom_v_blur_pyr_4 = {scale: bloomSize / (2 ** 4)};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_4);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_v_blur_pyr_4_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_4_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_v_blur_pyr_4, 0);
|
||||
|
||||
textures.bloom_output = gl.createTexture();
|
||||
textureSizes.bloom_output = {scale: 1};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.bloom_output);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.bloom_output_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_output_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.bloom_output, 0);
|
||||
|
||||
textures.palette_output = gl.createTexture();
|
||||
textureSizes.palette_output = {scale: 1};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.palette_output);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
state.palette_output_framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.palette_output_framebuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures.palette_output, 0);
|
||||
|
||||
textures.palette = gl.createTexture();
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.palette);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, Uint8ClampedArray.from(palette));
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
|
||||
textures.msdf = gl.createTexture();
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures.msdf);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, msdfImage);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
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);
|
||||
};
|
||||
|
||||
const resize = (gl, width, height) => {
|
||||
|
||||
textureSizes.fullscreen.width = width;
|
||||
textureSizes.fullscreen.height = height;
|
||||
|
||||
for (var name in textures) {
|
||||
const size = textureSizes[name];
|
||||
if (size == null) {
|
||||
continue;
|
||||
}
|
||||
size.width = Math.floor(width * size.scale);
|
||||
size.height = Math.floor(height * size.scale);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, textures[name]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size.width, size.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
}
|
||||
};
|
||||
|
||||
const draw = (gl, tick, time) => {
|
||||
|
||||
const flip = tick % 2 == 0;
|
||||
const doubleBufferFrontFBO = flip ? state.rain_compute_doublebuffer_2_framebuffer : state.rain_compute_doublebuffer_1_framebuffer;
|
||||
const doubleBufferFrontTex = flip ? textures.rain_compute_doublebuffer_2 : textures.rain_compute_doublebuffer_1;
|
||||
const doubleBufferBackTex = flip ? textures.rain_compute_doublebuffer_1 : textures.rain_compute_doublebuffer_2;
|
||||
let size;
|
||||
|
||||
gl.enableVertexAttribArray(0);
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
gl.blendFuncSeparate(1, 1, 1, 1);
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
|
||||
// rain compute
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, doubleBufferFrontFBO);
|
||||
gl.viewport(0, 0, 80, 80);
|
||||
gl.useProgram(state.rain_compute_program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.fullscreen_geometry); gl.vertexAttribPointer(state.rain_compute_program_a_aPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, doubleBufferBackTex); gl.uniform1i(state.rain_compute_program_u_previousComputeState, 0);
|
||||
gl.uniform1f(state.rain_compute_program_u_time, time);
|
||||
gl.uniform1f(state.rain_compute_program_u_tick, tick);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
// rain
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.rain_output_framebuffer);
|
||||
size = textureSizes.rain_output; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.enable(gl.BLEND);
|
||||
gl.useProgram(state.rain_program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.rain_geometry); gl.vertexAttribPointer(state.rain_program_a_aPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
size = textureSizes.fullscreen;
|
||||
const aspectRatio = size.width / size.height;
|
||||
const screenSize = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
|
||||
|
||||
gl.uniform2f(state.rain_program_u_screenSize, screenSize[0], screenSize[1]);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, doubleBufferFrontTex); gl.uniform1i(state.rain_program_u_computeState, 0);
|
||||
gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, textures.msdf); gl.uniform1i(state.rain_program_u_glyphMSDF, 1);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
gl.disable(gl.BLEND);
|
||||
|
||||
// high pass pyramid
|
||||
gl.useProgram(state.bloom_high_pass_program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.fullscreen_geometry); gl.vertexAttribPointer(state.bloom_high_pass_program_a_aPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_0_framebuffer);
|
||||
size = textureSizes.bloom_high_pass_pyr_0; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.rain_output); gl.uniform1i(state.bloom_high_pass_program_u_tex, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_1_framebuffer);
|
||||
size = textureSizes.bloom_high_pass_pyr_1; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_0); gl.uniform1i(state.bloom_high_pass_program_u_tex, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_2_framebuffer);
|
||||
size = textureSizes.bloom_high_pass_pyr_2; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_1); gl.uniform1i(state.bloom_high_pass_program_u_tex, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_3_framebuffer);
|
||||
size = textureSizes.bloom_high_pass_pyr_3; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_2); gl.uniform1i(state.bloom_high_pass_program_u_tex, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_high_pass_pyr_4_framebuffer);
|
||||
size = textureSizes.bloom_high_pass_pyr_4; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_3); gl.uniform1i(state.bloom_high_pass_program_u_tex, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
// blur pyramids
|
||||
gl.useProgram(state.bloom_blur_program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.fullscreen_geometry); gl.vertexAttribPointer(state.bloom_blur_program_a_aPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_0_framebuffer);
|
||||
size = textureSizes.bloom_h_blur_pyr_0; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.uniform1f(state.bloom_blur_program_u_width, size.width); gl.uniform1f(state.bloom_blur_program_u_height, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_0); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 1, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_0_framebuffer);
|
||||
size = textureSizes.bloom_v_blur_pyr_0; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_0); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 0, 1);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_1_framebuffer);
|
||||
size = textureSizes.bloom_h_blur_pyr_1; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.uniform1f(state.bloom_blur_program_u_width, size.width); gl.uniform1f(state.bloom_blur_program_u_height, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_1); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 1, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_1_framebuffer);
|
||||
size = textureSizes.bloom_v_blur_pyr_1; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_1); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 0, 1);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_2_framebuffer);
|
||||
size = textureSizes.bloom_h_blur_pyr_2; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.uniform1f(state.bloom_blur_program_u_width, size.width); gl.uniform1f(state.bloom_blur_program_u_height, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_2); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 1, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_2_framebuffer);
|
||||
size = textureSizes.bloom_v_blur_pyr_2; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_2); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 0, 1);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_3_framebuffer);
|
||||
size = textureSizes.bloom_h_blur_pyr_3; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.uniform1f(state.bloom_blur_program_u_width, size.width); gl.uniform1f(state.bloom_blur_program_u_height, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_3); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 1, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_3_framebuffer);
|
||||
size = textureSizes.bloom_v_blur_pyr_3; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_3); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 0, 1);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_h_blur_pyr_4_framebuffer);
|
||||
size = textureSizes.bloom_h_blur_pyr_4; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.uniform1f(state.bloom_blur_program_u_width, size.width); gl.uniform1f(state.bloom_blur_program_u_height, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_high_pass_pyr_4); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 1, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_v_blur_pyr_4_framebuffer);
|
||||
size = textureSizes.bloom_v_blur_pyr_4; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_h_blur_pyr_4); gl.uniform1i(state.bloom_blur_program_u_tex, 0);
|
||||
gl.uniform2f(state.bloom_blur_program_u_direction, 0, 1);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
// bloom combine
|
||||
gl.useProgram(state.bloom_combine_program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.fullscreen_geometry); gl.vertexAttribPointer(state.bloom_combine_program_a_aPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.bloom_output_framebuffer);
|
||||
size = textureSizes.bloom_output; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_0); gl.uniform1i(state.bloom_combine_program_u_pyr_0, 0);
|
||||
gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_1); gl.uniform1i(state.bloom_combine_program_u_pyr_1, 1);
|
||||
gl.activeTexture(gl.TEXTURE2); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_2); gl.uniform1i(state.bloom_combine_program_u_pyr_2, 2);
|
||||
gl.activeTexture(gl.TEXTURE3); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_3); gl.uniform1i(state.bloom_combine_program_u_pyr_3, 3);
|
||||
gl.activeTexture(gl.TEXTURE4); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_v_blur_pyr_4); gl.uniform1i(state.bloom_combine_program_u_pyr_4, 4);
|
||||
gl.uniform1f(state.bloom_combine_program_u_bloomStrength, 0.7);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
// palette
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, state.palette_output_framebuffer);
|
||||
size = textureSizes.palette_output; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.useProgram(state.palette_program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.fullscreen_geometry); gl.vertexAttribPointer(state.palette_program_a_aPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.rain_output); gl.uniform1i(state.palette_program_u_tex, 0);
|
||||
gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, textures.bloom_output); gl.uniform1i(state.palette_program_u_bloomTex, 1);
|
||||
gl.uniform1f(state.palette_program_u_time, 0);
|
||||
gl.activeTexture(gl.TEXTURE2); gl.bindTexture(gl.TEXTURE_2D, textures.palette); gl.uniform1i(state.palette_program_u_paletteTex, 2);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
// upscale
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
size = textureSizes.fullscreen; gl.viewport(0, 0, size.width, size.height);
|
||||
gl.useProgram(state.fullscreen_program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, state.fullscreen_geometry); gl.vertexAttribPointer(state.fullscreen_program_a_aPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures.palette_output); gl.uniform1i(state.fullscreen_program_u_tex, 0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
};
|
||||
|
||||
export {
|
||||
init, load, resize, draw
|
||||
};
|
||||
128
js/utils.js
128
js/utils.js
@@ -1,128 +0,0 @@
|
||||
const makePassTexture = (regl, halfFloat) =>
|
||||
regl.texture({
|
||||
width: 1,
|
||||
height: 1,
|
||||
type: halfFloat ? "half float" : "uint8",
|
||||
wrap: "clamp",
|
||||
min: "linear",
|
||||
mag: "linear",
|
||||
});
|
||||
|
||||
const makePassFBO = (regl, halfFloat) => regl.framebuffer({ color: makePassTexture(regl, halfFloat) });
|
||||
|
||||
const makeDoubleBuffer = (regl, props) => {
|
||||
const state = Array(2)
|
||||
.fill()
|
||||
.map(() =>
|
||||
regl.framebuffer({
|
||||
color: regl.texture(props),
|
||||
depthStencil: false,
|
||||
})
|
||||
);
|
||||
return {
|
||||
front: ({ tick }) => state[tick % 2],
|
||||
back: ({ tick }) => state[(tick + 1) % 2],
|
||||
};
|
||||
};
|
||||
|
||||
const isPowerOfTwo = (x) => Math.log2(x) % 1 == 0;
|
||||
|
||||
const loadImage = (regl, url) => {
|
||||
let texture = regl.texture([[0]]);
|
||||
let loaded = false;
|
||||
return {
|
||||
texture: () => texture,
|
||||
width: () => (loaded ? texture.width : 1),
|
||||
height: () => (loaded ? texture.height : 1),
|
||||
loaded: (async () => {
|
||||
if (url != null) {
|
||||
const data = new Image();
|
||||
data.crossOrigin = "anonymous";
|
||||
data.src = url;
|
||||
await data.decode();
|
||||
loaded = true;
|
||||
texture = regl.texture({
|
||||
data,
|
||||
mag: "linear",
|
||||
min: "linear",
|
||||
flipY: true,
|
||||
});
|
||||
}
|
||||
})(),
|
||||
};
|
||||
};
|
||||
|
||||
const loadText = (url) => {
|
||||
let text = "";
|
||||
let loaded = false;
|
||||
return {
|
||||
text: () => text,
|
||||
loaded: (async () => {
|
||||
if (url != null) {
|
||||
text = await (await fetch(url)).text();
|
||||
loaded = true;
|
||||
}
|
||||
})(),
|
||||
};
|
||||
};
|
||||
|
||||
const makeFullScreenQuad = (regl, uniforms = {}, context = {}) =>
|
||||
regl({
|
||||
vert: `
|
||||
precision mediump float;
|
||||
attribute vec2 aPosition;
|
||||
varying vec2 vUV;
|
||||
void main() {
|
||||
vUV = 0.5 * (aPosition + 1.0);
|
||||
gl_Position = vec4(aPosition, 0, 1);
|
||||
}
|
||||
`,
|
||||
|
||||
frag: `
|
||||
precision mediump float;
|
||||
varying vec2 vUV;
|
||||
uniform sampler2D tex;
|
||||
void main() {
|
||||
gl_FragColor = texture2D(tex, vUV);
|
||||
}
|
||||
`,
|
||||
|
||||
attributes: {
|
||||
aPosition: [-4, -4, 4, -4, 0, 4],
|
||||
},
|
||||
count: 3,
|
||||
|
||||
uniforms: {
|
||||
...uniforms,
|
||||
time: regl.context("time"),
|
||||
tick: regl.context("tick"),
|
||||
},
|
||||
|
||||
context,
|
||||
|
||||
depth: { enable: false },
|
||||
});
|
||||
|
||||
const make1DTexture = (regl, rgbas) => {
|
||||
const data = rgbas.map((rgba) => rgba.map((f) => Math.floor(f * 0xff))).flat();
|
||||
return regl.texture({
|
||||
data,
|
||||
width: data.length / 4,
|
||||
height: 1,
|
||||
format: "rgba",
|
||||
mag: "linear",
|
||||
min: "linear",
|
||||
});
|
||||
};
|
||||
|
||||
const makePass = (outputs, ready, setSize, execute) => ({
|
||||
outputs: outputs ?? {},
|
||||
ready: ready ?? Promise.resolve(),
|
||||
setSize: setSize ?? (() => {}),
|
||||
execute: execute ?? (() => {}),
|
||||
});
|
||||
|
||||
const makePipeline = (context, steps) =>
|
||||
steps.filter((f) => f != null).reduce((pipeline, f, i) => [...pipeline, f(context, i == 0 ? null : pipeline[i - 1].outputs)], []);
|
||||
|
||||
export { makePassTexture, makePassFBO, makeDoubleBuffer, loadImage, loadText, makeFullScreenQuad, make1DTexture, makePass, makePipeline };
|
||||
10527
lib/regl.js
10527
lib/regl.js
File diff suppressed because it is too large
Load Diff
1195
lib/webgl-debug.js
1195
lib/webgl-debug.js
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user