How to get smooth shading on TextGeometry, please 🙏

I can confirm that recalculating normals will work in smoothing things out, if it’s about altering vertices - the terrain in my project looks much smoother when done via “manual” vertex altering and recomputing normals (as opposed to, say, a displacement map with a higher level of detail).

I guess the main question here is whether this is a case of adjusting the normals, or having more segments in the rounded parts of the shapes, since smoothing is an effect in the latter case too (something quite visible in the case of spheres).

Too bad Three.js’ atrocious level of backward compatibility with previous versions and ways of doing things prevents easy testing of a similar code, otherwise we’d already have an answer to that… :roll_eyes:

Sadly it didn’t worked, nothing of the previously advised solutions worked on the font.
I’m starting to think it’s just because of how the font is generated and can’t be bypassed other than creating the font properly in blender and exporting/loading.

let mesh_2=new THREE.Mesh(text.geometry.clone(),new THREE.MeshNormalMaterial());
mesh_2.geometry=THREE.BufferGeometryUtils.mergeVertices(mesh_2.geometry,1e-0) ;
mesh_2.geometry.computeVertexNormals();
mesh_2.position.set(1,0,0);
scene.add(mesh_2);
2 Likes

I just can’t believe that nothing worked, since it does for me (finally figured out how to use the font loader, I was mistakenly copying the wrong font loader file before and then wondered why the importing didn’t work), by increasing the number of curve segments as advised - here, I set them to 36 and also set the bevel segments to 20 to be consistent, and now the “d” shape is smooth like baby skin:


Sorry about the environment, that’s just my globe project in the background. Obviously, when increasing the number of segments of any shape, the CPU usage will rise accordingly - that’s just the price of having nice things in the scene.

Thanks for the answer, but still nope, didn’t work :

Ah yes this works obviously, but as I said in the first message “other than massively subdividing?”, I’m trying to keep things lightweight, so disliked to add more polygons.
I was looking for a solution like in blender “shade smooth”, that works on something even low poly

Thanks a lot for all your help in any case !! :pray:

Ahh, ok, read that but somehow missed it - my bad. Hmm… maybe some blurring can do the job then? Apart from iterating through and adjusting the position attribute followed by recomputing normals for the whole geometry, that is… :thinking:

P.S. Altering the position attribute for a sphere is done like this (don’t know for the text though, it probably depends on its specific segments):

  var posidx, posval = new THREE.Vector3(), dirval = new THREE.Vector3(), disval;
  for (var j = 0; j < geometry.parameters.heightSegments + 1; j++)
  {
    for (var i = 0; i < geometry.parameters.widthSegments + 1; i++)
    {
      // ideally you'd only alter the positions in curved segments here
      posidx = (geometry.parameters.widthSegments + 1) * j + i;
      posval.fromBufferAttribute(geometry.attributes.position, posidx);
      dirval.fromBufferAttribute(geometry.attributes.normal, posidx);
      // the distance by which you alter vertices will depend here
      disval = 0;
      posval.addScaledVector(dirval, disval);
      geometry.attributes.position.setXYZ(posidx, posval.x, posval.y, posval.z);
    };
  };
  geometry.computeVertexNormals();
  geometry.attributes.position.needsUpdate = true;

If you can figure out a way to adjust vertices only for positions belonging to curved segments and in a way that follows some trigonometric function, something similar to this will probably do it. I’m not sure if simply adjusting vertices with a hardcoded value to trigger smoothing via recomputing normals will work, but you can try.

1 Like

import * as BufferGeometryUtils from ‘./jsm/utils/BufferGeometryUtils.js’;

2 Likes

Thanks that fixed the previous error, but I still have that issue line 64 :

Don’t use THREE.BufferGeometryUtils in this case, just simply BufferGeometryUtils. That will solve the issue.

1 Like

Thanks a lot ! I did exactly like that, but I’m having this result instead of the nice text you’re having :

Thanks, yeah that worked, sadly as shown on the last picture, the vertice merging seems to have destroyed the geometry :thinking:
I don’t understand how @Chaser_Code managed to have his nice result above with the text smoothed

I had a similar result as you did, after using @Chaser_Code’s code on your text and settings. Change the text to 'three.js' and its settings to the ones in the example here, and it will work (you’ll have to zoom out a bit though):

As for the value of 1e-0 or the changes needed to make your (or any, for that matter) text work, you’ll have to ask @Chaser_Code for details.

1 Like

Thanks a lot for everything !
Yeah I think it’s because the params are too “small” it’s messing up the geo. :thinking:

Here is the final result btw : Under Construction | Evry_Paris-Saclay - iGEM 2022
(Original idea from Ilithya)

Its depend of size geometry. 1=Meters or 1=kilometers.

I guess the easiest solution would be to just keep the same settings as in the example from the Three.js manual I linked above, and then simply set the object’s scale appropriately, e.g. (disregard my custom font JSON path):

  const normalMaterial = new THREE.MeshNormalMaterial();
  const loader = new FontLoader();
  loader.load("http://localhost:8080/helvetiker_regular.typeface.json", function(font)
  {
    const textGeometry = new TextGeometry('Under Construction !' + '\nEstimated delivery :' + '\nSoon™',
    {
      font: font,
      size: 80,
      height: 5,
      curveSegments: 12,
      bevelEnabled: true,
      bevelThickness: 10,
      bevelSize: 8,
      bevelOffset: 0,
      bevelSegments: 5
    });
    const textMesh = new THREE.Mesh(textGeometry, normalMaterial);
    textGeometry.center();
    textGeometry.computeBoundingBox();
    scene.add(textMesh);
    const stextMesh = new THREE.Mesh(textMesh.geometry.clone(), new THREE.MeshNormalMaterial());
    stextMesh.geometry = BufferGeometryUtils.mergeVertices(stextMesh.geometry, 1);
    stextMesh.geometry.computeVertexNormals();
    stextMesh.position.set(10, 10, 10);
    stextMesh.scale.setScalar(1 / 160);
    scene.add(stextMesh);
  }); 

That way, there’s no need to guess what value is needed for the tolerance parameter (I tried in vain to figure that out, but it’s pointless in this case). I set the scale to 1 / 160 because of the ratio between font sizes, i.e. 80 / 0.5 = 160, but you can adjust it to suit your needs, considering that fonts don’t size up on a per pixel basis. Same for positioning.

P.S. The stextMesh name comes from “smooth text mesh”, in case anyone is curious. :laughing:

1 Like

Thanks so much to you and everyone for all your help, you guys rock !! :heart_eyes:

thank youuuuu

1 Like

For the record, if one looks closely, there are still some places where vertices are not properly joined / merged. If that’s the case, just lower the tolerance parameter in the mergeVertices() function from 1 to, say, 0.5 or similar, to “close the gaps” in the letters’ geometries.

While a great idea and having a nice effect - credit to @Chaser_Code for it - I would personally still choose “subdividing” geometry by increasing the number of segments. It’s heavier on the CPU but it’s “built in” and doesn’t need further adjustments.