Matvii Hodovaniuk
Matvii Hodovaniuk

Reputation: 513

three.js n-body simulation

I am trying to do like this http://mbostock.github.io/protovis/ex/nbody.html and same project. But my system doesn't work. Can you help me This is my http://mendow.github.io/projects/n-body/index.html I gues i am doing wrong in place calculating attration each part to each

Problem is particles has one mass center and spin around it instead has mass center which change it position

<!DOCTYPE html>
<html>

<head>
  <title>n-body</title>
  <script src="http://mendow.github.io/projects/n-body/libs/three.js"></script>
  <script src="http://mendow.github.io/projects/n-body/libs/OrbitControls.js"></script>
  <script src="http://mendow.github.io/projects/n-body/libs/OBJLoader.js"></script>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }

  </style>
</head>
<script>
  //define global variable
  {
    var renderer;
    var scene;
    var camera;
    var orbit;
    var ps;

    var G = 9.81;
    var dt = 0.0001;
    var count = 1000;
    var cam = 30;
  }

  function init() {
    {
      // create a scene, that will hold all our elements such as objects, cameras and lights.
      scene = new THREE.Scene();

      // create a camera, which defines where we're looking at.
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

      // create a render, sets the background color and the size
      renderer = new THREE.WebGLRenderer();
      renderer.setClearColor(0x000000, 1.0);
      renderer.setSize(window.innerWidth, window.innerHeight);

      // position and point the camera to the center of the scene
      camera.position.x = cam;
      camera.position.y = cam;
      camera.position.z = cam;
      camera.lookAt(scene.position);

      orbit = new THREE.OrbitControls(camera);
    }
    setupParticleSystem(count);


    // add the output of the renderer to the html element
    document.body.appendChild(renderer.domElement);

    // call the render function
    render();
  }

  function setupParticleSystem(y) {



    var geometry = new THREE.Geometry();


    for (var j = 0; j < y; j++) {
      var v = new THREE.Vector3();
      var ran = 30;
      v.x = intRand(ran, -ran);
      v.y = intRand(ran, -ran);
      v.z = intRand(ran, -ran);
      v.vel = new THREE.Vector3(intRand(1, -1), intRand(1, -1), intRand(1, -1));
      v.acc =new THREE.Vector3(intRand(1, -1), intRand(1, -1), intRand(1, -1));
      v.mass = intRand(5, 0);
      geometry.vertices.push(v);
    }

    console.log(geometry.vertices);
    // use a material for some styling
    var psMat = new THREE.PointCloudMaterial();
    psMat.color = new THREE.Color(0x55ff55);
    psMat.transparent = true;
    psMat.size = 1;
    psMat.blending = THREE.AdditiveBlending;

    // Create a new particle system based on the provided geometry
    ps = new THREE.PointCloud(geometry, psMat);
    ps.sizeAttenuation = true;
    ps.sortParticles = true;

    ps.position.y = 100 / cam;
    ps.position.x = 100 / cam;
    ps.position.z = 100 / cam;
    // add the particle system to the scene
    scene.add(ps);

  }


  var step = 0;

  function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);

    var r,
      mult;
    var geometry = ps.geometry;
    var temp = ps.geometry;
    for (var i = 0; i < geometry.vertices.length; i++) {

      for (var j = 0; j < geometry.vertices.length; j++) {
        if (i != j) {
          var particle = geometry.vertices[i];
          var cntr = geometry.vertices[j];

          r = particle.length(cntr);

          mult = (-1) * G * (cntr.mass * particle.mass) / Math.pow(r, 3);

          particle.acc.x = mult * particle.x;
          particle.vel.x += particle.acc.x * dt;
          particle.x += particle.vel.x * dt;

          particle.acc.y = mult * particle.y;
          particle.vel.y += particle.acc.y * dt;
          particle.y += particle.vel.y * dt;

          particle.acc.z = mult * particle.z;
          particle.vel.z += particle.acc.z * dt;
          particle.z += particle.vel.z * dt;
        }
      }
    }

    geometry.verticesNeedUpdate = true;
    geometry.colorsNeedUpdate = true;

    orbit.update();
  }

  // calls the init function when the window is done loading.
  window.onload = init;

  function mrand() {
    return Math.random();
  }

  function intRand(min, max) {
    return Math.random() * (max - min) + min;
  }

</script>

<body>
</body>

</html>

Upvotes: 2

Views: 797

Answers (2)

Paul Masson
Paul Masson

Reputation: 241

Matvey, you need to calculate the changes to all particle locations and velocities with the old values before adding them to get new values. Otherwise you're calculating some of the changes based on altered positions and velocities which is inaccurate.

I've edited your render loop:

<!DOCTYPE html>
<html>

<head>
  <title>n-body</title>
  <script src="http://mendow.github.io/projects/n-body/libs/three.js"></script>
  <script src="http://mendow.github.io/projects/n-body/libs/OrbitControls.js"></script>
  <script src="http://mendow.github.io/projects/n-body/libs/OBJLoader.js"></script>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }

  </style>
</head>
<script>
  //define global variable
  {
    var renderer;
    var scene;
    var camera;
    var orbit;
    var ps;

    var G = 9.81;
    var dt = 0.0001;
    var count = 1000;
    var cam = 30;
  }

  function init() {
    {
      // create a scene, that will hold all our elements such as objects, cameras and lights.
      scene = new THREE.Scene();

      // create a camera, which defines where we're looking at.
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

      // create a render, sets the background color and the size
      renderer = new THREE.WebGLRenderer();
      renderer.setClearColor(0x000000, 1.0);
      renderer.setSize(window.innerWidth, window.innerHeight);

      // position and point the camera to the center of the scene
      camera.position.x = cam;
      camera.position.y = cam;
      camera.position.z = cam;
      camera.lookAt(scene.position);

      orbit = new THREE.OrbitControls(camera);
    }
    setupParticleSystem(count);


    // add the output of the renderer to the html element
    document.body.appendChild(renderer.domElement);

    // call the render function
    render();
  }

  function setupParticleSystem(y) {



    var geometry = new THREE.Geometry();


    for (var j = 0; j < y; j++) {
      var v = new THREE.Vector3();
      var ran = 30;
      v.x = intRand(ran, -ran);
      v.y = intRand(ran, -ran);
      v.z = intRand(ran, -ran);
      v.vel = new THREE.Vector3(intRand(1, -1), intRand(1, -1), intRand(1, -1));
      v.acc =new THREE.Vector3(intRand(1, -1), intRand(1, -1), intRand(1, -1));
      v.mass = intRand(5, 0);
      geometry.vertices.push(v);
    }

    console.log(geometry.vertices);
    // use a material for some styling
    var psMat = new THREE.PointCloudMaterial();
    psMat.color = new THREE.Color(0x55ff55);
    psMat.transparent = true;
    psMat.size = 1;
    psMat.blending = THREE.AdditiveBlending;

    // Create a new particle system based on the provided geometry
    ps = new THREE.PointCloud(geometry, psMat);
    ps.sizeAttenuation = true;
    ps.sortParticles = true;

    ps.position.y = 100 / cam;
    ps.position.x = 100 / cam;
    ps.position.z = 100 / cam;
    // add the particle system to the scene
    scene.add(ps);

  }


  var step = 0;

  function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);

    var r, mult;
    var geometry = ps.geometry;
    var temp = ps.geometry;

    var dx = [];
    var dv = [];

    for (var i = 0; i < geometry.vertices.length; i++) {

    var v = geometry.vertices[i].vel;
    dx.push( new THREE.Vector3( v.x * dt, v.y * dt, v.z * dt ) );

    var dvx = 0;
    var dvy = 0;
    var dvz = 0;

    for (var j = 0; j < geometry.vertices.length; j++) {

       if (i != j) {

          mult = (-1) * G * geometry.vertices[i].mass * geometry.vertices[j].mass;

         var vi = geometry.vertices[i];
         var vj = geometry.vertices[j];

         // http://www.scholarpedia.org/article/N-body_simulations_%28gravitational%29
         epsilon = .1;

         var r = Math.sqrt( ( vi.x - vj.x ) * ( vi.x - vj.x )
                          + ( vi.y - vj.y ) * ( vi.y - vj.y )
                          + ( vi.z - vj.z ) * ( vi.z - vj.z ) + epsilon )

         dvx += mult * ( vi.x - vj.x ) / Math.pow( r, 3 );
         dvy += mult * ( vi.y - vj.y ) / Math.pow( r, 3 );
         dvz += mult * ( vi.z - vj.z ) / Math.pow( r, 3 );

      }

    }

    dv.push( new THREE.Vector3( dvx * dt, dvy * dt, dvz * dt ) );

  }

  for ( var i=0 ; i < geometry.vertices.length ; i++ ) {

    geometry.vertices[i].add( dx[i] );
    geometry.vertices[i].vel.add( dv[i] );

  }

    geometry.verticesNeedUpdate = true;
    geometry.colorsNeedUpdate = true;

    orbit.update();
  }

  // calls the init function when the window is done loading.
  window.onload = init;

  function mrand() {
    return Math.random();
  }

  function intRand(min, max) {
    return Math.random() * (max - min) + min;
  }

</script>

<body>
</body>


</html>

The modification to the denominator of the forces helps to keep energy relatively constant during close encounters of particles as per http://www.scholarpedia.org/article/N-body_simulations_(gravitational)

Upvotes: 0

Scott Stensland
Scott Stensland

Reputation: 28335

When you examine your browser javascript console (F12) you will see this error :

Uncaught SecurityError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The cross-origin image at http://mendow.github.io/projects/n-body/assets/textures/ps_smoke.png may not be loaded.

One solution (see alternative solution below) is to simply put your asset files on same host as your HTML. That is local to your host computer. Here are the steps (linux cmds, amend for windows) :

cd into same dir as your html
mkdir -p assets/textures # create dir to park your ps_smoke.png
cd assets/textures # get into this new dir

# copy that remote file to your local dir
wget http://mendow.github.io/projects/n-body/assets/textures/ps_smoke.png

and then finally update your html

comment out :

psMat.map = THREE.ImageUtils.loadTexture("http://mendow.github.io/projects/n-body/assets/textures/ps_smoke.png");

good new location :

psMat.map = THREE.ImageUtils.loadTexture("assets/textures/ps_smoke.png");

Once I did this your code executes just fine.

Alternative to above solution is to just override this security check by adding following code just prior to the loadTexture call you are making :

THREE.ImageUtils.crossOrigin = '';

Upvotes: 1

Related Questions