如何在Three.js中实现带自然垂度的组件连接线函数?
Got it, let's fix that rope/cable effect so it looks naturally sagging like a real guitar cable. Your current code uses a fixed midpoint offset, which doesn't account for the actual rope length or physical sag—let's replace that with a catenary curve (the shape a hanging rope naturally takes) to get realistic results, and we'll properly use the ropeLength parameter you specified.
Why Your Original Code Isn't Working as Expected
- You're not using the
ropeLengthparameter at all, so the curve's length doesn't match the intended rope length - The midpoint offset is arbitrary (based on
shiftRatio) instead of being calculated to create a natural sag that fits the rope length - The orthogonal vector calculation might not always give the correct direction for the sag (we'll simplify this by sagging along the Y-axis for gravity-like behavior, but you can adjust if needed)
Revised Implementation with Catenary Curve
Here's the updated code that integrates a physics-accurate sagging rope, plus fixes to your existing scene setup:
const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); scene.add(camera); camera.position.z = 10; const renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); function animate() { requestAnimationFrame( animate ); renderer.render( scene, camera ); } animate(); // Add start/end spheres to visualize connection points const RADIUS = 1; const SEGMENTS = 16; const RINGS = 16; const sphereMaterial = new THREE.MeshLambertMaterial( { color: 0xCC0000 }); const sphereStart = new THREE.Mesh( new THREE.SphereGeometry( RADIUS, SEGMENTS, RINGS), sphereMaterial); sphereStart.position.set(0, -3, 0); scene.add(sphereStart); const sphereEnd = new THREE.Mesh( new THREE.SphereGeometry( RADIUS, SEGMENTS, RINGS), sphereMaterial); sphereEnd.position.set(6, 1, 0); scene.add(sphereEnd); const pointLight = new THREE.PointLight(0xFFFFFF); pointLight.position.set(10, 50, 130); scene.add(pointLight); // Add rope with a length longer than straight-line distance to create sag scene.add(drawSpline({x: 0, y: -3, z: 0}, {x: 6, y: 1, z: 0}, 10, 'blue')); function drawSpline(startPoint, endPoint, ropeLength, clr){ // Convert input points to THREE.Vector3 for easier math operations const start = new THREE.Vector3(startPoint.x, startPoint.y, startPoint.z); const end = new THREE.Vector3(endPoint.x, endPoint.y, endPoint.z); // Calculate straight-line distance between the two points const straightDistance = start.distanceTo(end); // Fallback: if rope length is shorter than straight distance, draw a straight line if (ropeLength <= straightDistance) { const points = [start, end]; const geometry = new THREE.BufferGeometry().setFromPoints(points); const material = new THREE.LineBasicMaterial({ color: clr, linewidth: 2 }); return new THREE.Line(geometry, material); } // Calculate horizontal/vertical differences between start and end const dx = end.x - start.x; const dy = end.y - start.y; const dz = end.z - start.z; // Keep Z constant for simplicity, adjust if needed // Calculate catenary curve parameters (physics-based sag) const a = (ropeLength * ropeLength - dy * dy - dx * dx) / (4 * dy); const p = dx / 2; const q = (ropeLength * ropeLength - dx * dx + dy * dy) / (4 * dy); // Generate points along the catenary const numPoints = 30; // More points = smoother initial curve const points = []; for (let i = 0; i <= numPoints; i++) { const t = i / numPoints; const x = start.x + t * dx; // Catenary equation adjusted to fit start/end positions const y = start.y + q - a * Math.cosh((x - start.x - p) / a); const z = start.z + t * dz; points.push(new THREE.Vector3(x, y, z)); } // Smooth the curve with CatmullRom for a polished look const curve = new THREE.CatmullRomCurve3(points); const smoothPoints = curve.getPoints(50); const geometry = new THREE.BufferGeometry().setFromPoints(smoothPoints); const material = new THREE.LineBasicMaterial({ color: clr, linewidth: 2 }); return new THREE.Line(geometry, material); }
Key Details of the Revised Function
- Straight Line Fallback: If the rope length is shorter than the direct path between points, we draw a straight line—you can't have a sagging rope that's shorter than the straight distance.
- Catenary Math: The catenary curve is the natural shape of a hanging rope under gravity. The calculations ensure the rope's total length matches your
ropeLengthparameter. - Smoothing: We generate initial catenary points, then use
CatmullRomCurve3to smooth the curve for a more natural, rounded look. - Controllable Sag: Adjust the
ropeLengthparameter to tweak how much the rope sags—longer lengths create deeper sags, shorter (but still longer than straight distance) create shallow sags.
If you want the sag to be perpendicular to the line between the two points (instead of along Y-axis), let me know and we can adjust the plane of the catenary calculation!
内容的提问来源于stack exchange,提问作者Rob

