CatmullRomCurve3 is not rendering as expected

I’m having a little issue here. I’m rendering a THREE.CatmullRomCurve3 but it is not rendering as per the points given. Attaching the code snippet and the snip of the output here.

Code snippet:

const camera = new THREE.PerspectiveCamera(95, window.innerWidth / window.innerHeight, 0.5, 2000);
camera.position.copy( new THREE.Vector3(1070,-900,0));
camera.lookAt(1078.3760583512485, -896.7445069923997, -25.48);
const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor("#233143");
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const points= [];
points.push(new THREE.Vector3(1078.3760583512485, -896.7445069923997, -25.48));
points.push(new THREE.Vector3(1078.3294048905373, -896.5172636993229, -25.49));
points.push(new THREE.Vector3(1078.5533414967358, -895.2421761974692, -25.48));
points.push(new THREE.Vector3(1078.8612543307245, -894.7750648967922, -25.71));
points.push(new THREE.Vector3(1079.1878285482526, -894.7119417488575, -25.66));
points.push(new THREE.Vector3(1079.5797176100314, -894.6866924837232, -25.65));
points.push(new THREE.Vector3(1081.27790354006, -894.8381880447268, -25.66));
points.push(new THREE.Vector3(1088.4998590946198, -895.4946687817574, -25.68));
points.push(new THREE.Vector3(1100.3125150762498, -896.832879383117, -25.63));
points.push(new THREE.Vector3(1114.1126084402204, -898.2847114242613, -25.65));
points.push(new THREE.Vector3(1123.1353875361383, -899.1810597889125, -25.66));
points.push(new THREE.Vector3(1133.763045642525, -900.0900326892734, -25.68));
points.push(new THREE.Vector3(1141.0316546559334, -900.7465130649507, -25.68));
points.push(new THREE.Vector3(1141.0316546559334, -900.7465130649507, -24.75));
points.push(new THREE.Vector3(1141.1063001919538, -900.7591376900673, -24.74));
points.push(new THREE.Vector3(1147.6191231589764, -901.2136240750551, -24.74));
points.push(new THREE.Vector3(1150.7169128786772, -901.4282426387072, -24.73));
points.push(new THREE.Vector3(1158.1068208869547, -901.9206028580666, -24.74));
points.push(new THREE.Vector3(1159.9076444301754, -902.0468490608037, -24.74));
points.push(new THREE.Vector3(1169.6115640364587, -902.6275815851986, -24.72));
points.push(new THREE.Vector3(1172.2521498519927, -902.7790770195425, -24.73));
points.push(new THREE.Vector3(1183.6169326212257, -903.359809499234, -24.73));
points.push(new THREE.Vector3(1189.868496214971, -903.6880495809019, -24.72));

// Rendering the curve
const randomSpline = new THREE.CatmullRomCurve3(points);
const extrudeSettings = {
    steps: 30,
    bevelEnabled: false,
    extrudePath: randomSpline
};
const wallShape = new THREE.Shape();
const wallHeight = 1 / 2;
const wallWidth = 0.1 / 2;
wallShape.moveTo(-wallHeight, -wallWidth);
wallShape.lineTo(-wallHeight, wallWidth);
wallShape.lineTo(wallHeight, wallWidth);
wallShape.lineTo(wallHeight, -wallWidth);
const geo = new THREE.ExtrudeBufferGeometry(wallShape, extrudeSettings);
const mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ color: 0xFF5733 }));
scene.add(mesh);

// Line to vsualise the expected curve position
const material = new THREE.LineBasicMaterial( { color: 0xFF5733 } );
const geometry = new THREE.BufferGeometry().setFromPoints( points );
const line = new THREE.Line( geometry, material );
scene.add(line);

renderer.render(scene, camera);

Output:

As seen in the output, the CatmullRomCurve3 is rendered in black color and also, to visualise the exact curve that’s supposed to render I have rendered a THREE.Line as well giving it the same points as of the curve. I suspect the curve is not rendering as expected as the given points are too proximal to each other. Any suggestions to resolve this would be appreciated.

/сс

Have you thought about light? MeshPhongMaterial otherwise remains black.

I found your code to give closer to the desired results, if you apply a few changes:

	// Rendering the curve
	const randomSpline = new THREE.CatmullRomCurve3(points, false, 'catmullrom', 0.1);
	const extrudeSettings = {
	  steps: 500,
	  bevelEnabled: false,
	  extrudePath: randomSpline
	};
	const wallShape = new THREE.Shape();
	const wallHeight = 0.1 / 2;
	const wallWidth = 0.1 / 2;

Those changes include:

  • specifying all parameters to THREE.CatmullRomCurve3().
  • the third one makes it a catmullrom spline, and only then the fourth parameter “tension” gets applied.
  • I found that using a low value of 0.1 for the tension parameter makes the tube hug more closely the underlying line geometry, compared to a high value.
  • For better visual debugging I set the wallShape to a small square shape. That way any part of the line sticking out of the hull becomes more apparent.
  • Most important, I increased the number of steps in the extrudeSettings to 500.

P.S.:
for debugging, it also helps a lot, to add OrbitControls() and an animation loop, to be able to zoom in and out and to view your geometry from different angles

1 Like

@vielzutun.ch, applying all of the above suggestions does resolve this issue. But the application I’m working on renders about 2000, 5000 or even 10000 curves in the scene depending on the data we receive and rendering all of these curves with steps set as 500 is resulting in performance degradation. Is there any other way to avoid performance issues or any way to identify what value should be assigned to steps for each curve based on the points of the curve?

Since I was curious myself, I did a little analysis on your data:

You’ll recognise the values in the X, Y, Z columns. The dPos column shows the length of a vector from each position to the next position. Note the huge dynamic in distances between your points.

Wikipedia states, that CatmullRom “assumes uniform spacing” between the points, which your data clearly doesn’t provide.

I added a VertexNormalsHelper() to your mesh, to see the position where the CatmullRom spline’s points are placed, which is indeed at uniform intervals:

import { VertexNormalsHelper } from "../examples/jsm/helpers/VertexNormalsHelper.js";
...
scene.add(mesh);
let helper = new VertexNormalsHelper( mesh, 1.0, 0xff0000, 1 );
mesh.add( helper );

Your x-coordinate range spans about 110 units. Allocating only 25 points will place them at intervals of about 4 units, which is not enough to follow the sudden changes in direction which your data require.

Hope that helps.

that does not really matter, does it. in fact, in his case he would want more points on curved parts and less on flat parts, to save on total number of points.

good question, but it is better answered at https://math.stackexchange.com/ or something like that.

edit: as a cheap hack you could do steps: points.length * 4, for example (this still creates more points than necessary).

It does, as long as he is using a Catmullrom spline.

docs say,

curveType – Type of the curve. Default is centripetal .

which specifically assumes non-uniform spacing. but if you change curveType in his snippet, it will still look the same just because there is not enough points either way