Adding comments, and other small adjustments

This commit is contained in:
Rezmason
2020-01-21 12:28:08 -08:00
parent a760d6854a
commit 67636db908
5 changed files with 64 additions and 11 deletions

View File

@@ -22,7 +22,28 @@
<body>
<script src="lib/regl.min.js"></script>
<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 makeMatrixRenderer from "./js/renderer.js";
import makeBloomPass from "./js/bloomPass.js";
@@ -37,6 +58,7 @@
const regl = createREGL({
canvas,
extensions: ["OES_texture_half_float", "OES_texture_half_float_linear"],
// These extensions are also needed, but Safari misreports that they are missing
optionalExtensions: [
"EXT_color_buffer_half_float",
"WEBGL_color_buffer_float",
@@ -45,16 +67,10 @@
});
const [config, uniforms] = makeConfig(window.location.search, data =>
regl.texture({
data,
width: data.length / 3,
height: 1,
format: "rgb",
mag: "linear",
min: "linear"
})
makePalette(regl, data)
);
// All this takes place in a full screen quad.
const fullScreenQuad = makeFullScreenQuad(regl, uniforms);
const renderer = makeMatrixRenderer(regl, config);
const bloomPass = makeBloomPass(regl, config, renderer.fbo);
@@ -82,10 +98,12 @@
});
const loop = regl.frame(({ viewportWidth, viewportHeight }) => {
// All the FBOs except the compute FBOs need to be sized to the window.
renderer.resize(viewportWidth, viewportHeight);
bloomPass.resize(viewportWidth, viewportHeight);
colorPass.resize(viewportWidth, viewportHeight);
// And here is the full draw sequence.
fullScreenQuad(() => {
renderer.update();
renderer.render(resources);

View File

@@ -1,5 +1,7 @@
import { makePassFBO, makePyramid, resizePyramid } from "./utils.js";
// The bloom pass is basically an added high-pass blur.
const pyramidHeight = 5;
const levelStrengths = Array(pyramidHeight)
.fill()
@@ -22,6 +24,7 @@ export default (regl, config, input) => {
const verticalBlurPyramid = makePyramid(regl, pyramidHeight);
const fbo = makePassFBO(regl);
// The high pass restricts the blur to bright things in our input texture.
const highPass = regl({
frag: `
precision mediump float;
@@ -42,6 +45,9 @@ export default (regl, config, input) => {
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({
frag: `
precision mediump float;
@@ -68,6 +74,7 @@ export default (regl, config, input) => {
framebuffer: regl.prop("fbo")
});
// The pyramid of textures gets flattened onto the source texture.
const combineBloom = regl({
frag: `
precision mediump float;
@@ -102,6 +109,7 @@ export default (regl, config, input) => {
return {
fbo,
resize: (viewportWidth, viewportHeight) => {
// The blur pyramids can be lower resolution than the screen.
resizePyramid(
highPassPyramid,
viewportWidth,

View File

@@ -1,5 +1,11 @@
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 =>
regl({
frag: `

View File

@@ -1,6 +1,13 @@
import { makePassFBO } from "./utils.js";
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)
.fill()
.map(() =>
@@ -191,9 +198,10 @@ export default (regl, config) => {
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({
vert: `
attribute vec2 aPosition;

View File

@@ -10,6 +10,8 @@ const 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) =>
Array(height)
.fill()
@@ -80,6 +82,16 @@ const makeFullScreenQuad = (regl, uniforms) =>
count: 3
});
const makePalette = (regl, data) =>
regl.texture({
data,
width: data.length / 3,
height: 1,
format: "rgb",
mag: "linear",
min: "linear"
});
export {
makePassTexture,
makePassFBO,
@@ -87,5 +99,6 @@ export {
resizePyramid,
loadImage,
loadImages,
makeFullScreenQuad
makeFullScreenQuad,
makePalette
};