mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-21 07:19:30 -07:00
Adding "glint", the shapes that appear on top of the glyphs in the Resurrections opening titles.
This commit is contained in:
7
TODO.txt
7
TODO.txt
@@ -17,14 +17,11 @@ Playdate version
|
|||||||
Maybe crank sounds? Not sure yet
|
Maybe crank sounds? Not sure yet
|
||||||
|
|
||||||
Support Resurrections anomaly streaks
|
Support Resurrections anomaly streaks
|
||||||
Kind of like the cursor: new rain pass output channel, new config prop
|
grit texture multiply
|
||||||
MSDF
|
|
||||||
They should line up in Photoshop without too much trouble, actually
|
|
||||||
Texture
|
|
||||||
Maybe give normal Matrix code a pixel grill texture
|
|
||||||
Lighting
|
Lighting
|
||||||
Different parts of a streak glow at different intensities, at different times
|
Different parts of a streak glow at different intensities, at different times
|
||||||
The streaks often dim slower, ie. are brighter, than the glyphs beneath them
|
The streaks often dim slower, ie. are brighter, than the glyphs beneath them
|
||||||
|
Different brightness/contrast?
|
||||||
Imagine they're metallic or something
|
Imagine they're metallic or something
|
||||||
|
|
||||||
Support Resurrections SDF bevel and "lights"
|
Support Resurrections SDF bevel and "lights"
|
||||||
|
|||||||
BIN
assets/resurrections_glint_msdf.png
Normal file
BIN
assets/resurrections_glint_msdf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
36
js/config.js
36
js/config.js
@@ -26,6 +26,7 @@ const fonts = {
|
|||||||
resurrections: {
|
resurrections: {
|
||||||
// The glyphs seen in the film trilogy
|
// The glyphs seen in the film trilogy
|
||||||
glyphTexURL: "assets/resurrections_msdf.png",
|
glyphTexURL: "assets/resurrections_msdf.png",
|
||||||
|
glintTexURL: "assets/resurrections_glint_msdf.png",
|
||||||
glyphSequenceLength: 135,
|
glyphSequenceLength: 135,
|
||||||
glyphTextureGridSize: [13, 12],
|
glyphTextureGridSize: [13, 12],
|
||||||
},
|
},
|
||||||
@@ -57,6 +58,8 @@ const defaults = {
|
|||||||
backgroundColor: [0, 0, 0], // The color "behind" the glyphs
|
backgroundColor: [0, 0, 0], // The color "behind" the glyphs
|
||||||
isolateCursor: true, // Whether the "cursor"— the brightest glyph at the bottom of a raindrop— has its own color
|
isolateCursor: true, // Whether the "cursor"— the brightest glyph at the bottom of a raindrop— has its own color
|
||||||
cursorColor: [1.5, 2, 0.9], // The color of the cursor
|
cursorColor: [1.5, 2, 0.9], // The color of the cursor
|
||||||
|
isolateGlint: false, // Whether the "glint"— highlights on certain symbols in the font— should appear
|
||||||
|
glintColor: [1, 1, 1], // The color of the glint
|
||||||
volumetric: false, // A mode where the raindrops appear in perspective
|
volumetric: false, // A mode where the raindrops appear in perspective
|
||||||
animationSpeed: 1, // The global rate that all animations progress
|
animationSpeed: 1, // The global rate that all animations progress
|
||||||
forwardSpeed: 0.25, // The speed volumetric rain approaches the eye
|
forwardSpeed: 0.25, // The speed volumetric rain approaches the eye
|
||||||
@@ -188,6 +191,28 @@ const versions = {
|
|||||||
{ hsl: [0.375, 1.0, 1.0], at: 1.0 },
|
{ hsl: [0.375, 1.0, 1.0], at: 1.0 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
trinity: {
|
||||||
|
font: "resurrections",
|
||||||
|
glyphEdgeCrop: 0.1,
|
||||||
|
cursorColor: [1.4, 2, 1.2],
|
||||||
|
isolateGlint: true,
|
||||||
|
glintColor: [2, 1.5, 0.5],
|
||||||
|
baseBrightness: -0.9,
|
||||||
|
baseContrast: 1.5,
|
||||||
|
highPassThreshold: 0,
|
||||||
|
numColumns: 50,
|
||||||
|
cycleSpeed: 0.03,
|
||||||
|
bloomStrength: 0.7,
|
||||||
|
fallSpeed: 0.3,
|
||||||
|
paletteEntries: [
|
||||||
|
{ hsl: [0.4, 0.9, 0.0], at: 0.0 },
|
||||||
|
{ hsl: [0.4, 1.0, 0.5], at: 1.0 },
|
||||||
|
],
|
||||||
|
volumetric: true,
|
||||||
|
forwardSpeed: 0.2,
|
||||||
|
raindropLength: 0.3,
|
||||||
|
density: 0.5,
|
||||||
|
},
|
||||||
palimpsest: {
|
palimpsest: {
|
||||||
font: "huberfishA",
|
font: "huberfishA",
|
||||||
isolateCursor: false,
|
isolateCursor: false,
|
||||||
@@ -303,6 +328,7 @@ const paramMapping = {
|
|||||||
stripeColors: { key: "stripeColors", parser: (s) => s },
|
stripeColors: { key: "stripeColors", parser: (s) => s },
|
||||||
backgroundColor: { key: "backgroundColor", parser: (s) => s.split(",").map(parseFloat) },
|
backgroundColor: { key: "backgroundColor", parser: (s) => s.split(",").map(parseFloat) },
|
||||||
cursorColor: { key: "cursorColor", parser: (s) => s.split(",").map(parseFloat) },
|
cursorColor: { key: "cursorColor", parser: (s) => s.split(",").map(parseFloat) },
|
||||||
|
glintColor: { key: "glintColor", parser: (s) => s.split(",").map(parseFloat) },
|
||||||
volumetric: { key: "volumetric", parser: (s) => s.toLowerCase().includes("true") },
|
volumetric: { key: "volumetric", parser: (s) => s.toLowerCase().includes("true") },
|
||||||
loops: { key: "loops", parser: (s) => s.toLowerCase().includes("true") },
|
loops: { key: "loops", parser: (s) => s.toLowerCase().includes("true") },
|
||||||
renderer: { key: "renderer", parser: (s) => s },
|
renderer: { key: "renderer", parser: (s) => s },
|
||||||
@@ -321,8 +347,14 @@ export default (urlParams) => {
|
|||||||
.filter(([_, value]) => value != null)
|
.filter(([_, value]) => value != null)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (validParams.effect != null && validParams.cursorColor == null) {
|
if (validParams.effect != null) {
|
||||||
validParams.cursorColor = [2, 2, 2];
|
if (validParams.cursorColor == null) {
|
||||||
|
validParams.cursorColor = [2, 2, 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validParams.glintColor == null) {
|
||||||
|
validParams.glintColor = [1, 1, 1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const version = validParams.version in versions ? versions[validParams.version] : versions.classic;
|
const version = validParams.version in versions ? versions[validParams.version] : versions.classic;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const makePalette = (regl, entries) => {
|
|||||||
export default ({ regl, config }, inputs) => {
|
export default ({ regl, config }, inputs) => {
|
||||||
const output = makePassFBO(regl, config.useHalfFloat);
|
const output = makePassFBO(regl, config.useHalfFloat);
|
||||||
const palette = makePalette(regl, config.paletteEntries);
|
const palette = makePalette(regl, config.paletteEntries);
|
||||||
const { backgroundColor, cursorColor, ditherMagnitude, bloomStrength } = config;
|
const { backgroundColor, cursorColor, glintColor, ditherMagnitude, bloomStrength } = config;
|
||||||
|
|
||||||
const palettePassFrag = loadText("shaders/glsl/palettePass.frag.glsl");
|
const palettePassFrag = loadText("shaders/glsl/palettePass.frag.glsl");
|
||||||
|
|
||||||
@@ -75,6 +75,7 @@ export default ({ regl, config }, inputs) => {
|
|||||||
uniforms: {
|
uniforms: {
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
cursorColor,
|
cursorColor,
|
||||||
|
glintColor,
|
||||||
ditherMagnitude,
|
ditherMagnitude,
|
||||||
bloomStrength,
|
bloomStrength,
|
||||||
tex: inputs.primary,
|
tex: inputs.primary,
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ export default ({ regl, config, lkg }) => {
|
|||||||
|
|
||||||
// We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
|
// We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
|
||||||
const msdf = loadImage(regl, config.glyphTexURL);
|
const msdf = loadImage(regl, config.glyphTexURL);
|
||||||
|
const glintMSDF = loadImage(regl, config.glintTexURL);
|
||||||
const rainPassVert = loadText("shaders/glsl/rainPass.vert.glsl");
|
const rainPassVert = loadText("shaders/glsl/rainPass.vert.glsl");
|
||||||
const rainPassFrag = loadText("shaders/glsl/rainPass.frag.glsl");
|
const rainPassFrag = loadText("shaders/glsl/rainPass.frag.glsl");
|
||||||
const output = makePassFBO(regl, config.useHalfFloat);
|
const output = makePassFBO(regl, config.useHalfFloat);
|
||||||
@@ -131,6 +132,7 @@ export default ({ regl, config, lkg }) => {
|
|||||||
"brightnessThreshold",
|
"brightnessThreshold",
|
||||||
"brightnessOverride",
|
"brightnessOverride",
|
||||||
"isolateCursor",
|
"isolateCursor",
|
||||||
|
"isolateGlint",
|
||||||
"glyphEdgeCrop",
|
"glyphEdgeCrop",
|
||||||
"isPolar",
|
"isPolar",
|
||||||
]),
|
]),
|
||||||
@@ -160,6 +162,7 @@ export default ({ regl, config, lkg }) => {
|
|||||||
symbolState: symbolDoubleBuffer.front,
|
symbolState: symbolDoubleBuffer.front,
|
||||||
effectState: effectDoubleBuffer.front,
|
effectState: effectDoubleBuffer.front,
|
||||||
glyphTex: msdf.texture,
|
glyphTex: msdf.texture,
|
||||||
|
glintTex: glintMSDF.texture,
|
||||||
|
|
||||||
camera: regl.prop("camera"),
|
camera: regl.prop("camera"),
|
||||||
transform: regl.prop("transform"),
|
transform: regl.prop("transform"),
|
||||||
@@ -201,7 +204,7 @@ export default ({ regl, config, lkg }) => {
|
|||||||
{
|
{
|
||||||
primary: output,
|
primary: output,
|
||||||
},
|
},
|
||||||
Promise.all([msdf.loaded, rainPassShine.loaded, rainPassSymbol.loaded, rainPassVert.loaded, rainPassFrag.loaded]),
|
Promise.all([msdf.loaded, glintMSDF.loaded, rainPassShine.loaded, rainPassSymbol.loaded, rainPassVert.loaded, rainPassFrag.loaded]),
|
||||||
(w, h) => {
|
(w, h) => {
|
||||||
output.resize(w, h);
|
output.resize(w, h);
|
||||||
const aspectRatio = w / h;
|
const aspectRatio = w / h;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const prideStripeColors = [
|
|||||||
export default ({ regl, config }, inputs) => {
|
export default ({ regl, config }, inputs) => {
|
||||||
const output = makePassFBO(regl, config.useHalfFloat);
|
const output = makePassFBO(regl, config.useHalfFloat);
|
||||||
|
|
||||||
const { backgroundColor, cursorColor, ditherMagnitude, bloomStrength } = config;
|
const { backgroundColor, cursorColor, glintColor, ditherMagnitude, bloomStrength } = config;
|
||||||
|
|
||||||
// Expand and convert stripe colors into 1D texture data
|
// Expand and convert stripe colors into 1D texture data
|
||||||
const stripeColors =
|
const stripeColors =
|
||||||
@@ -50,6 +50,7 @@ export default ({ regl, config }, inputs) => {
|
|||||||
uniforms: {
|
uniforms: {
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
cursorColor,
|
cursorColor,
|
||||||
|
glintColor,
|
||||||
ditherMagnitude,
|
ditherMagnitude,
|
||||||
bloomStrength,
|
bloomStrength,
|
||||||
tex: inputs.primary,
|
tex: inputs.primary,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const loadImage = (regl, url) => {
|
|||||||
let loaded = false;
|
let loaded = false;
|
||||||
return {
|
return {
|
||||||
texture: () => {
|
texture: () => {
|
||||||
if (!loaded) {
|
if (!loaded && url != null) {
|
||||||
console.warn(`texture still loading: ${url}`);
|
console.warn(`texture still loading: ${url}`);
|
||||||
}
|
}
|
||||||
return texture;
|
return texture;
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ export default ({ config, device, timeBuffer }) => {
|
|||||||
ditherMagnitude: config.ditherMagnitude,
|
ditherMagnitude: config.ditherMagnitude,
|
||||||
backgroundColor: config.backgroundColor,
|
backgroundColor: config.backgroundColor,
|
||||||
cursorColor: config.cursorColor,
|
cursorColor: config.cursorColor,
|
||||||
|
glintColor: config.glintColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
const paletteUniforms = paletteShaderUniforms.Palette;
|
const paletteUniforms = paletteShaderUniforms.Palette;
|
||||||
|
|||||||
@@ -26,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 { mat4, vec3 } = glMatrix;
|
||||||
|
|
||||||
const assets = [loadTexture(device, config.glyphTexURL), loadShader(device, "shaders/wgsl/rainPass.wgsl")];
|
const assets = [loadTexture(device, config.glyphTexURL), loadTexture(device, config.glintTexURL), loadShader(device, "shaders/wgsl/rainPass.wgsl")];
|
||||||
|
|
||||||
// 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
|
||||||
@@ -85,7 +85,7 @@ export default ({ config, device, timeBuffer }) => {
|
|||||||
let highPassOutput;
|
let highPassOutput;
|
||||||
|
|
||||||
const loaded = (async () => {
|
const loaded = (async () => {
|
||||||
const [msdfTexture, rainShader] = await Promise.all(assets);
|
const [msdfTexture, glintMSDFTexture, 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);
|
||||||
@@ -143,7 +143,15 @@ export default ({ config, device, timeBuffer }) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
computeBindGroup = makeBindGroup(device, computePipeline, 0, [configBuffer, timeBuffer, cellsBuffer]);
|
computeBindGroup = makeBindGroup(device, computePipeline, 0, [configBuffer, timeBuffer, cellsBuffer]);
|
||||||
renderBindGroup = makeBindGroup(device, renderPipeline, 0, [configBuffer, timeBuffer, sceneBuffer, linearSampler, msdfTexture.createView(), cellsBuffer]);
|
renderBindGroup = makeBindGroup(device, renderPipeline, 0, [
|
||||||
|
configBuffer,
|
||||||
|
timeBuffer,
|
||||||
|
sceneBuffer,
|
||||||
|
linearSampler,
|
||||||
|
msdfTexture.createView(),
|
||||||
|
glintMSDFTexture.createView(),
|
||||||
|
cellsBuffer,
|
||||||
|
]);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const build = (size) => {
|
const build = (size) => {
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export default ({ config, device, timeBuffer }) => {
|
|||||||
ditherMagnitude: config.ditherMagnitude,
|
ditherMagnitude: config.ditherMagnitude,
|
||||||
backgroundColor: config.backgroundColor,
|
backgroundColor: config.backgroundColor,
|
||||||
cursorColor: config.cursorColor,
|
cursorColor: config.cursorColor,
|
||||||
|
glintColor: config.glintColor,
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ const loadTexture = async (device, url) => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const loadTexture = async (device, url) => {
|
const loadTexture = async (device, url) => {
|
||||||
|
if (url == null) {
|
||||||
|
return device.createTexture({
|
||||||
|
size: [1, 1, 1],
|
||||||
|
format: "rgba8unorm",
|
||||||
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.crossOrigin = "Anonymous";
|
image.crossOrigin = "Anonymous";
|
||||||
image.src = url;
|
image.src = url;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ uniform sampler2D palette;
|
|||||||
uniform float bloomStrength;
|
uniform float bloomStrength;
|
||||||
uniform float ditherMagnitude;
|
uniform float ditherMagnitude;
|
||||||
uniform float time;
|
uniform float time;
|
||||||
uniform vec3 backgroundColor, cursorColor;
|
uniform vec3 backgroundColor, cursorColor, glintColor;
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
|
|
||||||
highp float rand( const in vec2 uv, const in float t ) {
|
highp float rand( const in vec2 uv, const in float t ) {
|
||||||
@@ -26,12 +26,13 @@ void main() {
|
|||||||
vec4 brightness = getBrightness(vUV);
|
vec4 brightness = getBrightness(vUV);
|
||||||
|
|
||||||
// Dither: subtract a random value from the brightness
|
// Dither: subtract a random value from the brightness
|
||||||
brightness -= rand( gl_FragCoord.xy, time ) * ditherMagnitude;
|
brightness -= rand( gl_FragCoord.xy, time ) * ditherMagnitude / 3.0;
|
||||||
|
|
||||||
// Map the brightness to a position in the palette texture
|
// Map the brightness to a position in the palette texture
|
||||||
gl_FragColor = vec4(
|
gl_FragColor = vec4(
|
||||||
texture2D( palette, vec2(brightness.r, 0.0)).rgb
|
texture2D( palette, vec2(brightness.r, 0.0)).rgb
|
||||||
+ min(cursorColor * brightness.g, vec3(1.0))
|
+ min(cursorColor * brightness.g, vec3(1.0))
|
||||||
|
+ min(glintColor * brightness.b, vec3(1.0))
|
||||||
+ backgroundColor,
|
+ backgroundColor,
|
||||||
1.0
|
1.0
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ precision lowp float;
|
|||||||
|
|
||||||
uniform sampler2D raindropState, symbolState, effectState;
|
uniform sampler2D raindropState, symbolState, effectState;
|
||||||
uniform float numColumns, numRows;
|
uniform float numColumns, numRows;
|
||||||
uniform sampler2D glyphTex;
|
uniform sampler2D glyphTex, glintTex;
|
||||||
uniform float glyphHeightToWidth, glyphSequenceLength, glyphEdgeCrop;
|
uniform float glyphHeightToWidth, glyphSequenceLength, glyphEdgeCrop;
|
||||||
uniform float baseContrast, baseBrightness;
|
uniform float baseContrast, baseBrightness;
|
||||||
uniform float brightnessOverride, brightnessThreshold;
|
uniform float brightnessOverride, brightnessThreshold;
|
||||||
@@ -16,7 +16,7 @@ uniform float slantScale;
|
|||||||
uniform bool isPolar;
|
uniform bool isPolar;
|
||||||
uniform bool showDebugView;
|
uniform bool showDebugView;
|
||||||
uniform bool volumetric;
|
uniform bool volumetric;
|
||||||
uniform bool isolateCursor;
|
uniform bool isolateCursor, isolateGlint;
|
||||||
|
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
varying vec4 vRaindrop, vSymbol, vEffect;
|
varying vec4 vRaindrop, vSymbol, vEffect;
|
||||||
@@ -87,7 +87,7 @@ vec2 getSymbolUV(float index) {
|
|||||||
return vec2(symbolX, symbolY);
|
return vec2(symbolX, symbolY);
|
||||||
}
|
}
|
||||||
|
|
||||||
float getSymbol(vec2 uv, float index) {
|
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;
|
||||||
@@ -96,9 +96,20 @@ float getSymbol(vec2 uv, float index) {
|
|||||||
uv = (uv + getSymbolUV(index)) / glyphTextureGridSize;
|
uv = (uv + getSymbolUV(index)) / glyphTextureGridSize;
|
||||||
|
|
||||||
// MSDF: calculate brightness of fragment based on distance to shape
|
// MSDF: calculate brightness of fragment based on distance to shape
|
||||||
vec3 dist = texture2D(glyphTex, uv).rgb;
|
vec2 symbol;
|
||||||
float sigDist = median3(dist) - 0.5;
|
{
|
||||||
return clamp(sigDist / fwidth(sigDist) + 0.5, 0., 1.);
|
vec3 dist = texture2D(glyphTex, uv).rgb;
|
||||||
|
float sigDist = median3(dist) - 0.5;
|
||||||
|
symbol.r = clamp(sigDist / fwidth(sigDist) + 0.5, 0., 1.);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isolateGlint) {
|
||||||
|
vec3 dist = texture2D(glintTex, uv).rgb;
|
||||||
|
float sigDist = median3(dist) - 0.5;
|
||||||
|
symbol.g = clamp(sigDist / fwidth(sigDist) + 0.5, 0., 1.);
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -111,7 +122,7 @@ void main() {
|
|||||||
vec4 effectData = volumetric ? vEffect : texture2D( effectState, uv);
|
vec4 effectData = volumetric ? vEffect : texture2D( effectState, uv);
|
||||||
|
|
||||||
vec2 brightness = getBrightness(raindropData.r, raindropData.g, effectData.r, effectData.g);
|
vec2 brightness = getBrightness(raindropData.r, raindropData.g, effectData.r, effectData.g);
|
||||||
float symbol = getSymbol(uv, symbolData.r);
|
vec2 symbol = getSymbol(uv, symbolData.r);
|
||||||
|
|
||||||
if (showDebugView) {
|
if (showDebugView) {
|
||||||
gl_FragColor = vec4(
|
gl_FragColor = vec4(
|
||||||
@@ -121,10 +132,10 @@ void main() {
|
|||||||
1. - (raindropData.r * 3.),
|
1. - (raindropData.r * 3.),
|
||||||
1. - (raindropData.r * 8.)
|
1. - (raindropData.r * 8.)
|
||||||
) * (1. - raindropData.g)
|
) * (1. - raindropData.g)
|
||||||
) * symbol,
|
) * symbol.r,
|
||||||
1.
|
1.
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
gl_FragColor = vec4(brightness * symbol, 0., 0.);
|
gl_FragColor = vec4(brightness * symbol.r, brightness.r * symbol.g, 0.);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ uniform float bloomStrength;
|
|||||||
uniform sampler2D stripes;
|
uniform sampler2D stripes;
|
||||||
uniform float ditherMagnitude;
|
uniform float ditherMagnitude;
|
||||||
uniform float time;
|
uniform float time;
|
||||||
uniform vec3 backgroundColor, cursorColor;
|
uniform vec3 backgroundColor, cursorColor, glintColor;
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
|
|
||||||
highp float rand( const in vec2 uv, const in float t ) {
|
highp float rand( const in vec2 uv, const in float t ) {
|
||||||
@@ -28,11 +28,12 @@ void main() {
|
|||||||
vec4 brightness = getBrightness(vUV);
|
vec4 brightness = getBrightness(vUV);
|
||||||
|
|
||||||
// Dither: subtract a random value from the brightness
|
// Dither: subtract a random value from the brightness
|
||||||
brightness -= rand( gl_FragCoord.xy, time ) * ditherMagnitude;
|
brightness -= rand( gl_FragCoord.xy, time ) * ditherMagnitude / 3.0;
|
||||||
|
|
||||||
gl_FragColor = vec4(
|
gl_FragColor = vec4(
|
||||||
color * brightness.r
|
color * brightness.r
|
||||||
+ min(cursorColor * brightness.g, 1.0)
|
+ min(cursorColor * brightness.g, vec3(1.0))
|
||||||
|
+ min(glintColor * brightness.b, vec3(1.0))
|
||||||
+ backgroundColor,
|
+ backgroundColor,
|
||||||
1.0
|
1.0
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ struct Config {
|
|||||||
bloomStrength : f32,
|
bloomStrength : f32,
|
||||||
ditherMagnitude : f32,
|
ditherMagnitude : f32,
|
||||||
backgroundColor : vec3<f32>,
|
backgroundColor : vec3<f32>,
|
||||||
cursorColor : vec3<f32>
|
cursorColor : vec3<f32>,
|
||||||
|
glintColor : vec3<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Palette {
|
struct Palette {
|
||||||
@@ -58,7 +59,7 @@ fn getBrightness(uv : vec2<f32>) -> vec4<f32> {
|
|||||||
var brightness = getBrightness(uv);
|
var brightness = getBrightness(uv);
|
||||||
|
|
||||||
// Dither: subtract a random value from the brightness
|
// Dither: subtract a random value from the brightness
|
||||||
brightness -= randomFloat( uv + vec2<f32>(time.seconds) ) * config.ditherMagnitude;
|
brightness -= randomFloat( uv + vec2<f32>(time.seconds) ) * config.ditherMagnitude / 3.0;
|
||||||
|
|
||||||
// Map the brightness to a position in the palette texture
|
// Map the brightness to a position in the palette texture
|
||||||
var paletteIndex = clamp(i32(brightness.r * 512.0), 0, 511);
|
var paletteIndex = clamp(i32(brightness.r * 512.0), 0, 511);
|
||||||
@@ -66,6 +67,7 @@ fn getBrightness(uv : vec2<f32>) -> vec4<f32> {
|
|||||||
textureStore(outputTex, coord, vec4<f32>(
|
textureStore(outputTex, coord, vec4<f32>(
|
||||||
palette.colors[paletteIndex]
|
palette.colors[paletteIndex]
|
||||||
+ min(config.cursorColor * brightness.g, vec3<f32>(1.0))
|
+ min(config.cursorColor * brightness.g, vec3<f32>(1.0))
|
||||||
|
+ min(config.glintColor * brightness.b, vec3<f32>(1.0))
|
||||||
+ config.backgroundColor,
|
+ config.backgroundColor,
|
||||||
1.0
|
1.0
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ struct Config {
|
|||||||
slantVec : vec2<f32>,
|
slantVec : vec2<f32>,
|
||||||
volumetric : i32,
|
volumetric : i32,
|
||||||
isolateCursor : i32,
|
isolateCursor : i32,
|
||||||
|
isolateGlint : i32,
|
||||||
loops : i32,
|
loops : i32,
|
||||||
highPassThreshold : f32,
|
highPassThreshold : f32,
|
||||||
};
|
};
|
||||||
@@ -76,7 +77,8 @@ struct CellData {
|
|||||||
@group(0) @binding(2) var<uniform> scene : Scene;
|
@group(0) @binding(2) var<uniform> scene : Scene;
|
||||||
@group(0) @binding(3) var linearSampler : sampler;
|
@group(0) @binding(3) var linearSampler : sampler;
|
||||||
@group(0) @binding(4) var msdfTexture : texture_2d<f32>;
|
@group(0) @binding(4) var msdfTexture : texture_2d<f32>;
|
||||||
@group(0) @binding(5) var<storage, read> cells_RO : CellData;
|
@group(0) @binding(5) var glintMSDFTexture : texture_2d<f32>;
|
||||||
|
@group(0) @binding(6) var<storage, read> cells_RO : CellData;
|
||||||
|
|
||||||
// Shader params
|
// Shader params
|
||||||
|
|
||||||
@@ -401,7 +403,7 @@ fn getSymbolUV(symbol : i32) -> vec2<f32> {
|
|||||||
return vec2<f32>(f32(symbolX), f32(symbolY));
|
return vec2<f32>(f32(symbolX), f32(symbolY));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getSymbol(cellUV : vec2<f32>, index : i32) -> f32 {
|
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.y = 1.0 - uv.y; // WebGL -> WebGPU y-flip
|
uv.y = 1.0 - uv.y; // WebGL -> WebGPU y-flip
|
||||||
@@ -410,10 +412,22 @@ fn getSymbol(cellUV : vec2<f32>, index : i32) -> f32 {
|
|||||||
uv += 0.5;
|
uv += 0.5;
|
||||||
uv = (uv + getSymbolUV(index)) / vec2<f32>(config.glyphTextureGridSize);
|
uv = (uv + getSymbolUV(index)) / vec2<f32>(config.glyphTextureGridSize);
|
||||||
|
|
||||||
|
var symbol = vec2<f32>();
|
||||||
|
|
||||||
// MSDF: calculate brightness of fragment based on distance to shape
|
// MSDF: calculate brightness of fragment based on distance to shape
|
||||||
var dist = textureSample(msdfTexture, linearSampler, uv).rgb;
|
{
|
||||||
var sigDist = median3(dist) - 0.5;
|
var dist = textureSample(msdfTexture, linearSampler, uv).rgb;
|
||||||
return clamp(sigDist / fwidth(sigDist) + 0.5, 0.0, 1.0);
|
var sigDist = median3(dist) - 0.5;
|
||||||
|
symbol.r = clamp(sigDist / fwidth(sigDist) + 0.5, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bool(config.isolateGlint)) {
|
||||||
|
var dist = textureSample(glintMSDFTexture, linearSampler, uv).rgb;
|
||||||
|
var sigDist = median3(dist) - 0.5;
|
||||||
|
symbol.g = clamp(sigDist / fwidth(sigDist) + 0.5, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fragment shader
|
// Fragment shader
|
||||||
@@ -446,11 +460,11 @@ fn getSymbol(cellUV : vec2<f32>, index : i32) -> f32 {
|
|||||||
1.0 - (cell.raindrop.r * 3.0),
|
1.0 - (cell.raindrop.r * 3.0),
|
||||||
1.0 - (cell.raindrop.r * 8.0)
|
1.0 - (cell.raindrop.r * 8.0)
|
||||||
) * (1.0 - cell.raindrop.g)
|
) * (1.0 - cell.raindrop.g)
|
||||||
) * symbol,
|
) * symbol.r,
|
||||||
1.0
|
1.0
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
output.color = vec4(brightness * symbol, 0.0, 0.0);
|
output.color = vec4(brightness * symbol.r, brightness.r * symbol.g, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var highPassColor = output.color;
|
var highPassColor = output.color;
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ struct Config {
|
|||||||
bloomStrength : f32,
|
bloomStrength : f32,
|
||||||
ditherMagnitude : f32,
|
ditherMagnitude : f32,
|
||||||
backgroundColor : vec3<f32>,
|
backgroundColor : vec3<f32>,
|
||||||
cursorColor : vec3<f32>
|
cursorColor : vec3<f32>,
|
||||||
|
glintColor : vec3<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Time {
|
struct Time {
|
||||||
@@ -56,11 +57,12 @@ fn getBrightness(uv : vec2<f32>) -> vec4<f32> {
|
|||||||
var brightness = getBrightness(uv);
|
var brightness = getBrightness(uv);
|
||||||
|
|
||||||
// Dither: subtract a random value from the brightness
|
// Dither: subtract a random value from the brightness
|
||||||
brightness -= randomFloat( uv + vec2<f32>(time.seconds) ) * config.ditherMagnitude;
|
brightness -= randomFloat( uv + vec2<f32>(time.seconds) ) * config.ditherMagnitude / 3.0;
|
||||||
|
|
||||||
textureStore(outputTex, coord, vec4<f32>(
|
textureStore(outputTex, coord, vec4<f32>(
|
||||||
color * brightness.r
|
color * brightness.r
|
||||||
+ min(config.cursorColor * brightness.g, vec3<f32>(1.0))
|
+ min(config.cursorColor * brightness.g, vec3<f32>(1.0))
|
||||||
|
+ min(config.glintColor * brightness.b, vec3<f32>(1.0))
|
||||||
+ config.backgroundColor,
|
+ config.backgroundColor,
|
||||||
1.0
|
1.0
|
||||||
));
|
));
|
||||||
|
|||||||
Reference in New Issue
Block a user