lottery
🎉🌟✨🎈年会抽奖程序,基于 Express + Three.js的 3D 球体抽奖程序,奖品🧧🎁,文字,图片,抽奖规则均可配置,😜抽奖人员信息Excel一键导入😍,抽奖结果Excel导出😎,给你的抽奖活动带来全新酷炫体验🚀🚀🚀
Utility classes for Text rendering in Three.js using Bitmap fonts and MSDF (multi-channel signed distance fields).
git clone https://github.com/leochocolat/three-msdf-text-utils.gitleochocolat/three-msdf-text-utilsText rendering utilities for Three.js using MSDF (multi-channel signed distance fields) and bitmap fonts. Provides crisp text at any scale with support for WebGL and WebGPU.
Forked from three-bmfont-text.
MSDFTextNodeMaterialMSDF text rendering requires two assets: bitmap font data (glyph metrics) and an MSDF atlas texture. You can either pre-generate these at build time or generate them at runtime in the browser.
Pre-generating assets gives you full control over quality and reduces runtime overhead.
Tools:
generateMSDFForked from @zappar/msdf-generator
Generate MSDF assets directly in the browser from any TTF font file. This approach is convenient for dynamic font loading or when you want to support user-uploaded fonts.
Setup:
Download the required WebAssembly files and place them in your public folder:
Usage:
import { MSDFTextGeometry, MSDFTextMaterial, generateMSDF } from "three-msdf-text-utils";
import * as THREE from "three";
generateMSDF('./fonts/roboto.ttf', {
workerUrl: '/msdfgen/worker.bundled.js',
wasmUrl: '/msdfgen/msdfgen.wasm',
}).then(({ font, atlas }) => {
const geometry = new MSDFTextGeometry({
text: "Hello World",
font: font.data,
});
const material = new MSDFTextMaterial();
material.uniforms.uMap.value = atlas;
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}).catch((error) => {
console.error('Font generation failed:', error);
});
See the live demo and source code for a complete example with font switching.
More details about this can be found here @zappar/msdf-generator
npm install three-msdf-text-utils
import { MSDFTextGeometry, MSDFTextMaterial } from "three-msdf-text-utils";
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
import * as THREE from 'three';
Promise.all([
loadFontAtlas("./fonts/roboto/roboto-regular.png"),
loadFont("./fonts/roboto/roboto-regular.fnt"),
]).then(([atlas, font]) => {
const geometry = new MSDFTextGeometry({
text: "Hello World",
font: font.data,
});
const material = new MSDFTextMaterial();
material.uniforms.uMap.value = atlas;
const mesh = new THREE.Mesh(geometry, material);
});
function loadFontAtlas(path) {
const promise = new Promise((resolve, reject) => {
const loader = new THREE.TextureLoader();
loader.load(path, resolve);
});
return promise;
}
function loadFont(path) {
const promise = new Promise((resolve, reject) => {
const loader = new FontLoader();
loader.load(path, resolve);
});
return promise;
}
const geometry = new MSDFTextGeometry(options);
Options can be an object, or a String – equivalent to { text: str }.
flipY (boolean): whether the texture will be Y-flipped (default: true)multipage (boolean): whether to construct this geometry with an extra buffer containing page IDs. This is necessary for multi-texture fonts (default: false)font (required) - the BMFont definition which holds chars, kernings, etctext (string) - the text to layout. Newline characters (\n) will cause line breakswidth (number) - the desired width of the text box, causes word-wrapping and clipping in "pre" mode. Leave as undefined to remove word-wrapping (default behaviour)mode (string) - a mode for word-wrapper; can be 'pre' (maintain spacing), or 'nowrap' (collapse whitespace but only break on newline characters), otherwise assumes normal word-wrap behaviour (collapse whitespace, break at width or newlines)align (string) - can be 'left', 'center' or 'right' (default: 'left')letterSpacing (number) - the letter spacing in pixels (default: 0)lineHeight (number) - the line height in pixels (default: font.common.lineHeight)tabSize (number) - the number of spaces to use in a single tab (default: 4)start (number) - the starting index into the text to layout (default: 0)end (number) - the ending index (exclusive) into the text to layout (default: text.length)update(options)
Re-builds the geometry using the given options. Any options not specified here will default to those set in the constructor. This method will recompute the text layout and rebuild the WebGL buffers. Options can be an object, or a String (equivalent to { text: str }).
layout
Text Layout instance with attributes: width, height, descender, ascender, xHeight, baseline, capHeight, lineHeight, linesTotal, lettersTotal
visibleGlyphs
A filtered set from geometry.layout.glyphs intended to align with the vertex data being used by the underlying BufferAttributes. This is an array of { line, position, index, data } objects (see here).
Besides the basic geometry attributes. There are some text specific attributes, mostly useful for animation purposes.
positionuv : uv coordinates used to map the right letter in the right letter quadcenter : center of each letter quadlayoutUv : uv coordinates of the full text block.glyphUv : uv coordinates of each individual letter quad.glyphResolution : resolution of each individual letter quad.lineIndex : index of each linelineLettersTotal: total amount of letters in each lineslineLetterIndex: index of each letter by linelineWordsTotal: total amount of words by linelineWordIndex: index of each word by linewordIndex: index of each wordletterIndex: index of each letterExtends Three.js ShaderMaterial with MSDF-specific rendering.
const material = new MSDFTextMaterial(); material.uniforms.uMap.value = atlas;
const defaultOptions = {
side: THREE.FrontSide,
transparent: true,
defines: {
IS_SMALL: false,
},
extensions: {
derivatives: true,
},
uniforms: {
// Common
uOpacity: { value: 1 },
uColor: { value: new Color("#ffffff") },
uMap: { value: null },
// Rendering
uThreshold: { value: 0.05 },
uAlphaTest: { value: 0.01 },
// Strokes
uStrokeColor: { value: new Color("#ff0000") },
uStrokeOutsetWidth: { value: 0.0 },
uStrokeInsetWidth: { value: 0.3 },
},
vertexShader,
fragmentShader,
};
Note: IS_SMALL boolean is useful to render small fonts, it will switch the alpha rendering calculation to make them visually much smoother
If you want to make some specific text effects you can create your own glsl code in your shader material based on the MSDFTextMaterial shader.
import { uniforms } from "three-msdf-text-utils";
import * as THREE from 'three';
const material = new THREE.ShaderMaterial({
side: DoubleSide,
transparent: true,
defines: {
IS_SMALL: false,
},
extensions: {
derivatives: true,
},
uniforms: {
// Common
...uniforms.common,
// Rendering
...uniforms.rendering,
// Strokes
...uniforms.strokes,
},
vertexShader: `
// Attribute
attribute vec2 layoutUv;
attribute float lineIndex;
attribute float lineLettersTotal;
attribute float lineLetterIndex;
attribute float lineWordsTotal;
attribute float lineWordIndex;
attribute float wordIndex;
attribute float letterIndex;
// Varyings
varying vec2 vUv;
varying vec2 vLayoutUv;
varying vec3 vViewPosition;
varying vec3 vNormal;
varying float vLineIndex;
varying float vLineLettersTotal;
varying float vLineLetterIndex;
varying float vLineWordsTotal;
varying float vLineWordIndex;
varying float vWordIndex;
varying float vLetterIndex;
void main() {
// Output
vec4 mvPosition = vec4(position, 1.0);
mvPosition = modelViewMatrix * mvPosition;
gl_Position = projectionMatrix * mvPosition;
// Varyings
vUv = uv;
vLayoutUv = layoutUv;
vViewPosition = -mvPosition.xyz;
vNormal = normal;
vLineIndex = lineIndex;
vLineLettersTotal = lineLettersTotal;
vLineLetterIndex = lineLetterIndex;
vLineWordsTotal = lineWordsTotal;
vLineWordIndex = lineWordIndex;
vWordIndex = wordIndex;
vLetterIndex = letterIndex;
}
`,
fragmentShader: `
// Varyings
varying vec2 vUv;
// Uniforms: Common
uniform float uOpacity;
uniform float uThreshold;
uniform float uAlphaTest;
uniform vec3 uColor;
uniform sampler2D uMap;
// Uniforms: Strokes
uniform vec3 uStrokeColor;
uniform float uStrokeOutsetWidth;
uniform float uStrokeInsetWidth;
// Utils: Median
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
void main() {
// Common
// Texture sample
vec3 s = texture2D(uMap, vUv).rgb;
// Signed distance
float sigDist = median(s.r, s.g, s.b) - 0.5;
float afwidth = 1.4142135623730951 / 2.0;
#ifdef IS_SMALL
float alpha = smoothstep(uThreshold - afwidth, uThreshold + afwidth, sigDist);
#else
float alpha = clamp(sigDist / fwidth(sigDist) + 0.5, 0.0, 1.0);
#endif
// Strokes
// Outset
float sigDistOutset = sigDist + uStrokeOutsetWidth * 0.5;
// Inset
float sigDistInset = sigDist - uStrokeInsetWidth * 0.5;
#ifdef IS_SMALL
float outset = smoothstep(uThreshold - afwidth, uThreshold + afwidth, sigDistOutset);
float inset = 1.0 - smoothstep(uThreshold - afwidth, uThreshold + afwidth, sigDistInset);
#else
float outset = clamp(sigDistOutset / fwidth(sigDistOutset) + 0.5, 0.0, 1.0);
float inset = 1.0 - clamp(sigDistInset / fwidth(sigDistInset) + 0.5, 0.0, 1.0);
#endif
// Border
float border = outset * inset;
// Alpha Test
if (alpha < uAlphaTest) discard;
// Output: Common
vec4 filledFragColor = vec4(uColor, uOpacity * alpha);
// Output: Strokes
vec4 strokedFragColor = vec4(uStrokeColor, uOpacity * border);
gl_FragColor = filledFragColor;
}
`,
});
material.uniforms.uMap.value = atlas;
It extends from Three.js WebGPU NodeMaterial
import { MSDFTextNodeMaterial } from 'three-msdf-text-utils/webgpu';
const material = new MSDFTextNodeMaterial({ map: atlas, color: '#ff0000' });
// Here no uniforms object, you can access materials properties directly.
materials.strokeOutsetWidth = 0.2;
materials.strokeInsetWidth = 0.2;
materials.strokeColor = '#00ff00';
color: fill colorstrokeColor: stroke colorstrokeOutsetWidth: stroke size outside the glyphstrokeInsetWidth: stroke size inside the glyphisSmooth: Switch render mode from sharp to smooth, useful for tiny fonts –– use only 0 or 1threshold: smooth threshold (only used for isSmooth === 1)Generates MSDF font assets at runtime from a TTF font file. Forked from @zappar/msdf-generator
const { font, atlas } = await generateMSDF(fontPath, options);
| Parameter | Type | Required | Description |
|---|---|---|---|
fontPath |
string |
Yes | URL or path to a TTF font file |
options |
object |
Yes | Configuration options (see below) |
| Option | Type | Default | Description |
|---|---|---|---|
workerUrl |
string |
required | Path to worker.bundled.js |
wasmUrl |
string |
required | Path to msdfgen.wasm |
charset |
string |
A-Za-z0-9 + space |
Characters to include in the atlas |
fontSize |
number |
48 |
Font size in pixels |
textureSize |
[number, number] |
[512, 512] |
Atlas texture dimensions |
fieldRange |
number |
4 |
Distance field range in pixels |
fixOverlaps |
boolean |
true |
Fix overlapping glyph paths |
onProgress |
function |
- | Callback with progress percentage (progress: number) => void |
A Promise that resolves to an object containing:
| Property | Type | Description |
|---|---|---|
font |
THREE.Font |
Three.js Font instance (use font.data for geometry) |
atlas |
THREE.Texture |
Three.js Texture ready to use with materials |
generateMSDF('./font.ttf', {
workerUrl: '/msdfgen/worker.bundled.js',
wasmUrl: '/msdfgen/msdfgen.wasm',
charset: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,!?',
fontSize: 64,
textureSize: [1024, 1024],
onProgress: (progress) => console.log(`Generating: ${progress}%`),
}).then(({ font, atlas }) => {
// font.data contains the glyph metrics for MSDFTextGeometry
// atlas is a THREE.Texture for MSDFTextMaterial
});
Text not visible?
material.side = THREE.DoubleSidemesh.scale.set(0.01, 0.01, 0.01))near/far values if the mesh is being culledgenerateMSDF not working?
worker.bundled.js is the bundled version (not worker.js)wasmUrl points to the .wasm file, not .jsRandom m char in your webgl text?
Make sure to include a space char in the charset when generating the bitmap font and atlas.
npm install npm run dev
more like this
🎉🌟✨🎈年会抽奖程序,基于 Express + Three.js的 3D 球体抽奖程序,奖品🧧🎁,文字,图片,抽奖规则均可配置,😜抽奖人员信息Excel一键导入😍,抽奖结果Excel导出😎,给你的抽奖活动带来全新酷炫体验🚀🚀🚀
Convert Chinese Characters to Single-Line Fonts using Computer Vision
search projects, people, and tags