Misaligned text display

Hello, in my code, I’m trying to render text (with positions defined in an array). I can see the text correctly, and everything works except that the letters are not straight. The letters shift more and more with each one. How can I fix this so that the text is perfectly aligned ?

const loader = new FontLoader();
                    loader.load("https://threejs.org/examples/fonts/helvetiker_regular.typeface.json", (font) => {
                        textMeshesRef.current = [];
                        hoverPlaneRefs.current = [];

                        allPosition.forEach(({ title, x, y, z }, index) => {
                            const textGroup = new THREE.Group();
                            const avgZ = z + (title.length * 0.05) / 2;

                            textMeshesRef.current[index] = [];
                            hoverPlaneRefs.current[index] = [];

                            let currentOffset = 0; // Gère l'espacement total

                            [...title].forEach((char, i) => {
                                const geometry = new TextGeometry(char, {
                                    font,
                                    size: 0.05,
                                    height: 0.005
                                });
                                geometry.computeBoundingBox(); // Calculer la boîte englobante
                                const charWidth = geometry.boundingBox.max.x - geometry.boundingBox.min.x; // Largeur réelle
                                const charHeight = geometry.boundingBox.max.y - geometry.boundingBox.min.y; // Hauteur réelle

                                const material = new THREE.MeshBasicMaterial({ color: 0xffffff });
                                const mesh = new THREE.Mesh(geometry, material);

                                // Ajuster la position y pour aligner les lettres
                                const yOffset = -charHeight / 2;

                                mesh.position.set(x, y + yOffset, z + currentOffset);
                                mesh.userData.originalPosition = mesh.position.clone();
                                mesh.lookAt(new THREE.Vector3(x, y + 0.6, avgZ));
                                mesh.rotation.z = -1.5;

                                textMeshesRef.current[index].push(mesh);
                                textGroup.add(mesh);

                                currentOffset += charWidth + 0.025; // Ajout d'un petit espacement constant entre les lettres
                            });

                            sceneRef?.current?.add(textGroup);

                            const planeGeometry = new THREE.PlaneGeometry(0.5, 0.1);
                            const planeMaterial = new THREE.MeshBasicMaterial({
                                color: 0xff0000,
                                transparent: true,
                                opacity: 0
                            });

                            const hoverPlane = new THREE.Mesh(planeGeometry, planeMaterial);
                            hoverPlane.position.set(x + 0.025, y, z + 0.2);
                            hoverPlane.lookAt(new THREE.Vector3(x + 0.025, y + 0.6, z + 0.2));
                            hoverPlane.rotation.z = -1.5;
                            sceneRef?.current?.add(hoverPlane);
                            hoverPlaneRefs.current[index].push(hoverPlane);
                        });

                        window.addEventListener("mousemove", onMouseMove);
                        window.addEventListener("click", onMouseClick);
                    });

The issue is likely due to the fact that each letter’s geometry isn’t centered before applying rotations. When you rotate a mesh, it rotates around its local origin. If the geometry isn’t centered, the pivot point is off, causing each letter to shift as you add them.

To fix this, center each letter’s geometry before creating the mesh. You can do this by calling geometry.center() after computing the bounding box. For example:

js

복사

const geometry = new TextGeometry(char, {
  font,
  size: 0.05,
  height: 0.005
});
geometry.computeBoundingBox();
geometry.center(); // Center the geometry so the pivot is in the middle

const material = new THREE.MeshBasicMaterial({ color: 0xffffff });
const mesh = new THREE.Mesh(geometry, material);

By centering the geometry, the rotation (via lookAt and manual rotation) will behave more predictably, and your letters will remain aligned. You might need to adjust your offset calculations to account for the change in pivot, but this should resolve the shifting issue.

I tried with this but the letters are still not straight (see screen).

const loader = new FontLoader();
                    loader.load("https://threejs.org/examples/fonts/helvetiker_regular.typeface.json", (font) => {
                        textMeshesRef.current = [];
                        hoverPlaneRefs.current = [];

                        allPosition.forEach(({ title, x, y, z }, index) => {
                            const textGroup = new THREE.Group();
                            const avgZ = z + (title.length * 0.05) / 2;

                            textMeshesRef.current[index] = [];
                            hoverPlaneRefs.current[index] = [];

                            let currentOffset = 0; // Gère l'espacement total

                            [...title].forEach((char, i) => {
                                const geometry = new TextGeometry(char, {
                                    font,
                                    size: 0.05,
                                    height: 0.005
                                });
                                geometry.computeBoundingBox();
                                geometry.center();

                                const material = new THREE.MeshBasicMaterial({ color: 0xffffff });
                                const mesh = new THREE.Mesh(geometry, material);

                                mesh.position.set(x, y, z + currentOffset);
                                mesh.userData.originalPosition = mesh.position.clone();
                                mesh.lookAt(new THREE.Vector3(x, y + 0.6, avgZ));
                                mesh.rotation.z = -1.5;

                                textMeshesRef.current[index].push(mesh);
                                textGroup.add(mesh);

                                currentOffset += geometry.boundingBox.max.x + 0.025; // Ajout d'un petit espacement constant entre les lettres
                            });

                            sceneRef?.current?.add(textGroup);

                            const planeGeometry = new THREE.PlaneGeometry(0.5, 0.1);
                            const planeMaterial = new THREE.MeshBasicMaterial({
                                color: 0xff0000,
                                transparent: true,
                                opacity: 0
                            });

                            const hoverPlane = new THREE.Mesh(planeGeometry, planeMaterial);
                            hoverPlane.position.set(x + 0.025, y, z + 0.2);
                            hoverPlane.lookAt(new THREE.Vector3(x + 0.025, y + 0.6, z + 0.2));
                            hoverPlane.rotation.z = -1.5;
                            sceneRef?.current?.add(hoverPlane);
                            hoverPlaneRefs.current[index].push(hoverPlane);
                        });

                        window.addEventListener("mousemove", onMouseMove);
                        window.addEventListener("click", onMouseClick);
                    });

Just out of curiousity, why do you create a word letter by letter, when you can create the word whole at once with aligned letters? :thinking:

Because when hovering I do an animation that requires me to manage the letters one by one.

Ah, okay. Then, the worst thing you can do, following AI-instructions, is to apply .center to letter geometries.

1 Like

Very concise.

The reason why it’s a bad idea is called glyph metrics.

How can I fix this problem in this case ?

Have a look at this example. You can type your own text one-by-one characters and it shows properly aligned.

Internally, it creates a solid string, and the result is a single geometry, that contains all the letters in the string, whereas the author wants to operate with individual letters, if I got it correctly :thinking:

Made it just for fun before lunch :sweat_smile:

Picture:

Demo: https://codepen.io/prisoner849/full/azbzwxW

PS This is not the ultimate solution, but an example.

3 Likes

Very cool! :clap: