3. js эффект веревки / кабеля - анимация толстых линий - PullRequest
0 голосов
/ 16 июня 2020

С помощью 3. js Я хочу создать эффект раскачивания объекта на тросе или веревке. Это не требует реальной физики, поскольку "качающийся" объект просто следует фиксированной анимации. Самым простым решением является использование THREE.Line, однако проблема в том, что THREE.Line может иметь толщину только 1 пиксель и выглядит ужасно.

В трех примерах. js есть пример «толстых линий» :

https://threejs.org/examples/?q=lines#webgl_lines_fat

однако проблема в том, что после того, как я создал линию с помощью LineGeometry (), я не могу понять, как ее анимировать.

Единственное решение, которое я нашел до сих пор, - это удалять, а затем создавать новую строку в каждом кадре, что работает, но кажется действительно неэкономичным и плохо оптимизированным способом сделать это.

Кто-нибудь знает лучший способ анимировать линейную геометрию без удаления и замены каждого кадра? Или есть другой метод из трех. js, который позволил бы мне создавать более толстые анимированные линии?

Спасибо !!

Ответы [ 2 ]

1 голос
/ 17 июня 2020

Просто хотел опубликовать более подробную версию отличного ответа West Langleys. Чтобы оживить ТРИ Line2, вам нужно использовать команды:

line.geometry.attributes.instanceStart.setXYZ( index, x, y, z ); 
line.geometry.attributes.instanceEnd.setXYZ( index, x, y, z );

Меня смутило значение индекса - вместо того, чтобы думать о Line2 как о точках вершин (метод, используемый для создания линии), вам нужно подумать о Line2 как о том, что он состоит из отдельных отдельных линий между двумя наборами точек ... поэтому каждая линия имеет начальную точку и конечную точку.

Следовательно, "W" определяется НЕ как 5 вершин, а как 4 линии. Таким образом, вы можете «разделить» Line2, установив другую Начальную точку на конечную точку предыдущих строк. Индекс - это количество строк, составляющих ваш объект. В моем случае у меня есть две линии, образующие V-образную форму ... поэтому я установил свой индекс на 1, чтобы повлиять на конец строки 0 и начало строки 1, как в примере Уэста:

var index = 1;
line.geometry.attributes.instanceEnd.setXYZ( index - 1, x, y, z );
line.geometry.attributes.instanceStart.setXYZ( index, x, y, z );

И тогда вам просто нужно обновить строку, используя:

line.geometry.attributes.instanceStart.data.needsUpdate = true;

Еще раз спасибо West за этот действительно полезный ответ. Я бы никогда не догадался об этом, поскольку вы не видите этих переменных, когда смотрите на свойства объекта Line2. Очень полезная информация. Надеюсь, это когда-нибудь поможет кому-то другому.

1 голос
/ 17 июня 2020

На самом деле у меня есть небольшой проект, в котором я анимирую лампочку, раскачивающуюся по веревке. Вы можете получить к нему доступ здесь , функции, о которых я говорю ниже, находятся в helperFns. js.

На самом деле, я в основном создаю свой прикрепленный объект отдельно:

let geometry = new THREE.SphereGeometry( 1, 32, 32 );
var material = new THREE.MeshStandardMaterial({color:0x000000,emissive:0xffffff,emissiveIntensity:lightIntensity});
bulb = new THREE.Mesh( geometry, material );
light = new THREE.PointLight(0xF5DCAF,lightIntensity,Infinity,2)
light.power = lightIntensity*20000
light.position.set(0,length*Math.sin(theta),z0-length*Math.cos(theta))
light.add(bulb)
light.castShadow = true;
hemiLight = new THREE.HemisphereLight( 0xddeeff, 0x0f0e0d, 0.1 );
scene.add(hemiLight)
scene.add(light)

Затем я добавляю связанный с ним сплайн:

// Create the wire linking the bulb to the roof
var curveObject = drawSpline(light.position,{x:0,y:0,z:z0},0xffffff);
scene.add(curveObject)

Где drawSpline - это следующая функция:

// Build a spline representing the wire between the roof and the bulb. The new middle point is computed as the middle point shifted orthogonally from the lign by shiftRatio
function drawSpline(beginning,end,clr){
    // Compute y sign to know which way to bend the wire
    let ySign = Math.sign((end.y+beginning.y)/2)
    // Compute the bending strength and multiply per Math.abs(beginning.y) to ensure it decreases as the bulb gets closer to the theta = 0 position, and also to ensure
    // that the shift is null if thete is null (no discontinuity in the wire movement)
    let appliedRatio = -shiftRatio*Math.abs(beginning.y)
    // Compute middle line position vector and the direction vector from the roof to the bulb
    let midVector = new THREE.Vector3( 0, (end.y+beginning.y)/2, (end.z+beginning.z)/2 )
    let positionVector = new THREE.Vector3(0,end.y-beginning.y,end.z-beginning.z)
    // Compute the orthogonal vector to the direction vector (opposite sense to the bending shift)
    let orthogVector = new THREE.Vector3(0,positionVector.z,-positionVector.y).normalize() 


    // Compute the curve passing by the three points
    var curve = new THREE.CatmullRomCurve3( [
        new THREE.Vector3( beginning.x, beginning.y, beginning.z ),
        midVector.clone().addScaledVector(orthogVector,ySign*appliedRatio),
        new THREE.Vector3( end.x, end.y, end.z ),
    ]);

    // Build the curve line object
    var points = curve.getPoints( 20 );
    var geometry = new THREE.BufferGeometry().setFromPoints( points );
    var material = new THREE.LineBasicMaterial( { color : clr } );

    // Create the final object to add to the scene
    var curveObject = new THREE.Line( geometry, material );
    return curveObject;
}

Создает CatmullRomCurve3, интерполируя 3 точки (одна фиксированная точка в (0, 0, 0), одна средняя точка для применения изгиба и положение лампы. Вы можете На самом деле начните с прямой линии, а затем попытайтесь вычислить некоторую кривую.
Для этого вы хотите получить вектор, ортогональный линии, и сдвинуть линию (в правильную сторону) вдоль этого вектора.

И, наконец, при каждом вызове animate () я перерисовываю сплайн для нового положения лампочки:

scene.children[2] = drawSpline(light.position,{x:0,y:0,z:z0},0xffffff)

Скажите мне, если есть точка, которую вы не получили, но это должно помочь ваша проблема.

...