Ткань Simulaltion 3d модель ткани obj - PullRequest
0 голосов
/ 22 апреля 2020

Я видел много симуляций одежды в трех. js. Я обнаружил, что это делается только с 2d плоскими поверхностями. Но есть ли способ, которым я могу смоделировать 3d модель ткани, как показано ниже ..

3d model cloth

Быть смоделированным как ткань в 3d сцене .

Существует множество обучающих программ по симуляции плоскости 2d, например

2d cloth simuation

И код для них приведен ниже ...



<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <link type="text/css" rel="stylesheet" href="main.css">

</head>

<body>

    <script src="../build/three.js"></script>
    <script src="../src/OrbitControls.js"></script>
    <script>
        var params = {
            enableWind: true,
            tooglePins: togglePins
        };

        var DAMPING = 0.03;
        var DRAG = 1 - DAMPING;
        var MASS = 0.1;
        var restDistance = 25;

        var xSegs = 10;
        var ySegs = 10;

        var clothFunction = plane(restDistance * xSegs, restDistance * ySegs);

        var cloth = new Cloth(xSegs, ySegs);

        var GRAVITY = 981 * 1.4;
        var gravity = new THREE.Vector3(0, -GRAVITY, 0).multiplyScalar(MASS);


        var TIMESTEP = 18 / 1000;
        var TIMESTEP_SQ = TIMESTEP * TIMESTEP;

        var pins = [];

        var windForce = new THREE.Vector3(0, 0, 0);

        var tmpForce = new THREE.Vector3();

        var lastTime;


        function plane(width, height) {

            return function(u, v, target) {

                var x = (u - 0.5) * width;
                var y = (v + 0.5) * height;
                var z = 0;

                target.set(x, y, z);

            };

        }

        function Particle(x, y, z, mass) {

            this.position = new THREE.Vector3();
            this.previous = new THREE.Vector3();
            this.original = new THREE.Vector3();
            this.a = new THREE.Vector3(0, 0, 0); // acceleration
            this.mass = mass;
            this.invMass = 1 / mass;
            this.tmp = new THREE.Vector3();
            this.tmp2 = new THREE.Vector3();

            // init

            clothFunction(x, y, this.position); // position
            clothFunction(x, y, this.previous); // previous
            clothFunction(x, y, this.original);

        }

        // Force -> Acceleration

        Particle.prototype.addForce = function(force) {

            this.a.add(
                this.tmp2.copy(force).multiplyScalar(this.invMass)
            );

        };


        // Performs Verlet integration

        Particle.prototype.integrate = function(timesq) {

            var newPos = this.tmp.subVectors(this.position, this.previous);
            newPos.multiplyScalar(DRAG).add(this.position);
            newPos.add(this.a.multiplyScalar(timesq));

            this.tmp = this.previous;
            this.previous = this.position;
            this.position = newPos;

            this.a.set(0, 0, 0);

        };


        var diff = new THREE.Vector3();

        function satisfyConstraints(p1, p2, distance) {

            diff.subVectors(p2.position, p1.position);
            var currentDist = diff.length();
            if (currentDist === 0) return; // prevents division by 0
            var correction = diff.multiplyScalar(1 - distance / currentDist);
            var correctionHalf = correction.multiplyScalar(0.5);
            p1.position.add(correctionHalf);
            p2.position.sub(correctionHalf);

        }


        function Cloth(w, h) {

            w = w || 10;
            h = h || 10;
            this.w = w;
            this.h = h;

            var particles = [];
            var constraints = [];

            var u, v;

            // Create particles
            for (v = 0; v <= h; v++) {

                for (u = 0; u <= w; u++) {

                    particles.push(
                        new Particle(u / w, v / h, 0, MASS)
                    );

                }

            }

            // Structural

            for (v = 0; v < h; v++) {

                for (u = 0; u < w; u++) {

                    constraints.push([
                        particles[index(u, v)],
                        particles[index(u, v + 1)],
                        restDistance
                    ]);

                    constraints.push([
                        particles[index(u, v)],
                        particles[index(u + 1, v)],
                        restDistance
                    ]);

                }

            }

            for (u = w, v = 0; v < h; v++) {

                constraints.push([
                    particles[index(u, v)],
                    particles[index(u, v + 1)],
                    restDistance

                ]);

            }

            for (v = h, u = 0; u < w; u++) {

                constraints.push([
                    particles[index(u, v)],
                    particles[index(u + 1, v)],
                    restDistance
                ]);

            }



            this.particles = particles;
            this.constraints = constraints;

            function index(u, v) {

                return u + v * (w + 1);

            }

            this.index = index;

        }

        function simulate(time) {

            if (!lastTime) {

                lastTime = time;
                return;

            }

            var i, j, il, particles, particle, constraints, constraint;

            // Aerodynamics forces

            if (params.enableWind) {

                var indx;
                var normal = new THREE.Vector3();
                var indices = clothGeometry.index;
                var normals = clothGeometry.attributes.normal;

                particles = cloth.particles;

                for (i = 0, il = indices.count; i < il; i += 3) {

                    for (j = 0; j < 3; j++) {

                        indx = indices.getX(i + j);
                        normal.fromBufferAttribute(normals, indx);
                        tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(windForce));
                        particles[indx].addForce(tmpForce);

                    }

                }

            }

            for (particles = cloth.particles, i = 0, il = particles.length; i < il; i++) {

                particle = particles[i];
                particle.addForce(gravity);

                particle.integrate(TIMESTEP_SQ);

            }

            // Start Constraints

            constraints = cloth.constraints;
            il = constraints.length;

            for (i = 0; i < il; i++) {

                constraint = constraints[i];
                satisfyConstraints(constraint[0], constraint[1], constraint[2]);

            }



            // Floor Constraints

            for (particles = cloth.particles, i = 0, il = particles.length; i < il; i++) {

                particle = particles[i];
                pos = particle.position;
                if (pos.y < -250) {

                    pos.y = -250;

                }

            }

            // Pin Constraints

            for (i = 0, il = pins.length; i < il; i++) {

                var xy = pins[i];
                var p = particles[xy];
                p.position.copy(p.original);
                p.previous.copy(p.original);

            }


        }

        /* testing cloth simulation */

        var pinsFormation = [];
        var pins = [6];

        pinsFormation.push(pins);

        pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        pinsFormation.push(pins);

        pins = [0];
        pinsFormation.push(pins);

        pins = []; // cut the rope ;)
        pinsFormation.push(pins);

        pins = [0, cloth.w]; // classic 2 pins
        pinsFormation.push(pins);

        pins = pinsFormation[1];

        function togglePins() {

            pins = pinsFormation[~~(Math.random() * pinsFormation.length)];

        }

        var container, stats;
        var camera, scene, renderer;

        var clothGeometry;
        var sphere;
        var object;

        init();
        animate();

        function init() {

            container = document.createElement('div');
            document.body.appendChild(container);

            // scene

            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x000000);
            scene.fog = new THREE.Fog(0xcce0ff, 500, 10000);

            // camera

            camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000);
            camera.position.set(1000, 50, 1500);

            // lights

            scene.add(new THREE.AmbientLight(0x666666));

            var light = new THREE.DirectionalLight(0xdfebff, 1);
            light.position.set(50, 200, 100);
            light.position.multiplyScalar(1.3);

            light.castShadow = true;

            light.shadow.mapSize.width = 1024;
            light.shadow.mapSize.height = 1024;

            var d = 300;

            light.shadow.camera.left = -d;
            light.shadow.camera.right = d;
            light.shadow.camera.top = d;
            light.shadow.camera.bottom = -d;

            light.shadow.camera.far = 1000;

            scene.add(light);

            // cloth material

            var loader = new THREE.TextureLoader();
            var clothTexture = loader.load('textures/water/Water_1_M_Flow.jpg');
            clothTexture.anisotropy = 16;

            var clothMaterial = new THREE.MeshLambertMaterial({
                map: clothTexture,
                side: THREE.DoubleSide,
                // wireframe: true,
                // alphaTest: 0.5
            });

            // cloth geometry

            clothGeometry = new THREE.ParametricBufferGeometry(clothFunction, cloth.w, cloth.h);

            // cloth mesh

            object = new THREE.Mesh(clothGeometry, clothMaterial);
            object.position.set(0, 0, 0);
            object.castShadow = true;
            scene.add(object);

            // object.customDepthMaterial = new THREE.MeshDepthMaterial({
            //     depthPacking: THREE.RGBADepthPacking,
            //     map: clothTexture,
            //     alphaTest: 0.5
            // });


            // renderer

            renderer = new THREE.WebGLRenderer({
                antialias: true
            });
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);

            container.appendChild(renderer.domElement);

            renderer.outputEncoding = THREE.sRGBEncoding;

            renderer.shadowMap.enabled = true;

            // controls

            var controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.maxPolarAngle = Math.PI * 0.5;
            controls.minDistance = 1000;
            controls.maxDistance = 5000;

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

            var time = Date.now();

            var windStrength = Math.cos(time / 7000) * 20 + 40;

            windForce.set(Math.sin(time / 2000), Math.cos(time / 3000), Math.sin(time / 1000));
            windForce.normalize();
            windForce.multiplyScalar(windStrength);

            simulate(time);
            render();

        }

        function render() {

            var p = cloth.particles;

            for (var i = 0, il = p.length; i < il; i++) {

                var v = p[i].position;

                clothGeometry.attributes.position.setXYZ(i, v.x, v.y, v.z);

            }

            clothGeometry.attributes.position.needsUpdate = true;

            clothGeometry.computeVertexNormals();

            renderer.render(scene, camera);

        }
    </script>
</body>

</html>


Могу ли я загрузить 3d модель ткани и изменить код так, чтобы она также имитировала ткань ... Так же, как повешение ткани, и когда дует ветер, они должны реагировать соответствующим образом. Используя три. js вместе с патронами. js или пушкой. js тоже

Ответы [ 2 ]

2 голосов
/ 22 апреля 2020

Код, который вы разместили, не будет носить одежду, так как не будет столкновений. Код в боеприпасах. js, но вам нужно создать одежду самостоятельно.

Ткань обычно моделируется с использованием масс и пружин.

M--s--M--s--M--s--M
|\   /|\   /|\   /|
| \ / | \ / | \ / |
s  s  s  s  s  s  s
| / \ | / \ | / \ |
|/   \|/   \|/   \|
M--s--M--s--M--s--M
|\   /|\   /|\   /|
| \ / | \ / | \ / |
s  s  s  s  s  s  s
| / \ | / \ | / \ |
|/   \|/   \|/   \|
M--s--M--s--M--s--M

выше - это диаграмма масс (M) и источники (ы). Каждая пружина соединена между двумя массами и пытается не дать массам слишком сильно растянуться и приблизиться. Для моделирования одежды вам нужны тысячи масс и пружин.

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

Кроме того, что вам нужно столкновения на теле персонажа, на которого вы собираетесь надеть одежду, с которой столкнутся массы, поэтому они не go внутри тела и просто падают на пол. У большинства физических движков есть несколько примитивов, таких как коробка, сфера, капсула, цилиндр, которые оптимизированы. Они также могут использовать универсальные c полигоны для столкновения, но они медленнее, поэтому вам решать, можете ли вы использовать несколько примитивных фигур, прикрепленных к вашей модели, для столкновения или вам нужна более высокая точность использования полигонов для ваших столкновений.

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

боеприпасы. js AFAICT не имеет документов, кроме как сказать, что это порт Bullet Physics, документация которого здесь

Я не вижу JavaScript демонстрации для нестандартной ткани.

Этот боеприпас. js демо , кажется, не подходит под одежду, потому что, если бы эти фигуры были на самом деле одеждой, они просто рухнули бы в кучу, а не действовали как будто они раздуты, но тогда, возможно, такое поведение - это настройка. Вам нужно будет выкопать документы и / или этого образца

Вам нужно будет отделить геометрию вашей одежды от модели человеческого тела / манекена, поверните одежда превращается в мягкое тело или вручную создает массы и пружины, а затем также делает тело человека / человека жестким или из меня sh, или из примитивов, чтобы оно удерживало одежду.

Если бы я делал это Я бы начал с кубика твердого тела внутри сферы мягкого тела и посмотрел бы, насколько подробно мне нужно сделать сферу, чтобы она вела себя как одежда (складка и складка)

0 голосов
/ 23 апреля 2020

Popup Dev Я работал с этим, но найти, что использовать геометрию плоскости для манипулирования намного проще. У меня также есть вопросы о том, как оригинальный пример ткани рассчитывает и использует «ветер». Свяжитесь со мной в твиттере и давайте обсудим.

...