three-text 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -25
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/vector/core/index.cjs +45 -77
- package/dist/vector/core/index.js +45 -77
- package/dist/vector/index2.cjs +5 -19
- package/dist/vector/index2.js +5 -19
- package/dist/vector/slugTSL.cjs +4 -27
- package/dist/vector/slugTSL.js +4 -27
- package/dist/vector/webgl/index.cjs +1 -4
- package/dist/vector/webgl/index.js +1 -4
- package/dist/vector/webgpu/index.cjs +0 -3
- package/dist/vector/webgpu/index.js +0 -3
- package/package.json +2 -2
package/dist/vector/slugTSL.js
CHANGED
|
@@ -4,20 +4,11 @@ import { Fn, select, uint, float, sqrt, max, If, abs, vec2, ivec2, varying, attr
|
|
|
4
4
|
import { u as unpackSlugVertices } from './index2.js';
|
|
5
5
|
import './core/index.js';
|
|
6
6
|
|
|
7
|
-
// Slug
|
|
7
|
+
// Slug adapter using Three.js TSL (node materials)
|
|
8
|
+
// Works on both WebGPU and WebGL backends (r170+)
|
|
8
9
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// band-accelerated ray-curve intersection
|
|
12
|
-
//
|
|
13
|
-
// Works on both WebGPU and WebGL backends via Three.js TSL
|
|
14
|
-
//
|
|
15
|
-
// Compared to the raw GLSL/WGSL standalone renderers, this adapter
|
|
16
|
-
// trades some features for Three.js integration:
|
|
17
|
-
// - No vertex dilation (may cause sub-pixel edge clipping at extreme zoom)
|
|
18
|
-
// - No adaptive supersampling (single-sample per fragment)
|
|
19
|
-
//
|
|
20
|
-
// Requires peer dependencies: three, three/tsl
|
|
10
|
+
// No vertex dilation or adaptive supersampling (unlike the raw
|
|
11
|
+
// GLSL/WGSL renderers); may clip at sub-pixel edges under extreme zoom
|
|
21
12
|
// @ts-ignore - three is a peer dependency
|
|
22
13
|
const LOG_BAND_TEX_W = 12;
|
|
23
14
|
const BAND_TEX_W_MASK = (1 << LOG_BAND_TEX_W) - 1;
|
|
@@ -79,10 +70,6 @@ const calcBandLoc = Fn(([glyphX, glyphY, offset]) => {
|
|
|
79
70
|
bx.assign(bx.bitAnd(BAND_TEX_W_MASK));
|
|
80
71
|
return ivec2(bx, by);
|
|
81
72
|
});
|
|
82
|
-
// Create a Three.js Mesh from SlugGPUData using TSL node materials.
|
|
83
|
-
// Returns a single transparent mesh suitable for any Three.js scene.
|
|
84
|
-
// The Slug algorithm evaluates per-fragment coverage analytically,
|
|
85
|
-
// so no stencil buffer or multi-pass rendering is required
|
|
86
73
|
function createSlugTSLMesh(gpuData, color) {
|
|
87
74
|
const attrs = unpackSlugVertices(gpuData);
|
|
88
75
|
const geo = new THREE.BufferGeometry();
|
|
@@ -109,16 +96,11 @@ function createSlugTSLMesh(gpuData, color) {
|
|
|
109
96
|
bandTex.magFilter = THREE.NearestFilter;
|
|
110
97
|
bandTex.generateMipmaps = false;
|
|
111
98
|
bandTex.needsUpdate = true;
|
|
112
|
-
// Varyings: vertex attributes interpolated to fragment stage
|
|
113
99
|
const vTexcoord = varying(attribute('slugTexcoord', 'vec2'), 'v_texcoord');
|
|
114
100
|
const vBanding = varying(attribute('slugBanding', 'vec4'), 'v_banding');
|
|
115
101
|
const vGlyph = varying(attribute('slugGlyph', 'vec4'), 'v_glyph');
|
|
116
102
|
const vColor = varying(attribute('slugColor', 'vec4'), 'v_color');
|
|
117
|
-
// Color uniform (allows dynamic color updates)
|
|
118
103
|
const textColor = uniform(new THREE.Color(color?.r ?? 1, color?.g ?? 1, color?.b ?? 1));
|
|
119
|
-
// Main per-fragment evaluation: SlugRenderSingle ported to TSL
|
|
120
|
-
// Evaluates horizontal and vertical band loops to compute
|
|
121
|
-
// analytic winding-number coverage
|
|
122
104
|
const slugRenderSingle = Fn(([renderCoord, emsPerPixel, bandTransform, glyphData]) => {
|
|
123
105
|
const pixelsPerEm = vec2(float(1).div(emsPerPixel.x), float(1).div(emsPerPixel.y));
|
|
124
106
|
const glyphLocX = glyphData.x.toInt();
|
|
@@ -127,7 +109,6 @@ function createSlugTSLMesh(gpuData, color) {
|
|
|
127
109
|
const bandMaxY = glyphData.w.toInt().bitAnd(0xFF);
|
|
128
110
|
const bandIdxX = max(min(renderCoord.x.mul(bandTransform.x).add(bandTransform.z).toInt(), bandMaxX), int(0));
|
|
129
111
|
const bandIdxY = max(min(renderCoord.y.mul(bandTransform.y).add(bandTransform.w).toInt(), bandMaxY), int(0));
|
|
130
|
-
// Horizontal band loop
|
|
131
112
|
const xcov = float(0).toVar();
|
|
132
113
|
const xwgt = float(0).toVar();
|
|
133
114
|
const hbandData = textureLoad(bandTex, ivec2(glyphLocX.add(bandIdxY), glyphLocY));
|
|
@@ -160,7 +141,6 @@ function createSlugTSLMesh(gpuData, color) {
|
|
|
160
141
|
});
|
|
161
142
|
hIdx.addAssign(1);
|
|
162
143
|
});
|
|
163
|
-
// Vertical band loop
|
|
164
144
|
const ycov = float(0).toVar();
|
|
165
145
|
const ywgt = float(0).toVar();
|
|
166
146
|
const vbandOffset = bandMaxY.add(1).add(bandIdxX);
|
|
@@ -194,17 +174,14 @@ function createSlugTSLMesh(gpuData, color) {
|
|
|
194
174
|
});
|
|
195
175
|
vIdx.addAssign(1);
|
|
196
176
|
});
|
|
197
|
-
// CalcCoverage (nonzero winding rule)
|
|
198
177
|
const coverage = max(abs(xcov.mul(xwgt).add(ycov.mul(ywgt))).div(max(xwgt.add(ywgt), float(1.0 / 65536.0))), min(abs(xcov), abs(ycov)));
|
|
199
178
|
return clamp(coverage, 0, 1);
|
|
200
179
|
});
|
|
201
|
-
// Top-level fragment node
|
|
202
180
|
const fragmentNode = Fn(() => {
|
|
203
181
|
const emsPerPixel = fwidth(vTexcoord);
|
|
204
182
|
const coverage = slugRenderSingle(vTexcoord, emsPerPixel, vBanding, vGlyph);
|
|
205
183
|
return vec4(textColor.x, textColor.y, textColor.z, vColor.w.mul(coverage));
|
|
206
184
|
})();
|
|
207
|
-
// Material & mesh
|
|
208
185
|
const material = new MeshBasicNodeMaterial();
|
|
209
186
|
material.fragmentNode = fragmentNode;
|
|
210
187
|
material.transparent = true;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var vertGLSL = "#version 300 es\n// GLSL 300 es port of the Slug vertex shader\n// Eric Lengyel, MIT License, 2017\n\nprecision highp float;\nprecision highp int;\n\n// Per-vertex attributes (5 x vec4, matching Slug reference layout)\nlayout(location = 0) in vec4 a_pos; // .xy = object-space position, .zw = outward normal\nlayout(location = 1) in vec4 a_tex; // .xy = em-space sample coords, .z = packed glyph loc, .w = packed band max + flags\nlayout(location = 2) in vec4 a_jac; // inverse Jacobian (2x2): (j00, j01, j10, j11)\nlayout(location = 3) in vec4 a_bnd; // (bandScaleX, bandScaleY, bandOffsetX, bandOffsetY)\nlayout(location = 4) in vec4 a_col; // vertex color RGBA\n\nuniform mat4 slug_matrix; // MVP matrix (rows as vec4s)\nuniform vec2 slug_viewport; // viewport dimensions in pixels\n\nout vec4 v_color;\nout vec2 v_texcoord;\nflat out vec4 v_banding;\nflat out ivec4 v_glyph;\n\nvoid SlugUnpack(vec4 tex, vec4 bnd, out vec4 vbnd, out ivec4 vgly) {\n uvec2 g = floatBitsToUint(tex.zw);\n vgly = ivec4(g.x & 0xFFFFu, g.x >> 16u, g.y & 0xFFFFu, g.y >> 16u);\n vbnd = bnd;\n}\n\nvec2 SlugDilate(vec4 pos, vec4 tex, vec4 jac, vec4 m0, vec4 m1, vec4 m3, vec2 dim, out vec2 vpos) {\n vec2 n = normalize(pos.zw);\n float s = dot(m3.xy, pos.xy) + m3.w;\n float t = dot(m3.xy, n);\n\n float u = (s * dot(m0.xy, n) - t * (dot(m0.xy, pos.xy) + m0.w)) * dim.x;\n float v = (s * dot(m1.xy, n) - t * (dot(m1.xy, pos.xy) + m1.w)) * dim.y;\n\n float s2 = s * s;\n float st = s * t;\n float uv = u * u + v * v;\n vec2 d = pos.zw * (s2 * (st + sqrt(uv)) / (uv - st * st));\n\n vpos = pos.xy + d;\n return vec2(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\n}\n\nvoid main() {\n vec2 p;\n\n // Dynamic dilation: expand quad by a pixel to prevent edge clipping
|
|
3
|
+
var vertGLSL = "#version 300 es\n// GLSL 300 es port of the Slug vertex shader\n// Eric Lengyel, MIT License, 2017\n\nprecision highp float;\nprecision highp int;\n\n// Per-vertex attributes (5 x vec4, matching Slug reference layout)\nlayout(location = 0) in vec4 a_pos; // .xy = object-space position, .zw = outward normal\nlayout(location = 1) in vec4 a_tex; // .xy = em-space sample coords, .z = packed glyph loc, .w = packed band max + flags\nlayout(location = 2) in vec4 a_jac; // inverse Jacobian (2x2): (j00, j01, j10, j11)\nlayout(location = 3) in vec4 a_bnd; // (bandScaleX, bandScaleY, bandOffsetX, bandOffsetY)\nlayout(location = 4) in vec4 a_col; // vertex color RGBA\n\nuniform mat4 slug_matrix; // MVP matrix (rows as vec4s)\nuniform vec2 slug_viewport; // viewport dimensions in pixels\n\nout vec4 v_color;\nout vec2 v_texcoord;\nflat out vec4 v_banding;\nflat out ivec4 v_glyph;\n\nvoid SlugUnpack(vec4 tex, vec4 bnd, out vec4 vbnd, out ivec4 vgly) {\n uvec2 g = floatBitsToUint(tex.zw);\n vgly = ivec4(g.x & 0xFFFFu, g.x >> 16u, g.y & 0xFFFFu, g.y >> 16u);\n vbnd = bnd;\n}\n\nvec2 SlugDilate(vec4 pos, vec4 tex, vec4 jac, vec4 m0, vec4 m1, vec4 m3, vec2 dim, out vec2 vpos) {\n vec2 n = normalize(pos.zw);\n float s = dot(m3.xy, pos.xy) + m3.w;\n float t = dot(m3.xy, n);\n\n float u = (s * dot(m0.xy, n) - t * (dot(m0.xy, pos.xy) + m0.w)) * dim.x;\n float v = (s * dot(m1.xy, n) - t * (dot(m1.xy, pos.xy) + m1.w)) * dim.y;\n\n float s2 = s * s;\n float st = s * t;\n float uv = u * u + v * v;\n vec2 d = pos.zw * (s2 * (st + sqrt(uv)) / (uv - st * st));\n\n vpos = pos.xy + d;\n return vec2(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\n}\n\nvoid main() {\n vec2 p;\n\n // Dynamic dilation: expand quad by a pixel to prevent edge clipping\n v_texcoord = SlugDilate(a_pos, a_tex, a_jac,\n slug_matrix[0], slug_matrix[1], slug_matrix[3],\n slug_viewport, p);\n\n // MVP transform on dilated position\n gl_Position.x = p.x * slug_matrix[0].x + p.y * slug_matrix[0].y + slug_matrix[0].w;\n gl_Position.y = p.x * slug_matrix[1].x + p.y * slug_matrix[1].y + slug_matrix[1].w;\n gl_Position.z = p.x * slug_matrix[2].x + p.y * slug_matrix[2].y + slug_matrix[2].w;\n gl_Position.w = p.x * slug_matrix[3].x + p.y * slug_matrix[3].y + slug_matrix[3].w;\n\n SlugUnpack(a_tex, a_bnd, v_banding, v_glyph);\n v_color = a_col;\n}\n";
|
|
4
4
|
|
|
5
5
|
var fragGLSL = "#version 300 es\n// GLSL 300 es port of the Slug fragment shader\n// Eric Lengyel, MIT License, 2017\n\nprecision highp float;\nprecision highp int;\n\n#define kLogBandTextureWidth 12\n\nin vec4 v_color;\nin vec2 v_texcoord;\nflat in vec4 v_banding;\nflat in ivec4 v_glyph;\n\nuniform sampler2D curveTexture; // RGBA32F control points\nuniform highp usampler2D bandTexture; // RGBA32UI band data\n\nlayout(location = 0) out vec4 outColor;\n\nuint CalcRootCode(float y1, float y2, float y3) {\n uint i1 = floatBitsToUint(y1) >> 31u;\n uint i2 = floatBitsToUint(y2) >> 30u;\n uint i3 = floatBitsToUint(y3) >> 29u;\n\n uint shift = (i2 & 2u) | (i1 & ~2u);\n shift = (i3 & 4u) | (shift & ~4u);\n\n return (0x2E74u >> shift) & 0x0101u;\n}\n\nvec2 SolveHorizPoly(vec4 p12, vec2 p3) {\n vec2 a = p12.xy - p12.zw * 2.0 + p3;\n vec2 b = p12.xy - p12.zw;\n float ra = 1.0 / a.y;\n float rb = 0.5 / b.y;\n\n float d = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\n float t1 = (b.y - d) * ra;\n float t2 = (b.y + d) * ra;\n\n if (abs(a.y) < 1.0 / 65536.0) t1 = t2 = p12.y * rb;\n\n return vec2((a.x * t1 - b.x * 2.0) * t1 + p12.x, (a.x * t2 - b.x * 2.0) * t2 + p12.x);\n}\n\nvec2 SolveVertPoly(vec4 p12, vec2 p3) {\n vec2 a = p12.xy - p12.zw * 2.0 + p3;\n vec2 b = p12.xy - p12.zw;\n float ra = 1.0 / a.x;\n float rb = 0.5 / b.x;\n\n float d = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\n float t1 = (b.x - d) * ra;\n float t2 = (b.x + d) * ra;\n\n if (abs(a.x) < 1.0 / 65536.0) t1 = t2 = p12.x * rb;\n\n return vec2((a.y * t1 - b.y * 2.0) * t1 + p12.y, (a.y * t2 - b.y * 2.0) * t2 + p12.y);\n}\n\nivec2 CalcBandLoc(ivec2 glyphLoc, uint offset) {\n ivec2 bandLoc = ivec2(glyphLoc.x + int(offset), glyphLoc.y);\n bandLoc.y += bandLoc.x >> kLogBandTextureWidth;\n bandLoc.x &= (1 << kLogBandTextureWidth) - 1;\n return bandLoc;\n}\n\nfloat CalcCoverage(float xcov, float ycov, float xwgt, float ywgt, int flags) {\n float coverage = max(abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0), min(abs(xcov), abs(ycov)));\n\n#if defined(SLUG_EVENODD)\n if ((flags & 0x1000) == 0) {\n#endif\n coverage = clamp(coverage, 0.0, 1.0);\n#if defined(SLUG_EVENODD)\n } else {\n coverage = 1.0 - abs(1.0 - fract(coverage * 0.5) * 2.0);\n }\n#endif\n\n#if defined(SLUG_WEIGHT)\n coverage = sqrt(coverage);\n#endif\n\n return coverage;\n}\n\nfloat SlugRenderSingle(vec2 renderCoord, vec2 emsPerPixel, vec4 bandTransform, ivec4 glyphData) {\n int curveIndex;\n\n vec2 pixelsPerEm = 1.0 / emsPerPixel;\n\n ivec2 bandMax = glyphData.zw;\n bandMax.y &= 0x00FF;\n\n ivec2 bandIndex = clamp(ivec2(renderCoord * bandTransform.xy + bandTransform.zw), ivec2(0, 0), bandMax);\n ivec2 glyphLoc = glyphData.xy;\n\n float xcov = 0.0;\n float xwgt = 0.0;\n\n uvec2 hbandData = texelFetch(bandTexture, ivec2(glyphLoc.x + bandIndex.y, glyphLoc.y), 0).xy;\n ivec2 hbandLoc = CalcBandLoc(glyphLoc, hbandData.y);\n\n for (curveIndex = 0; curveIndex < int(hbandData.x); curveIndex++) {\n ivec2 curveLoc = ivec2(texelFetch(bandTexture, ivec2(hbandLoc.x + curveIndex, hbandLoc.y), 0).xy);\n\n vec4 p12 = texelFetch(curveTexture, curveLoc, 0) - vec4(renderCoord, renderCoord);\n vec2 p3 = texelFetch(curveTexture, ivec2(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) break;\n\n uint code = CalcRootCode(p12.y, p12.w, p3.y);\n if (code != 0u) {\n vec2 r = SolveHorizPoly(p12, p3) * pixelsPerEm.x;\n\n if ((code & 1u) != 0u) {\n xcov += clamp(r.x + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n\n if (code > 1u) {\n xcov -= clamp(r.y + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n float ycov = 0.0;\n float ywgt = 0.0;\n\n uvec2 vbandData = texelFetch(bandTexture, ivec2(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0).xy;\n ivec2 vbandLoc = CalcBandLoc(glyphLoc, vbandData.y);\n\n for (curveIndex = 0; curveIndex < int(vbandData.x); curveIndex++) {\n ivec2 curveLoc = ivec2(texelFetch(bandTexture, ivec2(vbandLoc.x + curveIndex, vbandLoc.y), 0).xy);\n vec4 p12 = texelFetch(curveTexture, curveLoc, 0) - vec4(renderCoord, renderCoord);\n vec2 p3 = texelFetch(curveTexture, ivec2(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) break;\n\n uint code = CalcRootCode(p12.x, p12.z, p3.x);\n if (code != 0u) {\n vec2 r = SolveVertPoly(p12, p3) * pixelsPerEm.y;\n\n if ((code & 1u) != 0u) {\n ycov -= clamp(r.x + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n\n if (code > 1u) {\n ycov += clamp(r.y + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n return CalcCoverage(xcov, ycov, xwgt, ywgt, glyphData.w);\n}\n\nfloat SlugRender(vec2 renderCoord, vec4 bandTransform, ivec4 glyphData) {\n vec2 emsPerPixel = fwidth(renderCoord);\n\n#if defined(SLUG_ADAPTIVE_SUPERSAMPLE)\n // Per-pixel rotated RGSS-4. The base RGSS offsets are rotated by a\n // unique angle per fragment (interleaved gradient noise). This converts\n // structured aliasing shimmer into uncorrelated grain that the eye\n // naturally filters out, much closer to how hardware MSAA on many small\n // triangles behaves perceptually.\n float noise = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));\n float angle = noise * 6.2831853;\n float ca = cos(angle), sa = sin(angle);\n\n // Base RGSS offsets rotated by per-pixel angle\n vec2 o0 = vec2(ca * -0.375 - sa * 0.125, sa * -0.375 + ca * 0.125) * emsPerPixel;\n vec2 o1 = vec2(ca * 0.125 - sa * 0.375, sa * 0.125 + ca * 0.375) * emsPerPixel;\n vec2 o2 = vec2(ca * 0.375 - sa * -0.125, sa * 0.375 + ca * -0.125) * emsPerPixel;\n vec2 o3 = vec2(ca * -0.125 - sa * -0.375, sa * -0.125 + ca * -0.375) * emsPerPixel;\n\n float coverage =\n SlugRenderSingle(renderCoord + o0, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o1, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o2, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o3, emsPerPixel, bandTransform, glyphData);\n return coverage * 0.25;\n#else\n return SlugRenderSingle(renderCoord, emsPerPixel, bandTransform, glyphData);\n#endif\n}\n\nvoid main() {\n float coverage = SlugRender(v_texcoord, v_banding, v_glyph);\n outColor = v_color * coverage;\n}\n";
|
|
6
6
|
|
|
7
|
-
// Slug shader source re-exports
|
|
8
|
-
// The .glsl/.wgsl files are the single source of truth, imported as strings
|
|
9
|
-
// at build time via the glslPlugin in rollup.config.js
|
|
10
7
|
// @ts-ignore - resolved by rollup glslPlugin
|
|
11
8
|
const vertexShaderGLSL300 = vertGLSL;
|
|
12
9
|
const fragmentShaderGLSL300 = fragGLSL;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
var vertGLSL = "#version 300 es\n// GLSL 300 es port of the Slug vertex shader\n// Eric Lengyel, MIT License, 2017\n\nprecision highp float;\nprecision highp int;\n\n// Per-vertex attributes (5 x vec4, matching Slug reference layout)\nlayout(location = 0) in vec4 a_pos; // .xy = object-space position, .zw = outward normal\nlayout(location = 1) in vec4 a_tex; // .xy = em-space sample coords, .z = packed glyph loc, .w = packed band max + flags\nlayout(location = 2) in vec4 a_jac; // inverse Jacobian (2x2): (j00, j01, j10, j11)\nlayout(location = 3) in vec4 a_bnd; // (bandScaleX, bandScaleY, bandOffsetX, bandOffsetY)\nlayout(location = 4) in vec4 a_col; // vertex color RGBA\n\nuniform mat4 slug_matrix; // MVP matrix (rows as vec4s)\nuniform vec2 slug_viewport; // viewport dimensions in pixels\n\nout vec4 v_color;\nout vec2 v_texcoord;\nflat out vec4 v_banding;\nflat out ivec4 v_glyph;\n\nvoid SlugUnpack(vec4 tex, vec4 bnd, out vec4 vbnd, out ivec4 vgly) {\n uvec2 g = floatBitsToUint(tex.zw);\n vgly = ivec4(g.x & 0xFFFFu, g.x >> 16u, g.y & 0xFFFFu, g.y >> 16u);\n vbnd = bnd;\n}\n\nvec2 SlugDilate(vec4 pos, vec4 tex, vec4 jac, vec4 m0, vec4 m1, vec4 m3, vec2 dim, out vec2 vpos) {\n vec2 n = normalize(pos.zw);\n float s = dot(m3.xy, pos.xy) + m3.w;\n float t = dot(m3.xy, n);\n\n float u = (s * dot(m0.xy, n) - t * (dot(m0.xy, pos.xy) + m0.w)) * dim.x;\n float v = (s * dot(m1.xy, n) - t * (dot(m1.xy, pos.xy) + m1.w)) * dim.y;\n\n float s2 = s * s;\n float st = s * t;\n float uv = u * u + v * v;\n vec2 d = pos.zw * (s2 * (st + sqrt(uv)) / (uv - st * st));\n\n vpos = pos.xy + d;\n return vec2(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\n}\n\nvoid main() {\n vec2 p;\n\n // Dynamic dilation: expand quad by a pixel to prevent edge clipping
|
|
1
|
+
var vertGLSL = "#version 300 es\n// GLSL 300 es port of the Slug vertex shader\n// Eric Lengyel, MIT License, 2017\n\nprecision highp float;\nprecision highp int;\n\n// Per-vertex attributes (5 x vec4, matching Slug reference layout)\nlayout(location = 0) in vec4 a_pos; // .xy = object-space position, .zw = outward normal\nlayout(location = 1) in vec4 a_tex; // .xy = em-space sample coords, .z = packed glyph loc, .w = packed band max + flags\nlayout(location = 2) in vec4 a_jac; // inverse Jacobian (2x2): (j00, j01, j10, j11)\nlayout(location = 3) in vec4 a_bnd; // (bandScaleX, bandScaleY, bandOffsetX, bandOffsetY)\nlayout(location = 4) in vec4 a_col; // vertex color RGBA\n\nuniform mat4 slug_matrix; // MVP matrix (rows as vec4s)\nuniform vec2 slug_viewport; // viewport dimensions in pixels\n\nout vec4 v_color;\nout vec2 v_texcoord;\nflat out vec4 v_banding;\nflat out ivec4 v_glyph;\n\nvoid SlugUnpack(vec4 tex, vec4 bnd, out vec4 vbnd, out ivec4 vgly) {\n uvec2 g = floatBitsToUint(tex.zw);\n vgly = ivec4(g.x & 0xFFFFu, g.x >> 16u, g.y & 0xFFFFu, g.y >> 16u);\n vbnd = bnd;\n}\n\nvec2 SlugDilate(vec4 pos, vec4 tex, vec4 jac, vec4 m0, vec4 m1, vec4 m3, vec2 dim, out vec2 vpos) {\n vec2 n = normalize(pos.zw);\n float s = dot(m3.xy, pos.xy) + m3.w;\n float t = dot(m3.xy, n);\n\n float u = (s * dot(m0.xy, n) - t * (dot(m0.xy, pos.xy) + m0.w)) * dim.x;\n float v = (s * dot(m1.xy, n) - t * (dot(m1.xy, pos.xy) + m1.w)) * dim.y;\n\n float s2 = s * s;\n float st = s * t;\n float uv = u * u + v * v;\n vec2 d = pos.zw * (s2 * (st + sqrt(uv)) / (uv - st * st));\n\n vpos = pos.xy + d;\n return vec2(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\n}\n\nvoid main() {\n vec2 p;\n\n // Dynamic dilation: expand quad by a pixel to prevent edge clipping\n v_texcoord = SlugDilate(a_pos, a_tex, a_jac,\n slug_matrix[0], slug_matrix[1], slug_matrix[3],\n slug_viewport, p);\n\n // MVP transform on dilated position\n gl_Position.x = p.x * slug_matrix[0].x + p.y * slug_matrix[0].y + slug_matrix[0].w;\n gl_Position.y = p.x * slug_matrix[1].x + p.y * slug_matrix[1].y + slug_matrix[1].w;\n gl_Position.z = p.x * slug_matrix[2].x + p.y * slug_matrix[2].y + slug_matrix[2].w;\n gl_Position.w = p.x * slug_matrix[3].x + p.y * slug_matrix[3].y + slug_matrix[3].w;\n\n SlugUnpack(a_tex, a_bnd, v_banding, v_glyph);\n v_color = a_col;\n}\n";
|
|
2
2
|
|
|
3
3
|
var fragGLSL = "#version 300 es\n// GLSL 300 es port of the Slug fragment shader\n// Eric Lengyel, MIT License, 2017\n\nprecision highp float;\nprecision highp int;\n\n#define kLogBandTextureWidth 12\n\nin vec4 v_color;\nin vec2 v_texcoord;\nflat in vec4 v_banding;\nflat in ivec4 v_glyph;\n\nuniform sampler2D curveTexture; // RGBA32F control points\nuniform highp usampler2D bandTexture; // RGBA32UI band data\n\nlayout(location = 0) out vec4 outColor;\n\nuint CalcRootCode(float y1, float y2, float y3) {\n uint i1 = floatBitsToUint(y1) >> 31u;\n uint i2 = floatBitsToUint(y2) >> 30u;\n uint i3 = floatBitsToUint(y3) >> 29u;\n\n uint shift = (i2 & 2u) | (i1 & ~2u);\n shift = (i3 & 4u) | (shift & ~4u);\n\n return (0x2E74u >> shift) & 0x0101u;\n}\n\nvec2 SolveHorizPoly(vec4 p12, vec2 p3) {\n vec2 a = p12.xy - p12.zw * 2.0 + p3;\n vec2 b = p12.xy - p12.zw;\n float ra = 1.0 / a.y;\n float rb = 0.5 / b.y;\n\n float d = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\n float t1 = (b.y - d) * ra;\n float t2 = (b.y + d) * ra;\n\n if (abs(a.y) < 1.0 / 65536.0) t1 = t2 = p12.y * rb;\n\n return vec2((a.x * t1 - b.x * 2.0) * t1 + p12.x, (a.x * t2 - b.x * 2.0) * t2 + p12.x);\n}\n\nvec2 SolveVertPoly(vec4 p12, vec2 p3) {\n vec2 a = p12.xy - p12.zw * 2.0 + p3;\n vec2 b = p12.xy - p12.zw;\n float ra = 1.0 / a.x;\n float rb = 0.5 / b.x;\n\n float d = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\n float t1 = (b.x - d) * ra;\n float t2 = (b.x + d) * ra;\n\n if (abs(a.x) < 1.0 / 65536.0) t1 = t2 = p12.x * rb;\n\n return vec2((a.y * t1 - b.y * 2.0) * t1 + p12.y, (a.y * t2 - b.y * 2.0) * t2 + p12.y);\n}\n\nivec2 CalcBandLoc(ivec2 glyphLoc, uint offset) {\n ivec2 bandLoc = ivec2(glyphLoc.x + int(offset), glyphLoc.y);\n bandLoc.y += bandLoc.x >> kLogBandTextureWidth;\n bandLoc.x &= (1 << kLogBandTextureWidth) - 1;\n return bandLoc;\n}\n\nfloat CalcCoverage(float xcov, float ycov, float xwgt, float ywgt, int flags) {\n float coverage = max(abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0), min(abs(xcov), abs(ycov)));\n\n#if defined(SLUG_EVENODD)\n if ((flags & 0x1000) == 0) {\n#endif\n coverage = clamp(coverage, 0.0, 1.0);\n#if defined(SLUG_EVENODD)\n } else {\n coverage = 1.0 - abs(1.0 - fract(coverage * 0.5) * 2.0);\n }\n#endif\n\n#if defined(SLUG_WEIGHT)\n coverage = sqrt(coverage);\n#endif\n\n return coverage;\n}\n\nfloat SlugRenderSingle(vec2 renderCoord, vec2 emsPerPixel, vec4 bandTransform, ivec4 glyphData) {\n int curveIndex;\n\n vec2 pixelsPerEm = 1.0 / emsPerPixel;\n\n ivec2 bandMax = glyphData.zw;\n bandMax.y &= 0x00FF;\n\n ivec2 bandIndex = clamp(ivec2(renderCoord * bandTransform.xy + bandTransform.zw), ivec2(0, 0), bandMax);\n ivec2 glyphLoc = glyphData.xy;\n\n float xcov = 0.0;\n float xwgt = 0.0;\n\n uvec2 hbandData = texelFetch(bandTexture, ivec2(glyphLoc.x + bandIndex.y, glyphLoc.y), 0).xy;\n ivec2 hbandLoc = CalcBandLoc(glyphLoc, hbandData.y);\n\n for (curveIndex = 0; curveIndex < int(hbandData.x); curveIndex++) {\n ivec2 curveLoc = ivec2(texelFetch(bandTexture, ivec2(hbandLoc.x + curveIndex, hbandLoc.y), 0).xy);\n\n vec4 p12 = texelFetch(curveTexture, curveLoc, 0) - vec4(renderCoord, renderCoord);\n vec2 p3 = texelFetch(curveTexture, ivec2(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) break;\n\n uint code = CalcRootCode(p12.y, p12.w, p3.y);\n if (code != 0u) {\n vec2 r = SolveHorizPoly(p12, p3) * pixelsPerEm.x;\n\n if ((code & 1u) != 0u) {\n xcov += clamp(r.x + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n\n if (code > 1u) {\n xcov -= clamp(r.y + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n float ycov = 0.0;\n float ywgt = 0.0;\n\n uvec2 vbandData = texelFetch(bandTexture, ivec2(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0).xy;\n ivec2 vbandLoc = CalcBandLoc(glyphLoc, vbandData.y);\n\n for (curveIndex = 0; curveIndex < int(vbandData.x); curveIndex++) {\n ivec2 curveLoc = ivec2(texelFetch(bandTexture, ivec2(vbandLoc.x + curveIndex, vbandLoc.y), 0).xy);\n vec4 p12 = texelFetch(curveTexture, curveLoc, 0) - vec4(renderCoord, renderCoord);\n vec2 p3 = texelFetch(curveTexture, ivec2(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) break;\n\n uint code = CalcRootCode(p12.x, p12.z, p3.x);\n if (code != 0u) {\n vec2 r = SolveVertPoly(p12, p3) * pixelsPerEm.y;\n\n if ((code & 1u) != 0u) {\n ycov -= clamp(r.x + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n\n if (code > 1u) {\n ycov += clamp(r.y + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n return CalcCoverage(xcov, ycov, xwgt, ywgt, glyphData.w);\n}\n\nfloat SlugRender(vec2 renderCoord, vec4 bandTransform, ivec4 glyphData) {\n vec2 emsPerPixel = fwidth(renderCoord);\n\n#if defined(SLUG_ADAPTIVE_SUPERSAMPLE)\n // Per-pixel rotated RGSS-4. The base RGSS offsets are rotated by a\n // unique angle per fragment (interleaved gradient noise). This converts\n // structured aliasing shimmer into uncorrelated grain that the eye\n // naturally filters out, much closer to how hardware MSAA on many small\n // triangles behaves perceptually.\n float noise = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));\n float angle = noise * 6.2831853;\n float ca = cos(angle), sa = sin(angle);\n\n // Base RGSS offsets rotated by per-pixel angle\n vec2 o0 = vec2(ca * -0.375 - sa * 0.125, sa * -0.375 + ca * 0.125) * emsPerPixel;\n vec2 o1 = vec2(ca * 0.125 - sa * 0.375, sa * 0.125 + ca * 0.375) * emsPerPixel;\n vec2 o2 = vec2(ca * 0.375 - sa * -0.125, sa * 0.375 + ca * -0.125) * emsPerPixel;\n vec2 o3 = vec2(ca * -0.125 - sa * -0.375, sa * -0.125 + ca * -0.375) * emsPerPixel;\n\n float coverage =\n SlugRenderSingle(renderCoord + o0, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o1, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o2, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o3, emsPerPixel, bandTransform, glyphData);\n return coverage * 0.25;\n#else\n return SlugRenderSingle(renderCoord, emsPerPixel, bandTransform, glyphData);\n#endif\n}\n\nvoid main() {\n float coverage = SlugRender(v_texcoord, v_banding, v_glyph);\n outColor = v_color * coverage;\n}\n";
|
|
4
4
|
|
|
5
|
-
// Slug shader source re-exports
|
|
6
|
-
// The .glsl/.wgsl files are the single source of truth, imported as strings
|
|
7
|
-
// at build time via the glslPlugin in rollup.config.js
|
|
8
5
|
// @ts-ignore - resolved by rollup glslPlugin
|
|
9
6
|
const vertexShaderGLSL300 = vertGLSL;
|
|
10
7
|
const fragmentShaderGLSL300 = fragGLSL;
|
|
@@ -4,9 +4,6 @@ var vertWGSL = "// WGSL port of the reference Slug vertex shader\n// Original HL
|
|
|
4
4
|
|
|
5
5
|
var fragWGSL = "// WGSL port of the reference Slug pixel shader\n// by Eric Lengyel, MIT License, Copyright 2017\n\nconst kLogBandTextureWidth: u32 = 12u;\n\n@group(0) @binding(1) var curveTexture: texture_2d<f32>;\n@group(0) @binding(2) var bandTexture: texture_2d<u32>;\n\nstruct FragmentInput {\n @location(0) color: vec4<f32>,\n @location(1) texcoord: vec2<f32>,\n @location(2) @interpolate(flat) banding: vec4<f32>,\n @location(3) @interpolate(flat) glyph: vec4<i32>,\n};\n\nfn CalcRootCode(y1: f32, y2: f32, y3: f32) -> u32 {\n let i1 = bitcast<u32>(y1) >> 31u;\n let i2 = bitcast<u32>(y2) >> 30u;\n let i3 = bitcast<u32>(y3) >> 29u;\n\n var shift = (i2 & 2u) | (i1 & ~2u);\n shift = (i3 & 4u) | (shift & ~4u);\n\n return (0x2E74u >> shift) & 0x0101u;\n}\n\nfn SolveHorizPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\n let a = p12.xy - p12.zw * 2.0 + p3;\n let b = p12.xy - p12.zw;\n let ra = 1.0 / a.y;\n let rb = 0.5 / b.y;\n\n let d = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\n var t1 = (b.y - d) * ra;\n var t2 = (b.y + d) * ra;\n\n if (abs(a.y) < 1.0 / 65536.0) {\n t1 = p12.y * rb;\n t2 = t1;\n }\n\n return vec2<f32>(\n (a.x * t1 - b.x * 2.0) * t1 + p12.x,\n (a.x * t2 - b.x * 2.0) * t2 + p12.x\n );\n}\n\nfn SolveVertPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\n let a = p12.xy - p12.zw * 2.0 + p3;\n let b = p12.xy - p12.zw;\n let ra = 1.0 / a.x;\n let rb = 0.5 / b.x;\n\n let d = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\n var t1 = (b.x - d) * ra;\n var t2 = (b.x + d) * ra;\n\n if (abs(a.x) < 1.0 / 65536.0) {\n t1 = p12.x * rb;\n t2 = t1;\n }\n\n return vec2<f32>(\n (a.y * t1 - b.y * 2.0) * t1 + p12.y,\n (a.y * t2 - b.y * 2.0) * t2 + p12.y\n );\n}\n\nfn CalcBandLoc(glyphLoc: vec2<i32>, offset: u32) -> vec2<i32> {\n var bandLoc = vec2<i32>(glyphLoc.x + i32(offset), glyphLoc.y);\n let bandShift = kLogBandTextureWidth;\n let bandMask = i32((1u << bandShift) - 1u);\n bandLoc.y += bandLoc.x >> bandShift;\n bandLoc.x &= bandMask;\n return bandLoc;\n}\n\nfn CalcCoverage(xcov: f32, ycov: f32, xwgt: f32, ywgt: f32) -> f32 {\n var coverage = max(\n abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0),\n min(abs(xcov), abs(ycov))\n );\n coverage = clamp(coverage, 0.0, 1.0);\n return coverage;\n}\n\nfn SlugRender(renderCoord: vec2<f32>, bandTransform: vec4<f32>, glyphData: vec4<i32>) -> f32 {\n let emsPerPixel = fwidth(renderCoord);\n let pixelsPerEm = 1.0 / emsPerPixel;\n\n var bandMax = glyphData.zw;\n bandMax.y &= 0x00FF;\n\n let bandIndex = clamp(\n vec2<i32>(renderCoord * bandTransform.xy + bandTransform.zw),\n vec2<i32>(0, 0),\n bandMax\n );\n let glyphLoc = glyphData.xy;\n\n var xcov = 0.0;\n var xwgt = 0.0;\n\n let hbandData = textureLoad(bandTexture, vec2<i32>(glyphLoc.x + bandIndex.y, glyphLoc.y), 0).xy;\n let hbandLoc = CalcBandLoc(glyphLoc, hbandData.y);\n\n for (var ci = 0; ci < i32(hbandData.x); ci++) {\n let curveLoc = vec2<i32>(textureLoad(bandTexture, vec2<i32>(hbandLoc.x + ci, hbandLoc.y), 0).xy);\n\n let p12 = textureLoad(curveTexture, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\n let p3 = textureLoad(curveTexture, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }\n\n let code = CalcRootCode(p12.y, p12.w, p3.y);\n if (code != 0u) {\n let r = SolveHorizPoly(p12, p3) * pixelsPerEm.x;\n\n if ((code & 1u) != 0u) {\n xcov += clamp(r.x + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n if (code > 1u) {\n xcov -= clamp(r.y + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n var ycov = 0.0;\n var ywgt = 0.0;\n\n let vbandData = textureLoad(bandTexture, vec2<i32>(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0).xy;\n let vbandLoc = CalcBandLoc(glyphLoc, vbandData.y);\n\n for (var ci = 0; ci < i32(vbandData.x); ci++) {\n let curveLoc = vec2<i32>(textureLoad(bandTexture, vec2<i32>(vbandLoc.x + ci, vbandLoc.y), 0).xy);\n let p12 = textureLoad(curveTexture, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\n let p3 = textureLoad(curveTexture, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }\n\n let code = CalcRootCode(p12.x, p12.z, p3.x);\n if (code != 0u) {\n let r = SolveVertPoly(p12, p3) * pixelsPerEm.y;\n\n if ((code & 1u) != 0u) {\n ycov -= clamp(r.x + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n if (code > 1u) {\n ycov += clamp(r.y + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n return CalcCoverage(xcov, ycov, xwgt, ywgt);\n}\n\n@fragment\nfn fs_main(in: FragmentInput) -> @location(0) vec4<f32> {\n let coverage = SlugRender(in.texcoord, in.banding, in.glyph);\n return in.color * coverage;\n}\n";
|
|
6
6
|
|
|
7
|
-
// Slug shader source re-exports
|
|
8
|
-
// The .glsl/.wgsl files are the single source of truth, imported as strings
|
|
9
|
-
// at build time via the glslPlugin in rollup.config.js
|
|
10
7
|
// @ts-ignore - resolved by rollup glslPlugin
|
|
11
8
|
const vertexShaderWGSL = vertWGSL;
|
|
12
9
|
const fragmentShaderWGSL = fragWGSL;
|
|
@@ -2,9 +2,6 @@ var vertWGSL = "// WGSL port of the reference Slug vertex shader\n// Original HL
|
|
|
2
2
|
|
|
3
3
|
var fragWGSL = "// WGSL port of the reference Slug pixel shader\n// by Eric Lengyel, MIT License, Copyright 2017\n\nconst kLogBandTextureWidth: u32 = 12u;\n\n@group(0) @binding(1) var curveTexture: texture_2d<f32>;\n@group(0) @binding(2) var bandTexture: texture_2d<u32>;\n\nstruct FragmentInput {\n @location(0) color: vec4<f32>,\n @location(1) texcoord: vec2<f32>,\n @location(2) @interpolate(flat) banding: vec4<f32>,\n @location(3) @interpolate(flat) glyph: vec4<i32>,\n};\n\nfn CalcRootCode(y1: f32, y2: f32, y3: f32) -> u32 {\n let i1 = bitcast<u32>(y1) >> 31u;\n let i2 = bitcast<u32>(y2) >> 30u;\n let i3 = bitcast<u32>(y3) >> 29u;\n\n var shift = (i2 & 2u) | (i1 & ~2u);\n shift = (i3 & 4u) | (shift & ~4u);\n\n return (0x2E74u >> shift) & 0x0101u;\n}\n\nfn SolveHorizPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\n let a = p12.xy - p12.zw * 2.0 + p3;\n let b = p12.xy - p12.zw;\n let ra = 1.0 / a.y;\n let rb = 0.5 / b.y;\n\n let d = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\n var t1 = (b.y - d) * ra;\n var t2 = (b.y + d) * ra;\n\n if (abs(a.y) < 1.0 / 65536.0) {\n t1 = p12.y * rb;\n t2 = t1;\n }\n\n return vec2<f32>(\n (a.x * t1 - b.x * 2.0) * t1 + p12.x,\n (a.x * t2 - b.x * 2.0) * t2 + p12.x\n );\n}\n\nfn SolveVertPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\n let a = p12.xy - p12.zw * 2.0 + p3;\n let b = p12.xy - p12.zw;\n let ra = 1.0 / a.x;\n let rb = 0.5 / b.x;\n\n let d = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\n var t1 = (b.x - d) * ra;\n var t2 = (b.x + d) * ra;\n\n if (abs(a.x) < 1.0 / 65536.0) {\n t1 = p12.x * rb;\n t2 = t1;\n }\n\n return vec2<f32>(\n (a.y * t1 - b.y * 2.0) * t1 + p12.y,\n (a.y * t2 - b.y * 2.0) * t2 + p12.y\n );\n}\n\nfn CalcBandLoc(glyphLoc: vec2<i32>, offset: u32) -> vec2<i32> {\n var bandLoc = vec2<i32>(glyphLoc.x + i32(offset), glyphLoc.y);\n let bandShift = kLogBandTextureWidth;\n let bandMask = i32((1u << bandShift) - 1u);\n bandLoc.y += bandLoc.x >> bandShift;\n bandLoc.x &= bandMask;\n return bandLoc;\n}\n\nfn CalcCoverage(xcov: f32, ycov: f32, xwgt: f32, ywgt: f32) -> f32 {\n var coverage = max(\n abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0),\n min(abs(xcov), abs(ycov))\n );\n coverage = clamp(coverage, 0.0, 1.0);\n return coverage;\n}\n\nfn SlugRender(renderCoord: vec2<f32>, bandTransform: vec4<f32>, glyphData: vec4<i32>) -> f32 {\n let emsPerPixel = fwidth(renderCoord);\n let pixelsPerEm = 1.0 / emsPerPixel;\n\n var bandMax = glyphData.zw;\n bandMax.y &= 0x00FF;\n\n let bandIndex = clamp(\n vec2<i32>(renderCoord * bandTransform.xy + bandTransform.zw),\n vec2<i32>(0, 0),\n bandMax\n );\n let glyphLoc = glyphData.xy;\n\n var xcov = 0.0;\n var xwgt = 0.0;\n\n let hbandData = textureLoad(bandTexture, vec2<i32>(glyphLoc.x + bandIndex.y, glyphLoc.y), 0).xy;\n let hbandLoc = CalcBandLoc(glyphLoc, hbandData.y);\n\n for (var ci = 0; ci < i32(hbandData.x); ci++) {\n let curveLoc = vec2<i32>(textureLoad(bandTexture, vec2<i32>(hbandLoc.x + ci, hbandLoc.y), 0).xy);\n\n let p12 = textureLoad(curveTexture, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\n let p3 = textureLoad(curveTexture, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }\n\n let code = CalcRootCode(p12.y, p12.w, p3.y);\n if (code != 0u) {\n let r = SolveHorizPoly(p12, p3) * pixelsPerEm.x;\n\n if ((code & 1u) != 0u) {\n xcov += clamp(r.x + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n if (code > 1u) {\n xcov -= clamp(r.y + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n var ycov = 0.0;\n var ywgt = 0.0;\n\n let vbandData = textureLoad(bandTexture, vec2<i32>(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0).xy;\n let vbandLoc = CalcBandLoc(glyphLoc, vbandData.y);\n\n for (var ci = 0; ci < i32(vbandData.x); ci++) {\n let curveLoc = vec2<i32>(textureLoad(bandTexture, vec2<i32>(vbandLoc.x + ci, vbandLoc.y), 0).xy);\n let p12 = textureLoad(curveTexture, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\n let p3 = textureLoad(curveTexture, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }\n\n let code = CalcRootCode(p12.x, p12.z, p3.x);\n if (code != 0u) {\n let r = SolveVertPoly(p12, p3) * pixelsPerEm.y;\n\n if ((code & 1u) != 0u) {\n ycov -= clamp(r.x + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n if (code > 1u) {\n ycov += clamp(r.y + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n return CalcCoverage(xcov, ycov, xwgt, ywgt);\n}\n\n@fragment\nfn fs_main(in: FragmentInput) -> @location(0) vec4<f32> {\n let coverage = SlugRender(in.texcoord, in.banding, in.glyph);\n return in.color * coverage;\n}\n";
|
|
4
4
|
|
|
5
|
-
// Slug shader source re-exports
|
|
6
|
-
// The .glsl/.wgsl files are the single source of truth, imported as strings
|
|
7
|
-
// at build time via the glslPlugin in rollup.config.js
|
|
8
5
|
// @ts-ignore - resolved by rollup glslPlugin
|
|
9
6
|
const vertexShaderWGSL = vertWGSL;
|
|
10
7
|
const fragmentShaderWGSL = fragWGSL;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "three-text",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "3D
|
|
3
|
+
"version": "0.6.2",
|
|
4
|
+
"description": "3D font rendering and text layout engine for the web",
|
|
5
5
|
"main": "dist/three/index.cjs",
|
|
6
6
|
"module": "dist/three/index.js",
|
|
7
7
|
"type": "module",
|