Утилита классов для рендеринга текста в трех.js с использованием растровых шрифтов и MSDF (многоканальные поля дистанции подписания).
Разветвляется из трех-бмфонта-текста.
Он включает в себя:
Чтобы получить эту работу, вам понадобятся некоторые конкретные файлы, вы можете сгенерировать их с помощью MSDF-BMFONT-XML или с помощью онлайн-инструмента. Вы также можете проверить мою MSDF-Font-Factory. Это уже включает в себя некоторые файлы, которые вы можете использовать, и сценарий для легкости создания ваших файлов.
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 ) ;Параметры могут быть объектом или строкой - эквивалентной {text: str}.
flipY (Boolean): будет ли текстура y-flyfled (по умолчанию True)multipage (логический): строить ли эту геометрию с помощью дополнительного буфера, содержащего идентификаторы страниц. Это необходимо для мультитектурных шрифтов (по умолчанию false) font (требуется) определение BMFONT, которое содержит Chars, Kernings и т. Д.text (строка) текст в макет. Newline символы ( n) вызовут разрывы линииwidth (число, необязательное) Желаемая ширина текстового поля вызывает обрывание слов и обрезку в режиме «pre». Оставьте столь же неопределенным, чтобы удалить обрывание слов (поведение по умолчанию)mode (строка) режим для Word-Wrapper; может быть «pre» (поддерживать интервал) или «nowrap» (пробелы коллапса, но только сломать на новых символах), в противном случае предполагает нормальное поведение Word-wrap (пробел с коллапсом, разрыв по ширине или новыми линиями)align (строка) может быть «слева», «центр» или «правый» (по умолчанию: слева)letterSpacing (номер) расстояние между буквами в пикселях (по умолчанию: 0)lineHeight (номер) высота строки в пикселях (по умолчанию в font.common.lineHeight)tabSize (номер) количество пробелов для использования на одной вкладке (по умолчанию 4)start (номер) начальный индекс в текст на макет (по умолчанию 0)end (номер) Индекс окончания (эксклюзивный) в текст в макет (по умолчанию text.length) update(options)Пересмотрите геометрию, используя заданные варианты. Любые параметры, не указанные здесь, будут по умолчанию, установленные в конструкторе. Этот метод будет пересекать текстовый макет и восстановить буферы WebGL. Параметры могут быть объектом или строкой - эквивалентной {text: str}.
layoutЭкземпляр для макета текста, вы можете использовать его для доступа к атрибутам макета, например:
Ширина, высота, потомка, восхождение, xheight, базовый, капхайт, Lineheight, Linestotal, Letterstotal
visibleGlyphs Отфильтрованный набор из geometry.layout.glyphs предназначенного для совместимости с данными вершины, используемыми базовыми буферетрибутами.
Это массив объектов { line, position, index, data } , см. Здесь. Например, это может быть использовано для добавления нового буфера для смещения line .
Помимо основных атрибутов геометрии. Есть некоторые текстовые атрибуты, в основном полезные для анимационных целей.
positionuv : УФ -координаты, используемые для сопоставления правой буквы в правой буквы Quadcenter : Центр каждой буквы QuadlayoutUv : УФ -координаты полного текстового блока.glyphUv : УФ -координаты каждой отдельной буквы Quad.glyphResolution : разрешение каждой отдельной буквы Quad.lineIndex : индекс каждой строкиlineLettersTotal : общее количество букв в каждой строкеlineLetterIndex : индекс каждой буквы по строкеlineWordsTotal : общее количество слов по строкеlineWordIndex : индекс каждого слова по строкеwordIndex : индекс каждого словаletterIndex : индекс каждой буквыОн простирается от Three.js Shadermaterial
Вы можете использовать его, просто установив текстуру Atlas из своего шрифта:
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 ,
} ; Примечание: IS_SMALL BOOLEAN полезен для визуализации небольших шрифтов, он переключит расчет альфа -рендеринга, чтобы сделать их визуально более плавным
Если вы хотите сделать некоторые конкретные текстовые эффекты, вы можете создать свой собственный код GLSL в вашем материале шейдера на основе шейдера 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