布料模拟器使用three.js

时间:2016-11-25 14:48:03

标签: javascript three.js game-physics

我正在研究three.js来创建像hermes'网站这样的布料模拟器,区别在于我想要自上而下的波浪而不是赫尔墨斯的水平波浪。

然而,我成功地制作了我想要的垂直波(这里live也添加了下面的片段) 但正如你可以看到顶部没有固定它也略微移动我想做顶侧固定它不应该像爱马仕网站一样移动,并且想要使这个波连续而不是只有一次网页加载时,我也注意到一旦有线当我在浏览器中打开我的修改版本5-10分钟时,它会缩小尺寸(高度和宽度),并在一段时间后变得太小。我不知道为什么!!

这里的任何一位专家可以为这三件事做些帮助吗?

  • 使顶部像hermes一样固定。
  • 连续波浪。
  • 摆脱尺寸缩小。

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

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

}

Particle.prototype.addForce = function( force ) {
    this.a.add(
        this.tmp2.copy( force ).multiplyScalar( this.invMass )
    );

};

Particle.prototype.integrate = function( timesq ) {

    var newPos = this.tmp.subVectors( this.position, this.previous );
    // newPos.multiplyScalar( this.drag ).add( this.position );
    newPos.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 );

};

function Cloth( mass, w, h, restDistance, drag, clothFunction ) {
    function index( u, v ) {

        return u + v * ( w + 1 );

    }

    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, drag, clothFunction )
            );
        }
    }

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

    this.index = index;

}


function animatedProduct( container, size, canvas, image ) {
    this.DAMPING = .02;
    this.DRAG = 1 - this.DAMPING
    this.MASS = 2000;
    this.STIFFNESS = 1;
    this.SEGMENTS = 40;
    this.canvas = canvas;
    this.size = size;
    this.demoMode = !0;
    this.startTime = Date.now();
    this.image = image;
    this.restDistance = this.size / this.SEGMENTS;
    this.container = container;
    this.gravity = new THREE.Vector3( 0, -80, 0 ).multiplyScalar( this.MASS );
    this.TIMESTEP_SQ = Math.pow(.01, 2);

    this.tmpForce = new THREE.Vector3;
    this.diff = new THREE.Vector3;

    this.pins = [];
    
    for( var i = 0; i <= this.SEGMENTS; i++ )
        this.pins.push( i );


    this.degree = 0;
    this.wave = 0;
}

animatedProduct.prototype = {
    createPlane: function( width, height ) {
        return function(c, d) {
            var e = ( c - .5 ) * width,
                f = ( d + .5 ) * height,
                g = 0;

            return new THREE.Vector3( e, f, g )
        }
    },
    satisfyConstraints: function( p1, p2, distance ) {
        this.diff.subVectors( p2.position, p1.position );

        var currentDist = this.diff.length();

        if ( currentDist === 0 )
            return; // prevents division by 0

        this.diff.normalize();

        var correction = this.diff.multiplyScalar( currentDist - distance );
        var correctionHalf = correction.multiplyScalar( 0.5 );

        p1.position.add( correctionHalf );
        p2.position.sub( correctionHalf );
    },
    simulate: function( timestep_sq ) {
        var b, c, d, e, f, g, h, i, j = this.clothGeometry.faces;
        
        for (d = this.cloth.particles, b = 0, c = d.length; c > b; b++) {
            e = d[b];
            e.addForce(this.gravity);
            e.integrate(timestep_sq);
        }
        
        for (f = this.cloth.constraints, c = f.length, b = 0; c > b; b++) {
            g = f[b];
            this.satisfyConstraints(g[0], g[1], g[2]);
        }

        for (d = this.cloth.particles, b = 0, c = d.length; c > b; b++) {
            e = d[b];

            e.position.x = e.original.x;
        }   

        for (b = 0, c = this.pins.length; c > b; b++) {
            var k = this.pins[ b ],
                l = d[ k ];

            l.position.y = l.original.y;
            l.position.x = l.original.x;

            l.position.z = l.position.z + this.wave;
        }

        if( this.degree <= 6 ) {
            this.wave = Math.sin( this.degree ) * 6;

            this.degree += 0.017 * 42;
        }
        else
            this.wave = 0;
    },
    init: function() {
        this.clothFunction = this.createPlane( this.size, this.size );
        
        this.cloth = new Cloth( this.MASS, this.SEGMENTS, this.SEGMENTS, this.restDistance, this.DRAG, this.createPlane( this.size, this.size ) );
        
        this.scene = new THREE.Scene;

        this.camera = new THREE.PerspectiveCamera( 45, this.canvas.width / this.canvas.height, 1, 1e4 );
        this.camera.position.y = 0;
        this.camera.position.z = 1e3;

        this.scene.add( this.camera );

        this.light = new THREE.DirectionalLight( 16777215, 1 );
        this.light.position.set( 20, -20, 100 );

        this.scene.add( this.light );

        THREE.ImageUtils.crossOrigin = "";

        var texture = THREE.ImageUtils.loadTexture( this.image, {}, function() {
            this.canvas.classList.add("play")
        }.bind( this ) );

        texture.flipY = !1;
        texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        texture.anisotropy = 16;
        
        var b = new THREE.MeshPhongMaterial({
            ambient: 16777215,
            shininess: 20,
            map: texture,
            side: THREE.DoubleSide
        });

        this.clothGeometry = new THREE.ParametricGeometry( this.clothFunction, this.cloth.w, this.cloth.h );
        this.clothGeometry.dynamic = !0;
        this.clothGeometry.computeFaceNormals();
        
        var c = {
                texture: {
                    type: "t",
                    value: texture
                }
            },
            d = "varying vec2 vUV;void main() {vUV = 0.75 * uv;vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );gl_Position = projectionMatrix * mvPosition;}",
            e = "uniform sampler2D texture;varying vec2 vUV;vec4 pack_depth( const in float depth ) {const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );const vec4 bit_mask  = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );vec4 res = fract( depth * bit_shift );res -= res.xxyz * bit_mask;return res;}void main() {vec4 pixel = texture2D( texture, vUV );if ( pixel.a < 0.5 ) discard;gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );}";
        
        this.object = new THREE.Mesh( this.clothGeometry, b );
        
        this.object.position.set( 0, 0, 0 );
        this.scene.add( this.object );
        this.object.customDepthMaterial = new THREE.ShaderMaterial({
            uniforms: c,
            vertexShader: d,
            fragmentShader: e
        });

        this.renderer = new THREE.WebGLRenderer({
            antialias: !0,
            canvas: this.canvas
        });

        this.renderer.setSize( this.canvas.width, this.canvas.height );
        this.renderer.setClearColor( 16777215, 1 );
        this.renderer.autoClear = !1;
        this.renderer.autoClearDepth = !1;

        this.container.appendChild( this.renderer.domElement );
        this.renderer.gammaInput = !0;
        this.renderer.gammaOutput = !0;
        this.canvas.addEventListener("mousedown", this.onClick.bind( this ), !1 );

        for (var f = 0; 20 > f; f++) this.simulate(this.TIMESTEP_SQ);

        this.play();
    },
    onClick: function(a) {
    },
    animate: function() {
        this.animationFrame = window.requestAnimationFrame(this.animate.bind(this));
        
        this.simulate(this.TIMESTEP_SQ);
        this.render();
    },
    pause: function() {
        window.cancelAnimationFrame( this.animationFrame );
    },
    play: function() {
        this.scene ? this.animate() : this.init();
    },
    render: function() {
        for ( var a = this.cloth.particles, b = 0, c = a.length; c > b; b++ )
            this.clothGeometry.vertices[ b ].copy( a[ b ].position );

        this.clothGeometry.computeFaceNormals();
        this.clothGeometry.computeVertexNormals();
        this.clothGeometry.normalsNeedUpdate = !0;
        this.clothGeometry.verticesNeedUpdate = !0;
        
        this.camera.lookAt( this.scene.position );
        
        this.renderer.clear();
        this.renderer.render( this.scene, this.camera );
    },
    stop: function() {
        this.pause();
        this.canvas.parentNode.removeChild( this.canvas );
    }
};

var size = 700,
    container = document.getElementById( "product-container" ),
    image = "http://media.hermes.com/media/catalog/product/import/S/S01/S011/item/flat/hd/H001485S-17.jpg",
    canvas = document.createElement( "canvas" );
    canvas.width = canvas.height =  600 + 20,
    canvas.id = "product",
    container.appendChild( canvas ),
    productAnimation = new animatedProduct( container, size, canvas, image );

productAnimation.play();
<script src="http://maksible.com/cloth/cloth_slower_v2/cloth/three.min.js"></script>
<body>
    <div id="product-container"></div>
    <script type="text/javascript" src="three.min.js"></script>
    <script type="text/javascript" src="logic.js"></script>
</body>

1 个答案:

答案 0 :(得分:0)

以下是具有three.js但逻辑与您不同的解决方案。希望它对你有用。

turtles-own[exchangeinfo]

to setup
  clear-all
  reset-ticks
  make_turtles
end

to go
  move
  tick
  if (ticks = 1) [inspect turtle 1]
end

to make_turtles
  create-turtles 10
  ask turtles
  [
    set color pink
    set size 2
    set xcor random max-pxcor
    set ycor random max-pycor
    set exchangeinfo 0
  ]
end

to move

  ask turtles
  [right random-float 270
    forward random-float 3
    if ((count (turtles in-radius 2)) > 0)
    [move-to one-of turtles in-radius 2]

  ]

  encounter ;<- this is the function that will decide whether or not to exchange info.

end

to encounter
  ask turtles[
    if (count turtles-here > 0)
    [ifelse (random-float 1 < 0.25)  ;note this is essentially @Alan's answer
      [set exchangeinfo 1]
      [set exchangeinfo 0]
    ]
  ]
end

用这个改变你的逻辑代码并运行。 干杯!