mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-17 22:09:28 -07:00
Adding comments, and other small adjustments
This commit is contained in:
36
index.html
36
index.html
@@ -22,7 +22,28 @@
|
|||||||
<body>
|
<body>
|
||||||
<script src="lib/regl.min.js"></script>
|
<script src="lib/regl.min.js"></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { loadImages, makeFullScreenQuad } from "./js/utils.js";
|
/*
|
||||||
|
|
||||||
|
This is an implementation of the green code seen in The Matrix film and video game franchise.
|
||||||
|
This project demonstrates five concepts:
|
||||||
|
1. Drawing to floating point frame buffer objects, or 'FBO's,
|
||||||
|
for performing computation and post-processing
|
||||||
|
2. GPU-side computation, with a fragment shader
|
||||||
|
updating two alternating FBOs
|
||||||
|
3. Rendering crisp "vector" graphics, with a multiple-channel
|
||||||
|
signed distance field (or 'MSDF')
|
||||||
|
4. Creating a blur/bloom effect from a texture pyramid
|
||||||
|
5. Color mapping with noise, to hide banding
|
||||||
|
|
||||||
|
For more information, please visit: https://github.com/Rezmason/matrix
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
loadImages,
|
||||||
|
makeFullScreenQuad,
|
||||||
|
makePalette
|
||||||
|
} from "./js/utils.js";
|
||||||
import makeConfig from "./js/config.js";
|
import makeConfig from "./js/config.js";
|
||||||
import makeMatrixRenderer from "./js/renderer.js";
|
import makeMatrixRenderer from "./js/renderer.js";
|
||||||
import makeBloomPass from "./js/bloomPass.js";
|
import makeBloomPass from "./js/bloomPass.js";
|
||||||
@@ -37,6 +58,7 @@
|
|||||||
const regl = createREGL({
|
const regl = createREGL({
|
||||||
canvas,
|
canvas,
|
||||||
extensions: ["OES_texture_half_float", "OES_texture_half_float_linear"],
|
extensions: ["OES_texture_half_float", "OES_texture_half_float_linear"],
|
||||||
|
// These extensions are also needed, but Safari misreports that they are missing
|
||||||
optionalExtensions: [
|
optionalExtensions: [
|
||||||
"EXT_color_buffer_half_float",
|
"EXT_color_buffer_half_float",
|
||||||
"WEBGL_color_buffer_float",
|
"WEBGL_color_buffer_float",
|
||||||
@@ -45,16 +67,10 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [config, uniforms] = makeConfig(window.location.search, data =>
|
const [config, uniforms] = makeConfig(window.location.search, data =>
|
||||||
regl.texture({
|
makePalette(regl, data)
|
||||||
data,
|
|
||||||
width: data.length / 3,
|
|
||||||
height: 1,
|
|
||||||
format: "rgb",
|
|
||||||
mag: "linear",
|
|
||||||
min: "linear"
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// All this takes place in a full screen quad.
|
||||||
const fullScreenQuad = makeFullScreenQuad(regl, uniforms);
|
const fullScreenQuad = makeFullScreenQuad(regl, uniforms);
|
||||||
const renderer = makeMatrixRenderer(regl, config);
|
const renderer = makeMatrixRenderer(regl, config);
|
||||||
const bloomPass = makeBloomPass(regl, config, renderer.fbo);
|
const bloomPass = makeBloomPass(regl, config, renderer.fbo);
|
||||||
@@ -82,10 +98,12 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const loop = regl.frame(({ viewportWidth, viewportHeight }) => {
|
const loop = regl.frame(({ viewportWidth, viewportHeight }) => {
|
||||||
|
// All the FBOs except the compute FBOs need to be sized to the window.
|
||||||
renderer.resize(viewportWidth, viewportHeight);
|
renderer.resize(viewportWidth, viewportHeight);
|
||||||
bloomPass.resize(viewportWidth, viewportHeight);
|
bloomPass.resize(viewportWidth, viewportHeight);
|
||||||
colorPass.resize(viewportWidth, viewportHeight);
|
colorPass.resize(viewportWidth, viewportHeight);
|
||||||
|
|
||||||
|
// And here is the full draw sequence.
|
||||||
fullScreenQuad(() => {
|
fullScreenQuad(() => {
|
||||||
renderer.update();
|
renderer.update();
|
||||||
renderer.render(resources);
|
renderer.render(resources);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { makePassFBO, makePyramid, resizePyramid } from "./utils.js";
|
import { makePassFBO, makePyramid, resizePyramid } from "./utils.js";
|
||||||
|
|
||||||
|
// The bloom pass is basically an added high-pass blur.
|
||||||
|
|
||||||
const pyramidHeight = 5;
|
const pyramidHeight = 5;
|
||||||
const levelStrengths = Array(pyramidHeight)
|
const levelStrengths = Array(pyramidHeight)
|
||||||
.fill()
|
.fill()
|
||||||
@@ -22,6 +24,7 @@ export default (regl, config, input) => {
|
|||||||
const verticalBlurPyramid = makePyramid(regl, pyramidHeight);
|
const verticalBlurPyramid = makePyramid(regl, pyramidHeight);
|
||||||
const fbo = makePassFBO(regl);
|
const fbo = makePassFBO(regl);
|
||||||
|
|
||||||
|
// The high pass restricts the blur to bright things in our input texture.
|
||||||
const highPass = regl({
|
const highPass = regl({
|
||||||
frag: `
|
frag: `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
@@ -42,6 +45,9 @@ export default (regl, config, input) => {
|
|||||||
framebuffer: regl.prop("fbo")
|
framebuffer: regl.prop("fbo")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// A 2D gaussian blur is just a 1D blur done horizontally, then done vertically.
|
||||||
|
// The FBO pyramid's levels represent separate levels of detail;
|
||||||
|
// by blurring them all, this 3x1 blur approximates a more complex gaussian.
|
||||||
const blur = regl({
|
const blur = regl({
|
||||||
frag: `
|
frag: `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
@@ -68,6 +74,7 @@ export default (regl, config, input) => {
|
|||||||
framebuffer: regl.prop("fbo")
|
framebuffer: regl.prop("fbo")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The pyramid of textures gets flattened onto the source texture.
|
||||||
const combineBloom = regl({
|
const combineBloom = regl({
|
||||||
frag: `
|
frag: `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
@@ -102,6 +109,7 @@ export default (regl, config, input) => {
|
|||||||
return {
|
return {
|
||||||
fbo,
|
fbo,
|
||||||
resize: (viewportWidth, viewportHeight) => {
|
resize: (viewportWidth, viewportHeight) => {
|
||||||
|
// The blur pyramids can be lower resolution than the screen.
|
||||||
resizePyramid(
|
resizePyramid(
|
||||||
highPassPyramid,
|
highPassPyramid,
|
||||||
viewportWidth,
|
viewportWidth,
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { makePassFBO } from "./utils.js";
|
import { makePassFBO } from "./utils.js";
|
||||||
|
|
||||||
|
// rendered texture's values are mapped to colors in a palette texture.
|
||||||
|
// A little noise is introduced, to hide the banding that appears
|
||||||
|
// in subtle gradients. The noise is also time-driven, so its grain
|
||||||
|
// won't persist across subsequent frames. This is a safe trick
|
||||||
|
// in screen space.
|
||||||
|
|
||||||
const colorizeByPalette = regl =>
|
const colorizeByPalette = regl =>
|
||||||
regl({
|
regl({
|
||||||
frag: `
|
frag: `
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { makePassFBO } from "./utils.js";
|
import { makePassFBO } from "./utils.js";
|
||||||
|
|
||||||
export default (regl, config) => {
|
export default (regl, config) => {
|
||||||
|
// These two framebuffers are used to compute the raining code.
|
||||||
|
// they take turns being the source and destination of the "compute" shader.
|
||||||
|
// The half float data type is crucial! It lets us store almost any real number,
|
||||||
|
// whereas the default type limits us to integers between 0 and 255.
|
||||||
|
|
||||||
|
// These FBOs are smaller than the screen, because their pixels correspond
|
||||||
|
// with glyphs in the final image, and the glyphs are much larger than a pixel.
|
||||||
const state = Array(2)
|
const state = Array(2)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() =>
|
.map(() =>
|
||||||
@@ -191,9 +198,10 @@ export default (regl, config) => {
|
|||||||
lastState: ({ tick }) => state[tick % 2]
|
lastState: ({ tick }) => state[tick % 2]
|
||||||
},
|
},
|
||||||
|
|
||||||
framebuffer: ({ tick }) => state[(tick + 1) % 2]
|
framebuffer: ({ tick }) => state[(tick + 1) % 2] // The crucial state FBO alternator
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
|
||||||
const render = regl({
|
const render = regl({
|
||||||
vert: `
|
vert: `
|
||||||
attribute vec2 aPosition;
|
attribute vec2 aPosition;
|
||||||
|
|||||||
15
js/utils.js
15
js/utils.js
@@ -10,6 +10,8 @@ const makePassTexture = regl =>
|
|||||||
|
|
||||||
const makePassFBO = regl => regl.framebuffer({ color: makePassTexture(regl) });
|
const makePassFBO = regl => regl.framebuffer({ color: makePassTexture(regl) });
|
||||||
|
|
||||||
|
// A pyramid is just an array of FBOs, where each FBO is half the width
|
||||||
|
// and half the height of the FBO below it.
|
||||||
const makePyramid = (regl, height) =>
|
const makePyramid = (regl, height) =>
|
||||||
Array(height)
|
Array(height)
|
||||||
.fill()
|
.fill()
|
||||||
@@ -80,6 +82,16 @@ const makeFullScreenQuad = (regl, uniforms) =>
|
|||||||
count: 3
|
count: 3
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const makePalette = (regl, data) =>
|
||||||
|
regl.texture({
|
||||||
|
data,
|
||||||
|
width: data.length / 3,
|
||||||
|
height: 1,
|
||||||
|
format: "rgb",
|
||||||
|
mag: "linear",
|
||||||
|
min: "linear"
|
||||||
|
});
|
||||||
|
|
||||||
export {
|
export {
|
||||||
makePassTexture,
|
makePassTexture,
|
||||||
makePassFBO,
|
makePassFBO,
|
||||||
@@ -87,5 +99,6 @@ export {
|
|||||||
resizePyramid,
|
resizePyramid,
|
||||||
loadImage,
|
loadImage,
|
||||||
loadImages,
|
loadImages,
|
||||||
makeFullScreenQuad
|
makeFullScreenQuad,
|
||||||
|
makePalette
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user