Clases de utilidad para representación de texto en tres.js utilizando fuentes de mapa de bits y MSDF (campos de distancia firmados multicanal).
Bifurcado de tres bmfont-text.
Incluye:
Para que esto funcione, necesitará algunos archivos específicos, puede generarlos con msdf-bmfont-xml o con la herramienta en línea. También puede verificar mi factor msdf-font, ya incluye algunos archivos que puede usar y un script para generar sus archivos fácilmente.
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 ) ;Las opciones pueden ser un objeto, o una cadena, equivalente a {Text: Str}.
flipY (booleano): si la textura se flota y (verdadero verdadero)multipage (booleano): si construir esta geometría con un búfer adicional que contiene ID de página. Esto es necesario para las fuentes de textura múltiple (falso predeterminado) font (requerido) La definición BMFONT que contiene caracteres, kernings, etc.text (cadena) El texto al diseño. Los personajes de Newline ( n) causarán descansos de líneawidth (número, opcional) El ancho deseado del cuadro de texto, causa envoltura de palabras y recorte en modo "pre". Dejar como indefinido para eliminar la envoltura de palabras (comportamiento predeterminado)mode (cadena) un modo para word-wrapper; puede ser 'pre' (mantener espaciado) o 'Nowrap' (colapsar en blanco pero solo romper los personajes de Newline), de lo contrario asume un comportamiento normal de Word-Wrap (colapso en blanco, ruptura en el ancho o las líneas nuevas)align (cadena) puede ser "izquierda", "centro" o "derecha" (predeterminado: izquierda)letterSpacing (número) El espacio de letras en píxeles (predeterminado: 0)lineHeight (número) La altura de la línea en píxeles (predeterminado a font.common.lineheight)tabSize (número) El número de espacios a usar en una sola pestaña (predeterminado 4)start (número) El índice inicial en el texto al diseño (predeterminado 0)end (número) El índice final (exclusivo) en el texto al diseño (texto predeterminado.length) update(options)Reconstruye la geometría utilizando las opciones dadas. Cualquier opción que no se especifique aquí predeterminará las establecidas en el constructor. Este método recomputará el diseño del texto y reconstruirá los buffers de WebGL. Las opciones pueden ser un objeto, o una cadena, equivalente a {Text: Str}.
layoutInstancia de diseño de texto, puede usarlo para acceder a los atributos de diseño como:
Ancho, altura, descendiente, ascender, xheight, basal, capheight, lineheight, linestotal, letterstotal
visibleGlyphs Un conjunto filtrado de geometry.layout.glyphs destinado a alinearse con los datos de vértice utilizados por los bufferattributes subyacentes.
Esta es una matriz de objetos { line, position, index, data } , ver aquí. Por ejemplo, esto podría usarse para agregar un nuevo bufferattribute para el desplazamiento line .
Además de los atributos de geometría básica. Hay algunos atributos específicos de texto, en su mayoría útiles para fines de animación.
positionuv : Coordenadas UV utilizadas para mapear la letra correcta en la letra derecha Quadcenter : Centro de cada letra QuadlayoutUv : coordenadas UV del bloque de texto completo.glyphUv : coordenadas UV de cada quad de letra individual.glyphResolution : resolución de cada quad de letra individual.lineIndex : índice de cada línealineLettersTotal : Cantidad total de letras en cada línealineLetterIndex : índice de cada letra por línealineWordsTotal : Cantidad total de palabras por línealineWordIndex : índice de cada palabra por líneawordIndex : índice de cada palabraletterIndex : índice de cada letraSe extiende desde tres.js shadermaterial
Puede usarlo simplemente configurando la textura de Atlas desde su fuente:
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 ,
} ; Nota: IS_SMALL Boolean es útil para hacer pequeñas fuentes, cambiará el cálculo de representación alfa para que sean visualmente mucho más suaves
Si desea hacer algunos efectos de texto específicos, puede crear su propio código GLSL en el material de su sombreador en función del sombreador 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