You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js实现两组件间带自然垂坠的绳索连接函数

Fixing the Natural Sagging Rope Effect in Three.js

Hey there! I see you're trying to build a realistic rope connection between two points (like a guitar cable) with natural sag, but your current code uses a fixed offset that doesn't respect the actual rope length or physical behavior. Let's fix that by switching to a catenary curve—this is the exact mathematical shape of a hanging rope under gravity, which will give you that authentic垂坠 look you're after.

The Problem with Your Current Code

Your existing drawSpline function uses a fixed shiftRatio to offset the middle point of a Catmull-Rom curve. This works for specific positions but falls short in key ways:

  • It ignores the ropeLength parameter specified in your required function signature
  • The sag won't scale naturally when the distance between start/end points changes
  • The offset direction is arbitrary, not rooted in physical logic

The Solution: Catenary Curve Implementation

We'll rewrite the drawSpline function to calculate a catenary curve based on the start point, end point, and rope length. Here's the breakdown:

  1. Calculate the straight-line distance between points to validate the rope length
  2. Solve for the catenary's shape parameter (using a numerical approximation since there's no closed-form solution)
  3. Generate evenly spaced points along the catenary
  4. Convert these points to Three.js coordinates and create the final Line object

Full Modified Code

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();

const RADIUS = 1;
const SEGMENTS = 16;
const RINGS = 16;

const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xCC0000 });
const sphere1 = new THREE.Mesh(new THREE.SphereGeometry(RADIUS, SEGMENTS, RINGS), sphereMaterial);
sphere1.position.set(0, -3, 0);
scene.add(sphere1);

const sphere2 = new THREE.Mesh(new THREE.SphereGeometry(RADIUS, SEGMENTS, RINGS), sphereMaterial);
sphere2.position.set(6, 1, 0);
scene.add(sphere2);

const pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.set(10, 50, 130);
scene.add(pointLight);

// Test the function: rope length is longer than straight-line distance to get visible sag
scene.add(drawSpline({x: 0, y: -3, z: 0}, {x: 6, y: 1, z: 0}, 10, 'blue'));

/**
 * Draws a natural sagging rope between two points using a catenary curve
 * @param {Object} startPoint - {x, y, z} start coordinates
 * @param {Object} endPoint - {x, y, z} end coordinates
 * @param {number} ropeLength - Total length of the rope
 * @param {string} clr - Color of the rope (default: white)
 * @returns {THREE.Line} Three.js Line object with the sagging rope
 */
function drawSpline(startPoint, endPoint, ropeLength, clr = 'white') {
    // Convert start/end to Three.js vectors for easier calculations
    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 straightLineDist = start.distanceTo(end);
    
    // If rope length is shorter than straight line, return a straight line (no sag possible)
    if (ropeLength <= straightLineDist) {
        const points = [start, end];
        const geometry = new THREE.BufferGeometry().setFromPoints(points);
        const material = new THREE.LineBasicMaterial({ color: clr });
        return new THREE.Line(geometry, material);
    }

    // Calculate horizontal (X-Z plane) and vertical (Y) components between points
    const dx = end.x - start.x;
    const dy = end.y - start.y;
    const dz = end.z - start.z;
    const horizontalDist = Math.sqrt(dx * dx + dz * dz);

    // Solve for catenary parameter 'a' using Newton-Raphson numerical approximation
    let a = horizontalDist / 2;
    const tolerance = 0.001;
    let iterations = 0;
    while (iterations < 100) {
        const f = 2 * a * Math.sinh(horizontalDist / (2 * a)) - Math.sqrt(ropeLength * ropeLength - dy * dy);
        const fPrime = 2 * Math.sinh(horizontalDist / (2 * a)) - (horizontalDist / a) * Math.cosh(horizontalDist / (2 * a));
        const delta = f / fPrime;
        a -= delta;
        if (Math.abs(delta) < tolerance) break;
        iterations++;
    }

    // Generate smooth points along the catenary curve
    const numPoints = 30; // Adjust for smoother/performance tradeoff
    const points = [];
    for (let i = 0; i <= numPoints; i++) {
        const t = i / numPoints;
        const x = start.x + dx * t;
        const z = start.z + dz * t;
        
        // Calculate catenary Y position based on the parameter 'a'
        const s = t * horizontalDist;
        const y = start.y + dy * t + a * (Math.cosh((s - horizontalDist / 2) / a) - Math.cosh(horizontalDist / (2 * a)));
        
        points.push(new THREE.Vector3(x, y, z));
    }

    // Assemble the final Line object
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    const material = new THREE.LineBasicMaterial({ color: clr });
    const curveObject = new THREE.Line(geometry, material);
    
    return curveObject;
}

Key Details Explained

  • Validation Check: The function first ensures the rope length is longer than the straight-line distance (you can't have a sagging rope shorter than the distance between two points!).
  • Numerical Solver: We use the Newton-Raphson method to find the catenary parameter a, which controls how much the rope sags—this is the key to getting realistic behavior.
  • 3D Adaptation: Since we're working in 3D, we calculate the horizontal distance in the X-Z plane and apply the sag to the Y axis, mimicking real-world gravity.
  • Smoothness Control: The numPoints variable lets you adjust curve smoothness—more points mean a smoother rope, fewer points mean better performance.

Testing Tips

  • Try different rope lengths: Longer lengths will create dramatic sag, while lengths just slightly longer than the straight-line distance will have minimal, subtle sag.
  • Move the start/end points around—the sag will adjust automatically based on their position and the rope length.

内容的提问来源于stack exchange,提问作者Rob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:05:07