@thi.ng/webgl-msdf
Version:
Multi-channel SDF font rendering & basic text layout for WebGL
85 lines (84 loc) • 2.27 kB
JavaScript
import { F, M4, S2D, V2, V3, V4 } from "@thi.ng/shader-ast/api/types";
import { assign } from "@thi.ng/shader-ast/ast/assign";
import { discard, ifThen } from "@thi.ng/shader-ast/ast/controlflow";
import { defMain, defn, ret } from "@thi.ng/shader-ast/ast/function";
import {
FLOAT0,
FLOAT05,
FLOAT1,
vec2,
vec4
} from "@thi.ng/shader-ast/ast/lit";
import { add, div, lt, mul, sub } from "@thi.ng/shader-ast/ast/ops";
import { $x, $xyz, $y, $z } from "@thi.ng/shader-ast/ast/swizzle";
import { sym } from "@thi.ng/shader-ast/ast/sym";
import { clamp, max, min, mix } from "@thi.ng/shader-ast/builtin/math";
import { fwidth, texture } from "@thi.ng/shader-ast/builtin/texture";
import { ONE4, ZERO4 } from "@thi.ng/vectors/api";
import { BLEND_NORMAL } from "@thi.ng/webgl/api/blend";
const median3 = defn(F, "median3", [V3], (v) => [
ret(max(min($x(v), $y(v)), min(max($x(v), $y(v)), $z(v))))
]);
const msdfSample = defn(V2, "msdfSample", [S2D, V2], (tex, uv) => {
let sd;
let w;
return [
sd = sym(sub(median3($xyz(texture(tex, uv))), FLOAT05)),
w = sym(clamp(add(div(sd, fwidth(sd)), FLOAT05), FLOAT0, FLOAT1)),
ret(vec2(sd, w))
];
});
const msdfShader = (opts = {}) => ({
vs: (gl, unis, ins, outs) => [
defMain(() => [
assign(outs.vuv, ins.uv),
opts.color ? assign(outs.vcolor, ins.color) : null,
assign(
gl.gl_Position,
mul(mul(unis.proj, unis.modelview), vec4(ins.position, 1))
)
])
],
fs: (_, unis, ins, outs) => [
defMain(() => {
let w;
return [
w = sym(msdfSample(unis.tex, ins.vuv)),
assign(
outs.fragColor,
mix(unis.bg, opts.color ? ins.vcolor : unis.fg, $y(w))
),
ifThen(lt($x(w), unis.thresh), [discard])
];
})
],
ext: {
OES_standard_derivatives: true
},
attribs: {
position: V3,
uv: V2,
...opts.color ? { color: V4 } : null
},
varying: {
vuv: V2,
...opts.color ? { vcolor: V4 } : null
},
uniforms: {
modelview: M4,
proj: M4,
tex: S2D,
thresh: [F, 1e-3],
bg: [V4, ZERO4],
...!opts.color ? { fg: [V4, ONE4] } : null
},
state: {
blend: true,
blendFn: BLEND_NORMAL
}
});
export {
median3,
msdfSample,
msdfShader
};