Skip to content

Commit b39af4e

Browse files
committed
✨ Add MSDFTextNodeMaterial for WebGPU support
1 parent 53b80ce commit b39af4e

File tree

13 files changed

+38911
-3545
lines changed

13 files changed

+38911
-3545
lines changed

README.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Forked from [three-bmfont-text](https://github.com/Jam3/three-bmfont-text).
1111
- Replace some deprecated three.js code
1212
- Add more geometry attributes : layout uv, letter center positions, letter index, line index, letter index by line, word index, word index by line...
1313
- Compatibility check with [Three.js FontLoader](https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/FontLoader.js)
14+
- WebGPU MSDFTextNodeMaterial
1415
- More to come...
1516

1617
## Demo
@@ -19,7 +20,7 @@ Forked from [three-bmfont-text](https://github.com/Jam3/three-bmfont-text).
1920
- [Stroke](https://leochocolat.github.io/three-msdf-text-utils/demo/?demo=stroke)
2021
- [Editor](https://leochocolat.github.io/three-msdf-text-utils/demo/?demo=editor)
2122
- [Reveal](https://leochocolat.github.io/three-msdf-text-utils/demo/?demo=reveal)
22-
- More to come...
23+
- [WebGPU](https://leochocolat.github.io/three-msdf-text-utils/demo/?demo=webgpu)
2324

2425
## Bitmap Font and Font Atlas
2526

@@ -152,6 +153,28 @@ const material = new MSDFTextMaterial(options);
152153
material.uniforms.uMap.value = atlas;
153154
```
154155

156+
157+
### MSDFTextNodeMaterial (WebGPU)
158+
159+
It extends from Three.js WebGPU NodeMaterial
160+
161+
```js
162+
const material = new MSDFTextNodeMaterial({ map: atlas, color: '#ff0000' });
163+
164+
// Here no uniforms object, you can access materials properties directly.
165+
materials.strokeOutsetWidth = 0.2;
166+
materials.strokeInsetWidth = 0.2;
167+
materials.strokeColor = '#00ff00';
168+
```
169+
#### Properties
170+
171+
- `color`: fill color
172+
- `strokeColor`: stroke color
173+
- `strokeOutsetWidth`: stroke size outside the glyph
174+
- `strokeInsetWidth`: stroke size inside the glyph
175+
- `isSmooth`: Switch render mode from sharp to smooth, useful for tiny fonts –– use only 0 or 1
176+
- `threshold`: smooth threshold (only used for isSmooth === 1)
177+
155178
#### Initial Properties
156179

157180
```js
@@ -363,10 +386,3 @@ npm install
363386
```bash
364387
npm run dev
365388
```
366-
367-
## Roadmap
368-
369-
- More examples
370-
- More docs for custom shader material
371-
- Manage versions
372-
- NPM Publish workflow

demo/build/bundle-demo.js

Lines changed: 38343 additions & 3045 deletions
Large diffs are not rendered by default.

demo/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ import scenes from './scenes';
22

33
const urlParams = new URLSearchParams(location.search);
44
const demoName = urlParams.get('demo');
5-
const scene = scenes[demoName] ? new scenes[demoName]() : new scenes.basic();
5+
const scene = scenes[demoName.toLowerCase()] ? new scenes[demoName.toLowerCase()]() : new scenes.basic();
66
scene.start();

demo/scenes/Basic/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export default class Basic {
6666
mesh.rotation.x = Math.PI;
6767
const scale = 3;
6868
mesh.position.x = -geometry.layout.width / 2 * scale;
69+
mesh.position.y = -geometry.layout.height / 2 * scale;
6970
mesh.scale.set(scale, scale, scale);
7071
this.scene.add(mesh);
7172

demo/scenes/WebGPU/config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default {
2+
name: 'WebGPU',
3+
4+
text: 'WebGPU',
5+
6+
settings: {
7+
color: '#ffffff',
8+
},
9+
};

demo/scenes/WebGPU/index.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// THREE WEBGPU
2+
import { WebGPURenderer } from 'three/webgpu';
3+
4+
// THREE
5+
import { Scene, PerspectiveCamera, TextureLoader, Mesh, DoubleSide, Color } from 'three';
6+
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
7+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
8+
9+
// Vendor
10+
import { Pane } from 'tweakpane';
11+
12+
// Lib
13+
import { MSDFTextGeometry, MSDFTextNodeMaterial } from '../../../src/index';
14+
15+
// Config
16+
import config from './config';
17+
18+
export default class WebGPU {
19+
constructor() {
20+
this.canvas = document.querySelector('.js-canvas');
21+
this.renderer = null;
22+
this.scene = null;
23+
this.camera = null;
24+
this.controls = null;
25+
this.debugger = new Pane({ title: `${config.name} Example` });
26+
}
27+
28+
start() {
29+
this.setupEventListeners();
30+
this.setup();
31+
this.setupText();
32+
this.update();
33+
}
34+
35+
setup() {
36+
this.camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
37+
this.camera.position.z = 1000;
38+
39+
this.scene = new Scene();
40+
this.scene.background = new Color('black');
41+
42+
this.renderer = new WebGPURenderer({ antialias: true, canvas: this.canvas });
43+
this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio));
44+
this.renderer.setSize(window.innerWidth, window.innerHeight);
45+
46+
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
47+
this.controls.target.set(0, 0, 0);
48+
this.controls.update();
49+
}
50+
51+
setupText() {
52+
const promises = [
53+
this.loadFontAtlas('./fonts/roboto/roboto-regular.png'),
54+
this.loadFont('./fonts/roboto/roboto-regular.fnt'),
55+
];
56+
57+
Promise.all(promises).then(([atlas, font]) => {
58+
const geometry = new MSDFTextGeometry({
59+
text: config.text,
60+
font: font.data,
61+
width: 1000,
62+
align: 'center',
63+
});
64+
65+
const material = new MSDFTextNodeMaterial({ map: atlas });
66+
material.side = DoubleSide;
67+
68+
const mesh = new Mesh(geometry, material);
69+
mesh.rotation.x = Math.PI;
70+
const scale = 3;
71+
mesh.position.x = -geometry.layout.width / 2 * scale;
72+
mesh.scale.set(scale, scale, scale);
73+
this.scene.add(mesh);
74+
75+
// Debug
76+
const debugFolderCommon = this.debugger.addFolder({ title: 'Common' });
77+
debugFolderCommon.addInput(material.opacity, 'value', { label: 'Opacity', min: 0, max: 1 });
78+
debugFolderCommon.addInput(config.settings, 'color', { label: 'Color' }).on('change', () => { material.color.value.set(config.settings.color); });
79+
80+
const debugFolderRendering = this.debugger.addFolder({ title: 'Rendering' });
81+
debugFolderRendering.addInput(material.isSmooth, 'value', { label: 'Is Smooth', options: [{ text: 'False', value: 0 }, { text: 'True', value: 1 }] });
82+
debugFolderRendering.addInput(material, 'alphaTest', { label: 'Alpha test', min: 0, max: 1 });
83+
debugFolderRendering.addInput(material.threshold, 'value', { label: 'Threshold (isSmooth)', min: 0, max: 1 });
84+
});
85+
}
86+
87+
loadFontAtlas(path) {
88+
const promise = new Promise((resolve, reject) => {
89+
const loader = new TextureLoader();
90+
loader.load(path, resolve);
91+
});
92+
93+
return promise;
94+
}
95+
96+
loadFont(path) {
97+
const promise = new Promise((resolve, reject) => {
98+
const loader = new FontLoader();
99+
loader.load(path, resolve);
100+
});
101+
102+
return promise;
103+
}
104+
105+
render() {
106+
this.renderer.renderAsync(this.scene, this.camera);
107+
}
108+
109+
update() {
110+
this.render();
111+
112+
requestAnimationFrame(this.update.bind(this));
113+
}
114+
115+
setupEventListeners() {
116+
window.addEventListener('resize', this.resizeHandler.bind(this));
117+
}
118+
119+
resizeHandler() {
120+
this.camera.aspect = window.innerWidth / window.innerHeight;
121+
this.camera.updateProjectionMatrix();
122+
this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio));
123+
this.renderer.setSize(window.innerWidth, window.innerHeight);
124+
}
125+
}

demo/scenes/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import Basic from './Basic';
22
import Editor from './Editor';
33
import Stroke from './Stroke';
44
import Reveal from './Reveal';
5+
import WebGPU from './WebGPU';
56

67
export default {
78
basic: Basic,
89
stroke: Stroke,
910
editor: Editor,
1011
reveal: Reveal,
12+
webgpu: WebGPU,
1113
};

0 commit comments

Comments
 (0)