mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 04:19:29 -07:00
Adding glyphFlip and glyphRotation parameters.
This commit is contained in:
@@ -101,7 +101,7 @@ Some of the [earliest](https://github.com/ppetr/xlockmore/blob/master/modes/matr
|
||||
|
||||
You can customize the digital rain quite a bit by stapling "URL variables" to its URL— by putting a '?' at the end of the link above, and then chaining together words, like this:
|
||||
|
||||
[https://rezmason.github.io/matrix/?width=100&fallSpeed=-0.1&effect=none](https://rezmason.github.io/matrix/?width=100&fallSpeed=-0.1&effect=none)
|
||||
[https://rezmason.github.io/matrix/?numColumns=100&fallSpeed=-0.1&slant=200&glyphRotation=180](https://rezmason.github.io/matrix/?numColumns=100&fallSpeed=-0.1&slant=200&glyphRotation=180)
|
||||
|
||||
Now you know link fu. Here's a list of customization options:
|
||||
|
||||
@@ -116,7 +116,9 @@ Now you know link fu. Here's a list of customization options:
|
||||
- "palimpsest" is a custom version inspired by the art and sound of [Rob Dougan](https://en.wikipedia.org/wiki/Rob_Dougan)'s [Furious Angels](https://en.wikipedia.org/wiki/Furious_Angels).
|
||||
- `skipIntro` - whether or not to start from a blank screen. Can be "true" or "false", default is *true*.
|
||||
- `font` - the set of glyphs to draw. Current options are "matrixcode", "resurrections", "gothic", "coptic", "huberfishA", and "huberfishD".
|
||||
- `width` - the number of columns (and rows) to draw. Default is 80.
|
||||
- `numColumns` - the number of columns (and rows) to draw. Default is 80.
|
||||
- `glyphFlip` - when set to "true", this flips the glyphs. Default is "false".
|
||||
- `glyphRotation` - the angle to rotate the glyphs in-place, in degrees. Default is 0. I suggest angles that are multiples of 90°.
|
||||
- `volumetric` - when set to "true", this renders the glyphs with depth, slowly approaching the eye. Default is "false".
|
||||
- `density` - the number of 3D raindrops to draw, proportional to the default. Default is 1.0.
|
||||
- `forwardSpeed` - the rate that the 3D raindrops approach. Default is 1.0.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Matrix digital rain</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" /></meta>
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /></meta>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover" />
|
||||
<style>
|
||||
@supports (padding-top: env(safe-area-inset-top)) {
|
||||
|
||||
17
js/config.js
17
js/config.js
@@ -101,6 +101,8 @@ const defaults = {
|
||||
glyphEdgeCrop: 0.0, // The border around a glyph in a font texture that should be cropped out
|
||||
glyphHeightToWidth: 1, // The aspect ratio of glyphs
|
||||
glyphVerticalSpacing: 1, // The ratio of the vertical distance between glyphs to their height
|
||||
glyphFlip: false, // Whether to horizontally reflect the glyphs
|
||||
glyphRotation: 0, // An angle to rotate the glyphs. Currently limited to 90° increments
|
||||
hasThunder: false, // An effect that adds dramatic lightning flashes
|
||||
isPolar: false, // Whether the glyphs arc across the screen or sit in a standard grid
|
||||
rippleTypeName: null, // The variety of the ripple effect
|
||||
@@ -134,12 +136,12 @@ const versions = {
|
||||
megacity: {
|
||||
font: "megacity",
|
||||
animationSpeed: 0.5,
|
||||
width: 40,
|
||||
numColumns: 40,
|
||||
},
|
||||
neomatrixology: {
|
||||
font: "neomatrixology",
|
||||
animationSpeed: 0.8,
|
||||
width: 40,
|
||||
numColumns: 40,
|
||||
palette: [
|
||||
{ color: hsl(0.15, 0.9, 0.0), at: 0.0 },
|
||||
{ color: hsl(0.15, 0.9, 0.2), at: 0.2 },
|
||||
@@ -443,7 +445,6 @@ const paramMapping = {
|
||||
font: { key: "font", parser: (s) => s },
|
||||
effect: { key: "effect", parser: (s) => s },
|
||||
camera: { key: "useCamera", parser: isTrue },
|
||||
width: { key: "numColumns", parser: (s) => nullNaN(parseInt(s)) },
|
||||
numColumns: { key: "numColumns", parser: (s) => nullNaN(parseInt(s)) },
|
||||
density: { key: "density", parser: (s) => nullNaN(range(parseFloat(s), 0)) },
|
||||
resolution: { key: "resolution", parser: (s) => nullNaN(parseFloat(s)) },
|
||||
@@ -501,6 +502,11 @@ const paramMapping = {
|
||||
},
|
||||
|
||||
volumetric: { key: "volumetric", parser: isTrue },
|
||||
glyphFlip: { key: "glyphFlip", parser: isTrue },
|
||||
glyphRotation: {
|
||||
key: "glyphRotation",
|
||||
parser: (s) => nullNaN(range(parseFloat(s), 0, Infinity)),
|
||||
},
|
||||
loops: { key: "loops", parser: isTrue },
|
||||
fps: { key: "fps", parser: (s) => nullNaN(range(parseFloat(s), 0, 60)) },
|
||||
skipIntro: { key: "skipIntro", parser: isTrue },
|
||||
@@ -516,16 +522,17 @@ paramMapping.backgroundRGB = paramMapping.backgroundColor;
|
||||
paramMapping.cursorRGB = paramMapping.cursorColor;
|
||||
paramMapping.glintRGB = paramMapping.glintColor;
|
||||
|
||||
paramMapping.width = paramMapping.numColumns;
|
||||
paramMapping.dropLength = paramMapping.raindropLength;
|
||||
paramMapping.angle = paramMapping.slant;
|
||||
paramMapping.colors = paramMapping.stripeColors;
|
||||
|
||||
export default (urlParams) => {
|
||||
const validParams = Object.fromEntries(
|
||||
Array.from(Object.entries(urlParams))
|
||||
Object.entries(urlParams)
|
||||
.filter(([key]) => key in paramMapping)
|
||||
.map(([key, value]) => [paramMapping[key].key, paramMapping[key].parser(value)])
|
||||
.filter(([_, value]) => value != null)
|
||||
.filter(([_, value]) => value != null),
|
||||
);
|
||||
|
||||
if (validParams.effect != null) {
|
||||
|
||||
@@ -31,6 +31,8 @@ const brVert = [1, 1];
|
||||
const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert];
|
||||
|
||||
export default ({ regl, config, lkg }) => {
|
||||
const { mat2, mat4, vec2, vec3 } = glMatrix;
|
||||
|
||||
// The volumetric mode multiplies the number of columns
|
||||
// to reach the desired density, and then overlaps them
|
||||
const volumetric = config.volumetric;
|
||||
@@ -49,6 +51,9 @@ export default ({ regl, config, lkg }) => {
|
||||
const slantScale = 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
|
||||
const showDebugView = config.effect === "none";
|
||||
|
||||
const glyphTransform = mat2.fromScaling(mat2.create(), vec2.fromValues(config.glyphFlip ? -1 : 1, 1));
|
||||
mat2.rotate(glyphTransform, glyphTransform, (config.glyphRotation * Math.PI) / 180);
|
||||
|
||||
const commonUniforms = {
|
||||
...extractEntries(config, ["animationSpeed", "glyphHeightToWidth", "glyphSequenceLength", "glyphTextureGridSize"]),
|
||||
numColumns,
|
||||
@@ -160,6 +165,7 @@ export default ({ regl, config, lkg }) => {
|
||||
"glyphEdgeCrop",
|
||||
"isPolar",
|
||||
]),
|
||||
glyphTransform,
|
||||
density,
|
||||
numQuadColumns,
|
||||
numQuadRows,
|
||||
@@ -212,7 +218,6 @@ export default ({ regl, config, lkg }) => {
|
||||
|
||||
// Camera and transform math for the volumetric mode
|
||||
const screenSize = [1, 1];
|
||||
const { mat4, vec3 } = glMatrix;
|
||||
const transform = mat4.create();
|
||||
if (volumetric && config.isometric) {
|
||||
mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
|
||||
|
||||
@@ -8,7 +8,7 @@ const rippleTypes = {
|
||||
|
||||
const numVerticesPerQuad = 2 * 3;
|
||||
|
||||
const makeConfigBuffer = (device, configUniforms, config, density, gridSize) => {
|
||||
const makeConfigBuffer = (device, configUniforms, config, density, gridSize, glyphTransform) => {
|
||||
const configData = {
|
||||
...config,
|
||||
gridSize,
|
||||
@@ -18,6 +18,7 @@ const makeConfigBuffer = (device, configUniforms, config, density, gridSize) =>
|
||||
slantScale: 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1),
|
||||
slantVec: [Math.cos(config.slant), Math.sin(config.slant)],
|
||||
msdfPxRange: 4,
|
||||
glyphTransform,
|
||||
};
|
||||
// console.table(configData);
|
||||
|
||||
@@ -25,7 +26,7 @@ const makeConfigBuffer = (device, configUniforms, config, density, gridSize) =>
|
||||
};
|
||||
|
||||
export default ({ config, device, timeBuffer }) => {
|
||||
const { mat4, vec3 } = glMatrix;
|
||||
const { mat2, mat4, vec2, vec3 } = glMatrix;
|
||||
|
||||
const assets = [
|
||||
loadTexture(device, config.glyphMSDFURL),
|
||||
@@ -45,6 +46,9 @@ export default ({ config, device, timeBuffer }) => {
|
||||
// rather than a single quad for our geometry
|
||||
const numQuads = config.volumetric ? numCells : 1;
|
||||
|
||||
const glyphTransform = mat2.fromScaling(mat2.create(), vec2.fromValues(config.glyphFlip ? -1 : 1, 1));
|
||||
mat2.rotate(glyphTransform, glyphTransform, (config.glyphRotation * Math.PI) / 180);
|
||||
|
||||
const transform = mat4.create();
|
||||
if (config.volumetric && config.isometric) {
|
||||
mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
|
||||
@@ -97,7 +101,7 @@ export default ({ config, device, timeBuffer }) => {
|
||||
const [glyphMSDFTexture, glintMSDFTexture, baseTexture, glintTexture, rainShader] = await Promise.all(assets);
|
||||
|
||||
const rainShaderUniforms = structs.from(rainShader.code);
|
||||
configBuffer = makeConfigBuffer(device, rainShaderUniforms.Config, config, density, gridSize);
|
||||
configBuffer = makeConfigBuffer(device, rainShaderUniforms.Config, config, density, gridSize, glyphTransform);
|
||||
|
||||
const introCellsBuffer = device.createBuffer({
|
||||
size: gridSize[0] * rainShaderUniforms.IntroCell.minSize,
|
||||
|
||||
@@ -21,6 +21,8 @@ uniform bool showDebugView;
|
||||
uniform bool volumetric;
|
||||
uniform bool isolateCursor, isolateGlint;
|
||||
|
||||
uniform mat2 glyphTransform;
|
||||
|
||||
varying vec2 vUV;
|
||||
varying vec4 vRaindrop, vSymbol, vEffect;
|
||||
varying float vDepth;
|
||||
@@ -110,6 +112,7 @@ vec2 getSymbol(vec2 uv, float index) {
|
||||
// resolve UV to cropped position of glyph in MSDF texture
|
||||
uv = fract(uv * vec2(numColumns, numRows));
|
||||
uv -= 0.5;
|
||||
uv = glyphTransform * uv;
|
||||
uv *= clamp(1. - glyphEdgeCrop, 0., 1.);
|
||||
uv += 0.5;
|
||||
uv = (uv + getSymbolUV(index)) / glyphTextureGridSize;
|
||||
|
||||
@@ -7,6 +7,7 @@ struct Config {
|
||||
glyphSequenceLength : f32,
|
||||
glyphTextureGridSize : vec2<i32>,
|
||||
glyphHeightToWidth : f32,
|
||||
glyphTransform : mat2x2<f32>,
|
||||
gridSize : vec2<f32>,
|
||||
showDebugView : i32,
|
||||
|
||||
@@ -493,6 +494,7 @@ fn getSymbol(cellUV : vec2<f32>, index : i32) -> vec2<f32> {
|
||||
// resolve UV to cropped position of glyph in MSDF texture
|
||||
var uv = fract(cellUV * config.gridSize);
|
||||
uv -= 0.5;
|
||||
uv = config.glyphTransform * uv;
|
||||
uv *= clamp(1.0 - config.glyphEdgeCrop, 0.0, 1.0);
|
||||
uv += 0.5;
|
||||
uv = (uv + getSymbolUV(index)) / vec2<f32>(config.glyphTextureGridSize);
|
||||
|
||||
Reference in New Issue
Block a user