Как анимировать треугольники в PlaneBufferGeometry?
Как я могу анимировать или просто взаимодействовать с этими треугольниками: https://jsfiddle.net/t2bjzc8r/1/ ? Я планирую наложить текстуру - фотографию на PlaneBufferGeometry и разбить ее на эти треугольники(будто стеклянная фотография разбивается на части), но я не могу понять, как я мог бы управлять ими. cutt.ly/8fJRJHG - как пример (нашел в гугле)
let renderer, scene, camera, geometry, material;
init();
animate();
function init(){
//SCENE
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x09090C );
//CAMERA
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 10000 );
camera.position.z = 3;
//scene.add(camera);
//Plane
geometry = new THREE.PlaneBufferGeometry( 1, 1, 10, 10 );
material = new THREE.MeshBasicMaterial({
wireframe: true
});
let plane = new THREE.Mesh( geometry, material );
scene.add( plane );
//RENDERER
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
renderer.render( scene, camera );
}
Ответы (1 шт):
В общем и целом, вариантов создания данного эффекта - масса. Дабы не лезть в дебри буферной геометрии, инстансов и кастомных шейдеров, можно пойти по самому простому пути: разбить плоскость на треугольники, где каждый треугольник - отдельный меш, который можно анимировать отдельно, используя Tween.js или GSAP (последний сейчас все больше входит в моду в связке с Three.js)
body{
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js";
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 15, 30);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
// плоскость и ее расщепление на треугольники
let division = 5;
let divisionStep = 1 / division;
let g = new THREE.PlaneBufferGeometry(1, 1, division, division);
// чуток рандомизируем точки внути плоскости и пересчитаем UV координаты
let pos = g.getAttribute("position");
let uv = g.getAttribute("uv");
let v3 = new THREE.Vector3(); // временный вектор для всяких нужд
for (let i = 0; i < pos.count; i++){
v3.fromBufferAttribute(pos, i);
if (Math.max(Math.abs(v3.x), Math.abs(v3.y)) < 0.5){
pos.setXYZ(
i,
v3.x + (Math.random() - 0.5) * divisionStep * 0.75,
v3.y + (Math.random() - 0.5) * divisionStep * 0.75,
0
);
}
}
g.translate(0.5, 0.5, 0);
for (let i = 0; i < uv.count; i++){
v3.fromBufferAttribute(pos, i);
uv.setXY(i, v3.x, v3.y);
}
g.translate(-0.5, -0.5, 0);
g.scale(10, 10, 1);
// разбиваем на осколки
let m = new THREE.MeshBasicMaterial({side: THREE.DoubleSide, wireframe: false, map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg")});
let pieces = [];
let faces = g.index.count / 3;
let tempTri = new THREE.Triangle();
let center = new THREE.Vector3();
for(let i = 0; i < faces; i++){
let a = new THREE.Vector3().fromBufferAttribute(pos, g.index.getX(i * 3 + 0));
let b = new THREE.Vector3().fromBufferAttribute(pos, g.index.getX(i * 3 + 1));
let c = new THREE.Vector3().fromBufferAttribute(pos, g.index.getX(i * 3 + 2));
let pg = new THREE.BufferGeometry().setFromPoints([a,b,c]);
tempTri.set(a, b, c);
tempTri.getMidpoint(center);
pg.translate(-center.x, -center.y, -center.z);
let uvs = [];
uvs.push(
uv.getX(g.index.getX(i * 3 + 0)),
uv.getY(g.index.getX(i * 3 + 0)),
uv.getX(g.index.getX(i * 3 + 1)),
uv.getY(g.index.getX(i * 3 + 1)),
uv.getX(g.index.getX(i * 3 + 2)),
uv.getY(g.index.getX(i * 3 + 2))
);
//console.log(uvs);
pg.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
let p = new THREE.Mesh(pg, m);
p.userData.finish = {
position: new THREE.Vector3().copy(center),
rotation: new THREE.Euler().copy(p.rotation)
};
p.userData.start = {
position: new THREE.Vector3().random().subScalar(0.5).multiplyScalar(2 * 25),
rotation: new THREE.Euler(
Math.random() * Math.PI * 10 + 5,
Math.random() * Math.PI * 10 + 5,
Math.random() * Math.PI * 10 + 5
)
}
p.position.copy(center);
pieces.push(p);
scene.add(p);
}
// анимация
pieces.forEach(piece => {
let duration = Math.random() * 5 + 1;
let p = piece.userData.finish.position;
let r = piece.userData.finish.rotation;
gsap.to(piece.position.copy(piece.userData.start.position), {x: p.x, y: p.y, z: p.z, duration: duration
}).play();
gsap.to(piece.rotation.copy(piece.userData.start.rotation), {x: r.x, y: r.y, z: r.z, duration: duration}).play();
});
renderer.setAnimationLoop(()=>{
renderer.render(scene, camera);
});
</script>