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:
|
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:
|
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).
|
- "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*.
|
- `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".
|
- `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".
|
- `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.
|
- `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.
|
- `forwardSpeed` - the rate that the 3D raindrops approach. Default is 1.0.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Matrix digital rain</title>
|
<title>Matrix digital rain</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" /></meta>
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /></meta>
|
<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" />
|
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover" />
|
||||||
<style>
|
<style>
|
||||||
@supports (padding-top: env(safe-area-inset-top)) {
|
@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
|
glyphEdgeCrop: 0.0, // The border around a glyph in a font texture that should be cropped out
|
||||||
glyphHeightToWidth: 1, // The aspect ratio of glyphs
|
glyphHeightToWidth: 1, // The aspect ratio of glyphs
|
||||||
glyphVerticalSpacing: 1, // The ratio of the vertical distance between glyphs to their height
|
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
|
hasThunder: false, // An effect that adds dramatic lightning flashes
|
||||||
isPolar: false, // Whether the glyphs arc across the screen or sit in a standard grid
|
isPolar: false, // Whether the glyphs arc across the screen or sit in a standard grid
|
||||||
rippleTypeName: null, // The variety of the ripple effect
|
rippleTypeName: null, // The variety of the ripple effect
|
||||||
@@ -134,12 +136,12 @@ const versions = {
|
|||||||
megacity: {
|
megacity: {
|
||||||
font: "megacity",
|
font: "megacity",
|
||||||
animationSpeed: 0.5,
|
animationSpeed: 0.5,
|
||||||
width: 40,
|
numColumns: 40,
|
||||||
},
|
},
|
||||||
neomatrixology: {
|
neomatrixology: {
|
||||||
font: "neomatrixology",
|
font: "neomatrixology",
|
||||||
animationSpeed: 0.8,
|
animationSpeed: 0.8,
|
||||||
width: 40,
|
numColumns: 40,
|
||||||
palette: [
|
palette: [
|
||||||
{ color: hsl(0.15, 0.9, 0.0), at: 0.0 },
|
{ color: hsl(0.15, 0.9, 0.0), at: 0.0 },
|
||||||
{ color: hsl(0.15, 0.9, 0.2), at: 0.2 },
|
{ color: hsl(0.15, 0.9, 0.2), at: 0.2 },
|
||||||
@@ -443,7 +445,6 @@ const paramMapping = {
|
|||||||
font: { key: "font", parser: (s) => s },
|
font: { key: "font", parser: (s) => s },
|
||||||
effect: { key: "effect", parser: (s) => s },
|
effect: { key: "effect", parser: (s) => s },
|
||||||
camera: { key: "useCamera", parser: isTrue },
|
camera: { key: "useCamera", parser: isTrue },
|
||||||
width: { key: "numColumns", parser: (s) => nullNaN(parseInt(s)) },
|
|
||||||
numColumns: { 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)) },
|
density: { key: "density", parser: (s) => nullNaN(range(parseFloat(s), 0)) },
|
||||||
resolution: { key: "resolution", parser: (s) => nullNaN(parseFloat(s)) },
|
resolution: { key: "resolution", parser: (s) => nullNaN(parseFloat(s)) },
|
||||||
@@ -501,6 +502,11 @@ const paramMapping = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
volumetric: { key: "volumetric", parser: isTrue },
|
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 },
|
loops: { key: "loops", parser: isTrue },
|
||||||
fps: { key: "fps", parser: (s) => nullNaN(range(parseFloat(s), 0, 60)) },
|
fps: { key: "fps", parser: (s) => nullNaN(range(parseFloat(s), 0, 60)) },
|
||||||
skipIntro: { key: "skipIntro", parser: isTrue },
|
skipIntro: { key: "skipIntro", parser: isTrue },
|
||||||
@@ -516,16 +522,17 @@ paramMapping.backgroundRGB = paramMapping.backgroundColor;
|
|||||||
paramMapping.cursorRGB = paramMapping.cursorColor;
|
paramMapping.cursorRGB = paramMapping.cursorColor;
|
||||||
paramMapping.glintRGB = paramMapping.glintColor;
|
paramMapping.glintRGB = paramMapping.glintColor;
|
||||||
|
|
||||||
|
paramMapping.width = paramMapping.numColumns;
|
||||||
paramMapping.dropLength = paramMapping.raindropLength;
|
paramMapping.dropLength = paramMapping.raindropLength;
|
||||||
paramMapping.angle = paramMapping.slant;
|
paramMapping.angle = paramMapping.slant;
|
||||||
paramMapping.colors = paramMapping.stripeColors;
|
paramMapping.colors = paramMapping.stripeColors;
|
||||||
|
|
||||||
export default (urlParams) => {
|
export default (urlParams) => {
|
||||||
const validParams = Object.fromEntries(
|
const validParams = Object.fromEntries(
|
||||||
Array.from(Object.entries(urlParams))
|
Object.entries(urlParams)
|
||||||
.filter(([key]) => key in paramMapping)
|
.filter(([key]) => key in paramMapping)
|
||||||
.map(([key, value]) => [paramMapping[key].key, paramMapping[key].parser(value)])
|
.map(([key, value]) => [paramMapping[key].key, paramMapping[key].parser(value)])
|
||||||
.filter(([_, value]) => value != null)
|
.filter(([_, value]) => value != null),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (validParams.effect != null) {
|
if (validParams.effect != null) {
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ const brVert = [1, 1];
|
|||||||
const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert];
|
const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert];
|
||||||
|
|
||||||
export default ({ regl, config, lkg }) => {
|
export default ({ regl, config, lkg }) => {
|
||||||
|
const { mat2, mat4, vec2, vec3 } = glMatrix;
|
||||||
|
|
||||||
// The volumetric mode multiplies the number of columns
|
// The volumetric mode multiplies the number of columns
|
||||||
// to reach the desired density, and then overlaps them
|
// to reach the desired density, and then overlaps them
|
||||||
const volumetric = config.volumetric;
|
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 slantScale = 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
|
||||||
const showDebugView = config.effect === "none";
|
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 = {
|
const commonUniforms = {
|
||||||
...extractEntries(config, ["animationSpeed", "glyphHeightToWidth", "glyphSequenceLength", "glyphTextureGridSize"]),
|
...extractEntries(config, ["animationSpeed", "glyphHeightToWidth", "glyphSequenceLength", "glyphTextureGridSize"]),
|
||||||
numColumns,
|
numColumns,
|
||||||
@@ -160,6 +165,7 @@ export default ({ regl, config, lkg }) => {
|
|||||||
"glyphEdgeCrop",
|
"glyphEdgeCrop",
|
||||||
"isPolar",
|
"isPolar",
|
||||||
]),
|
]),
|
||||||
|
glyphTransform,
|
||||||
density,
|
density,
|
||||||
numQuadColumns,
|
numQuadColumns,
|
||||||
numQuadRows,
|
numQuadRows,
|
||||||
@@ -212,7 +218,6 @@ export default ({ regl, config, lkg }) => {
|
|||||||
|
|
||||||
// Camera and transform math for the volumetric mode
|
// Camera and transform math for the volumetric mode
|
||||||
const screenSize = [1, 1];
|
const screenSize = [1, 1];
|
||||||
const { mat4, vec3 } = glMatrix;
|
|
||||||
const transform = mat4.create();
|
const transform = mat4.create();
|
||||||
if (volumetric && config.isometric) {
|
if (volumetric && config.isometric) {
|
||||||
mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
|
mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const rippleTypes = {
|
|||||||
|
|
||||||
const numVerticesPerQuad = 2 * 3;
|
const numVerticesPerQuad = 2 * 3;
|
||||||
|
|
||||||
const makeConfigBuffer = (device, configUniforms, config, density, gridSize) => {
|
const makeConfigBuffer = (device, configUniforms, config, density, gridSize, glyphTransform) => {
|
||||||
const configData = {
|
const configData = {
|
||||||
...config,
|
...config,
|
||||||
gridSize,
|
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),
|
slantScale: 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1),
|
||||||
slantVec: [Math.cos(config.slant), Math.sin(config.slant)],
|
slantVec: [Math.cos(config.slant), Math.sin(config.slant)],
|
||||||
msdfPxRange: 4,
|
msdfPxRange: 4,
|
||||||
|
glyphTransform,
|
||||||
};
|
};
|
||||||
// console.table(configData);
|
// console.table(configData);
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ const makeConfigBuffer = (device, configUniforms, config, density, gridSize) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default ({ config, device, timeBuffer }) => {
|
export default ({ config, device, timeBuffer }) => {
|
||||||
const { mat4, vec3 } = glMatrix;
|
const { mat2, mat4, vec2, vec3 } = glMatrix;
|
||||||
|
|
||||||
const assets = [
|
const assets = [
|
||||||
loadTexture(device, config.glyphMSDFURL),
|
loadTexture(device, config.glyphMSDFURL),
|
||||||
@@ -45,6 +46,9 @@ export default ({ config, device, timeBuffer }) => {
|
|||||||
// rather than a single quad for our geometry
|
// rather than a single quad for our geometry
|
||||||
const numQuads = config.volumetric ? numCells : 1;
|
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();
|
const transform = mat4.create();
|
||||||
if (config.volumetric && config.isometric) {
|
if (config.volumetric && config.isometric) {
|
||||||
mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
|
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 [glyphMSDFTexture, glintMSDFTexture, baseTexture, glintTexture, rainShader] = await Promise.all(assets);
|
||||||
|
|
||||||
const rainShaderUniforms = structs.from(rainShader.code);
|
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({
|
const introCellsBuffer = device.createBuffer({
|
||||||
size: gridSize[0] * rainShaderUniforms.IntroCell.minSize,
|
size: gridSize[0] * rainShaderUniforms.IntroCell.minSize,
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ uniform bool showDebugView;
|
|||||||
uniform bool volumetric;
|
uniform bool volumetric;
|
||||||
uniform bool isolateCursor, isolateGlint;
|
uniform bool isolateCursor, isolateGlint;
|
||||||
|
|
||||||
|
uniform mat2 glyphTransform;
|
||||||
|
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
varying vec4 vRaindrop, vSymbol, vEffect;
|
varying vec4 vRaindrop, vSymbol, vEffect;
|
||||||
varying float vDepth;
|
varying float vDepth;
|
||||||
@@ -110,6 +112,7 @@ vec2 getSymbol(vec2 uv, float index) {
|
|||||||
// resolve UV to cropped position of glyph in MSDF texture
|
// resolve UV to cropped position of glyph in MSDF texture
|
||||||
uv = fract(uv * vec2(numColumns, numRows));
|
uv = fract(uv * vec2(numColumns, numRows));
|
||||||
uv -= 0.5;
|
uv -= 0.5;
|
||||||
|
uv = glyphTransform * uv;
|
||||||
uv *= clamp(1. - glyphEdgeCrop, 0., 1.);
|
uv *= clamp(1. - glyphEdgeCrop, 0., 1.);
|
||||||
uv += 0.5;
|
uv += 0.5;
|
||||||
uv = (uv + getSymbolUV(index)) / glyphTextureGridSize;
|
uv = (uv + getSymbolUV(index)) / glyphTextureGridSize;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ struct Config {
|
|||||||
glyphSequenceLength : f32,
|
glyphSequenceLength : f32,
|
||||||
glyphTextureGridSize : vec2<i32>,
|
glyphTextureGridSize : vec2<i32>,
|
||||||
glyphHeightToWidth : f32,
|
glyphHeightToWidth : f32,
|
||||||
|
glyphTransform : mat2x2<f32>,
|
||||||
gridSize : vec2<f32>,
|
gridSize : vec2<f32>,
|
||||||
showDebugView : i32,
|
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
|
// resolve UV to cropped position of glyph in MSDF texture
|
||||||
var uv = fract(cellUV * config.gridSize);
|
var uv = fract(cellUV * config.gridSize);
|
||||||
uv -= 0.5;
|
uv -= 0.5;
|
||||||
|
uv = config.glyphTransform * uv;
|
||||||
uv *= clamp(1.0 - config.glyphEdgeCrop, 0.0, 1.0);
|
uv *= clamp(1.0 - config.glyphEdgeCrop, 0.0, 1.0);
|
||||||
uv += 0.5;
|
uv += 0.5;
|
||||||
uv = (uv + getSymbolUV(index)) / vec2<f32>(config.glyphTextureGridSize);
|
uv = (uv + getSymbolUV(index)) / vec2<f32>(config.glyphTextureGridSize);
|
||||||
|
|||||||
Reference in New Issue
Block a user