three.js n体模拟

three.js n-body simulation

我正在尝试这样做 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 我想我在计算每个部分对每个部分的吸引力时做错了

问题是粒子有一个质心并围绕它旋转而不是质心改变它的位置

<!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>

当您检查浏览器 javascript 控制台 (F12) 时,您将看到此错误:

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.

一种解决方案(请参阅下面的替代解决方案)是简单地将资产文件放在与 HTML 相同的主机上。那是您的主机本地的。以下是步骤(linux cmds,修改为 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

然后最后更新你的 html

注释掉:

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

不错的新位置:

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

一旦我这样做了,你的代码就可以正常执行了。

上述解决方案的替代方法是通过在您正在进行的 loadTexture 调用之前添加以下代码来覆盖此安全检查:

THREE.ImageUtils.crossOrigin = '';

Matvey,您需要用旧值计算所有粒子位置和速度的变化,然后再将它们相加以获得新值。否则,您将根据不准确的改变位置和速度计算一些变化。

我已经编辑了你的渲染循环:

<!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>

根据http://www.scholarpedia.org/article/N-body_simulations_(gravitational)

,对力的分母的修改有助于在粒子近距离接触期间保持能量相对恒定