Classes d'utilité pour le rendu de texte en trois.js à l'aide de polices bitmap et de msdf (champs de distance signés multi-canaux).
Fourchu à partir de texte à trois-bmfont.
Il comprend:
Pour que cela fonctionne, vous aurez besoin de fichiers spécifiques, vous pouvez les générer avec MSDF-BMFONT-XML ou avec l'outil en ligne. Vous pouvez également consulter mon facteur MSDF-FONT, il comprend déjà certains fichiers que vous pouvez utiliser et un script pour générer vos fichiers facilement.
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 ) ;Les options peuvent être un objet ou une chaîne - équivalent à {text: str}.
flipY (booléen): si la texture sera flippée en Y (par défaut True)multipage (booléen): s'il faut construire cette géométrie avec un tampon supplémentaire contenant des ID de page. Ceci est nécessaire pour les polices multi-textures (par défaut false) font (requise) La définition BMFONT qui contient des caractères, des grains, etc.text (chaîne) Le texte à disposer. Les caractères de Newline ( n) provoqueront des pauses de lignewidth (nombre, facultatif) La largeur souhaitée de la zone de texte, provoque des mots et une coupure de mot en mode "pré". Laissez comme non défini pour supprimer l'emballage de mots (comportement par défaut)mode (chaîne) Un mode pour Word-Wrapper; Peut être `` pré '(maintenir l'espacement), ou «Nowrap» (effondrement des espaces blancs mais uniquement sur les caractères de Newline), sinon et assume le comportement normal de la parole (effondrement de l'espace, une rupture à la largeur ou des nouvelles)align (String) peut être "à gauche", "centre" ou "droite" (par défaut: gauche)letterSpacing (numéro) l'espacement des lettres en pixels (par défaut: 0)lineHeight (numéro) la hauteur de la ligne en pixels (par défaut à Font.Common.lineHeight)tabSize (Numéro) Le nombre d'espaces à utiliser dans un seul onglet (par défaut 4)start (numéro) l'index de démarrage dans le texte à disposer (par défaut 0)end (numéro) l'index de fin (exclusif) dans le texte à disposer (Text.length par défaut) update(options)Représente la géométrie à l'aide des options données. Toutes les options non spécifiées ici seront par défaut à celles définies dans le constructeur. Cette méthode recomputera la disposition du texte et reconstruia les tampons WebGL. Les options peuvent être un objet ou une chaîne - équivalent à {text: str}.
layoutInstance de mise en page de texte, vous pouvez l'utiliser pour accéder aux attributs de mise en page tels que:
Largeur, hauteur, descendance, ascension, Xheight, ligne de base, capheight, ligne de ligne, linestotal, lettre de lettre
visibleGlyphs Un ensemble filtré de geometry.layout.glyphs destiné à s'aligner avec les données de sommet utilisées par les tampons sous-jacents.
Il s'agit d'un tableau d'objets { line, position, index, data } , voir ici. Par exemple, cela pourrait être utilisé pour ajouter un nouveau bufferAttribute pour le décalage line .
Outre les attributs de géométrie de base. Il existe des attributs spécifiques au texte, principalement utiles à des fins d'animation.
positionuv : Coordonnées UV utilisées pour cartographier la bonne lettre dans la bonne lettre quadcenter : centre de chaque lettre quadlayoutUv : Coordonnées UV du bloc de texte intégral.glyphUv : coordonnées UV de chaque quad de lettre individuelle.glyphResolution : Résolution de chaque lettre individuelle Quad.lineIndex : index de chaque lignelineLettersTotal : quantité totale de lettres dans chaque lignelineLetterIndex : index de chaque lettre par lignelineWordsTotal : quantité totale de mots par lignelineWordIndex : index de chaque mot par lignewordIndex : Index de chaque motletterIndex : index de chaque lettreIl s'étend à partir de trois.js shaderaterial
Vous pouvez l'utiliser simplement en définissant la texture de l'atlas à partir de votre police:
const material = new MSDFTextMaterial ( options ) ;
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 ,
} ; Remarque: IS_SMALL Boolean est utile pour rendre les petites polices, elle changera le calcul de rendu alpha pour les rendre visuellement beaucoup plus lisses
Si vous souhaitez faire des effets de texte spécifiques, vous pouvez créer votre propre code GLSL dans votre matériau de shader en fonction du shader MSDFTextMaterial.
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 ; npm installnpm run dev