Overlapping connection point when extruding a ring

In my first project with ThreeJS I try to create a ring. I first create a cross section, which I then extrude with the following formula.

const radius = 25;

const path = new THREE.CatmullRomCurve3();
path.getPoint = (t) => {
  const radians = 2 * Math.PI * t;
  return new THREE.Vector3( radius * Math.cos( radians ),
    radius * Math.sin( radians ),
    0 );

const shape = new THREE.Shape();
shape.ellipse(0, 0, 5, 3, 0, 0, true, 0);

const extrudeSettings = {
  steps: 100,
  bevelEnabled: false,
  extrudePath: path,
  curveSegments: 10

const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);

Is there a way to prevent this horrible looking connection?

I’ve converted your code to a live example but I’m unable to see the glitch: https://jsfiddle.net/e195zkgs/

It does not make sense create an instance of CatmullRomCurve3 if you are going to overwrite getPoint(). Use the Curve class instead.

The glitch is only visible, when I do this (live example):

const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
for (const face of geometry.faces) {
  if (face.materialIndex === 1) {
    for (const vertex of face.vertexNormals) {
      vertex.z = 0;
const buffer = new THREE.BufferGeometry().fromGeometry(geometry);

That happens because of this line. When computing normals like that, you produce a seam where the (unconnected) start and end vertices meet.

In general, recomputing normals via computeVertexNormals() is not supported by all geometry generators.

1 Like

First of all, many thanks for the explanation. As I am still quite new to the subject, I do not know how to proceed at the moment. How would you connect the ends? Or would a different geometry be more useful?

Could you produce your geometry in a DCC tool like Blender instead and then import it via GLTFLoader?

In the future, I would like to visualize parameter adjustments directly on the object in the browser, which is why constructing the object in a DCC tool would probably be to inflexible. Or is that wrong?

If you need to generate geometries on-the-fly then importing 3D assets is indeed the wrong approach.

You might find some suggestions in my addons. There shapes are also dynamically changed. I used self defined Geometry, indexed BufferGeometry and non-indexed BufferGeometry.


Try it out :slightly_smiling_face:

Do you have another idea how I could solve the problem?

I have to correct my previous post. The problem is that you convert to Geometry and back to BufferGeometry. The usage of computeVertexNormals() is actually fine as long as you don’t do it with Geometry. Try to make your geometry modifications without using Geometry.

Ok, but if I only use the BufferGeometry in combination with computeVertexNormals(), I get this result (live example):

Not a glitch, but completely normal and expected. Imagine forming a cylinder from a piece of paper. Where the edges meet, a seam is formed.

The light distortion you see is caused by vertices occupying the same space, but having different normals.

Since the geometry is mono-grouped and has no hard edges, the solution is to make sure these vertices also share the same normal values.

So should I push the first values from the extrusion into the array again?


Solution: ewice.txt (3.1 KB)

Thank you very much for your effort. The only problem I have now is this black border.

This is very interesting to me…
Is there any reason you’re not using LatheGeometry for this?

(I’m learning from this code, so may be a naive question)

The border is caused by modifying the vertex normals.
To counter it, any of the following might work.

  • Add emissive att to Pong Mat.
  • Revert back to your own light set up [upper left img]
  • Soften the effect by assigning vertex.z to Math.sign(vertex.z)*0.005; [upper right img]
  • Do not modify vertex normals [ lower middle img