Classes de utilidade para renderização de texto em três.js usando fontes de bitmap e msdf (campos de distância assinados por vários canais).
Bifurcado de três bmfont-text.
Inclui:
Para conseguir isso, você precisará de alguns arquivos específicos, você pode gerá-los com o MSDF-BMFONT-XML ou com a ferramenta online. Você também pode verificar meu fábrica MSDF-Fonts, já inclui alguns arquivos que você pode usar e um script para gerar seus arquivos facilmente.
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 ) ;As opções podem ser um objeto ou uma string - equivalente a {text: str}.
flipY (booleano): se a textura será lasca em Y (padrão true)multipage (booleana): se deve construir essa geometria com um buffer extra contendo IDs de página. Isso é necessário para fontes multi-textura (padrão false) font (necessária) A definição BMFont que detém chars, kernings, etctext (string) o texto a ser layout. Os caracteres da Newline ( n) causarão quebras de linhawidth (número, opcional) a largura desejada da caixa de texto, causa embrulho e recorte de palavras no modo "pré". Deixe como indefinido para remover o embrulho de palavras (comportamento padrão)mode (string) um modo para word-wrapper; pode ser 'pré' (manter espaçamento) ou 'Nowrap' (colapso em branco, mas apenas quebrar os caracteres da Newline), caso contrário, assume um comportamento normal de gravação de palavras (colapso Whitespace, quebra de largura ou newlines)align (string) pode ser "esquerda", "centro" ou "direita" (padrão: esquerda)letterSpacing (número) o espaçamento da carta em pixels (padrão: 0)lineHeight (número) a altura da linha em pixels (padrão para font.common.lineHeight)tabSize (número) o número de espaços a serem usados em uma única guia (padrão 4)start (número) o índice de partida no texto para layout (padrão 0)end (número) o índice de final (exclusivo) no texto a layout (text.leng.length) update(options)Re-compra a geometria usando as opções fornecidas. Quaisquer opções não especificadas aqui serão padrão para os definidos no construtor. Este método recomputará o layout de texto e reconstruirá os buffers WebGL. As opções podem ser um objeto ou uma string - equivalente a {text: str}.
layoutInstância de layout de texto, você pode usá -lo para acessar atributos de layout, como:
largura, altura, descendência, ascendente, xheight, linha de base, peso, alinhamento, linestotal, letterstotal
visibleGlyphs Um conjunto filtrado da geometry.layout.glyphs destinado a se alinhar com os dados do vértice que estão sendo usados pelos buffertributos subjacentes.
Esta é uma matriz de objetos { line, position, index, data } , veja aqui. Por exemplo, isso pode ser usado para adicionar um novo bufferattribute para deslocamento line .
Além dos atributos básicos da geometria. Existem alguns atributos específicos de texto, principalmente úteis para fins de animação.
positionuv : coordenadas UV usadas para mapear a letra certa na letra direita Quadcenter : centro de cada quádra de letralayoutUv : coordenadas UV do bloco de texto completo.glyphUv : Coordenadas UV de cada letra individual Quad.glyphResolution : Resolução de cada letra individual Quad.lineIndex : índice de cada linhalineLettersTotal : quantidade total de letras em cada linhaslineLetterIndex : índice de cada letra por linhalineWordsTotal : quantidade total de palavras por linhalineWordIndex : índice de cada palavra por linhawordIndex : índice de cada palavraletterIndex : índice de cada letraSe estende de três.js shadematerial
Você pode usá -lo apenas definindo a textura do Atlas da sua fonte:
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 é útil para renderizar pequenas fontes, ele mudará o cálculo da renderização alfa para torná -las visualmente muito mais suaves
Se você deseja fazer alguns efeitos específicos de texto, pode criar seu próprio código GLSL em seu material de shader com base no 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