トップへ(mam-mam.net/)

MarchingCubes.jsでメタボールの表現 ~Webサイトで3Dコンテンツ(Three.js)デザイン

検索:

MarchingCubes.jsでメタボールを表現(Three.js[r145]を使用)

Three.jsでマーチンキューブ(MarchingCubes.js)を使うとポリゴンによるメタボールの表現が可能です。
<script src="./three_r145/three.min.js"></script>
<script src="./three_r145/OrbitControls.js"></script>
<script src="./three_r145/MarchingCubes.js"></script>
のようにしてマーチンキューブが使えるようにします。

マウス左ドラッグやパンで視点回転します。


<canvas id="can" style="width:1000px; height:600px; max-width:calc(100vw - 36px);max-height:calc((100vw - 36px) * 60 / 100);margin:0;padding:0;cursor:grab;" width="1000" height="600"></canvas>

<script src="./three_r145/three.min.js"></script>
<script src="./three_r145/OrbitControls.js"></script>
<script src="./three_r145/MarchingCubes.js"></script>

<script>
  var balls=[];
  var scene,camera;
  var SphereMesh;
  var main = function () {
    //■シーンを作成
    scene = new THREE.Scene();

    //描画先canvasを取得
    let can=document.getElementById('can');
    //canvasの高さ,幅を取得
    let w = parseInt(can.style.width);
    let h = parseInt(can.style.height);

    //■カメラを作って設定
    //THREE.PerspectiveCamera( 画角(度), アスペクト比, カメラに写る最短距離, カメラに写る最長距離 );
    camera = new THREE.PerspectiveCamera( 50, w/h, 1, 10000 );
    camera.position.set( 0, 0, 2000 );//カメラの位置を設定
    camera.rotation.set(0,0,0);     //カメラの回転角を設定
    //camera.lookAt(new THREE.Vector3(0, 0, 0));
    scene.add( camera );

    //■レンダラーをDOM(canvas)上に設置する
    renderer = new THREE.WebGLRenderer({canvas:can , antialias: true});//アンチエイリアスをtureにする
    renderer.setSize( w, h );

    //■光源------------------------------------------------
    //環境光(すべての物体にすべての方向から与える光)光の当たらない部分でも真っ黒にならない
    var AmbientLight=new THREE.AmbientLight(0xffffff,0.4);
    scene.add( AmbientLight );
    //スポットライト            色,   強さ,距離,角度(angle),半影[0-1],減衰[1,1]
    const spotLight=new THREE.SpotLight(0xffffff, 2,3000,Math.PI/4, 0.8, 1);
    //(x→, y↑, z手前)
    spotLight.position.set(100,1000,1500);
    spotLight.target.position.set(0,0,0);
    scene.add(spotLight);
    scene.add(spotLight.target);

    //■テクスチャマッピング用画像のロードと設定
    //球体に貼り付けるテクスチャ画像のロード
    let Texture360=new THREE.TextureLoader().load('./three_r145/img/360.jpg');
    Texture360.wrapS=THREE.RepeatWrapping;
    Texture360.wrapT=THREE.RepeatWrapping;
    //テクスチャの繰り返し回数を指定する
    Texture360.repeat.set(1,1);
    //球体用の基本マテリアルを作成する
    let SphereMaterial=new THREE.MeshBasicMaterial({
      color:0xFFFFFF,
      side:THREE.BackSide,//両面を表示する設定
      map: Texture360
    });
    //球体ジオメトリの作成(半径、横分割数,高さ方向分割数)
    let SphereGeometry=new THREE.SphereGeometry(1,32,24);
    //球体のメッシュの作成
    SphereMesh=new THREE.Mesh(SphereGeometry,SphereMaterial);
    SphereMesh.scale.set(5000,5000,5000);
    scene.add(SphereMesh);

    // キューブマップ
    //■反射マッピング用のターゲット作成
    cubeRenderTargetReflection = new THREE.WebGLCubeRenderTarget(512,{
        generateMipmaps: true,
        minFilter: THREE.LinearMipmapLinearFilter    });
    cubeRenderTargetReflection.texture.mapping = THREE.CubeReflectionMapping;//反射マッピングに設定
    //■反射マッピング用キューブカメラの作成
    //(カメラに写る最短距離, カメラに写る最長距離 ,ターゲット)
    cubeCameraReflection = new THREE.CubeCamera( 0.1, 100000, cubeRenderTargetReflection);
    //■反射材質の作成
    let materialReflection=new THREE.MeshLambertMaterial({
      color:0xffffff,
      envMap:cubeCameraReflection.renderTarget.texture,
      reflectivity:0.8,    //反射率
    });

    //■MarchingCubesインスタンスの作成
    //(resolution:値を大きくするほど滑らかな描画20~100, material, enableUvs, enableColors, maxPolyCount)
    effect = new THREE.MarchingCubes( 64, materialReflection, true, true ,100000);
    effect.position.set( 0, 0, 0 );
    effect.scale.set( 1000, 1000, 1000 );
    //effect.isolation=80; //分離???
    scene.add(effect);

    //メタボールの準備
    for(let i=0;i<20;i++){
      balls[i]=[];
      balls[i].x=Math.random()*0.8;
      balls[i].y=Math.random()*0.8;
      balls[i].z=Math.random()*0.1+0.5;
      balls[i].vx=Math.random()*0.01-0.005;
      balls[i].vy=Math.random()*0.01-0.005;
    }

    //マウスの左右ドラッグ+中ボタンの上下ホイールで動かす
    const controls = new THREE.OrbitControls(camera, renderer.domElement);  
    controls.target.set(0,0,0);
    controls.enablePan=false; //パンできなくする
    controls.enableZoom=false;//ズームできなくする
    controls.update();
    renderLoop();
  };

  function renderLoop () {
    setTimeout(renderLoop,33);
    effect.reset();//リセット
    for(let i=0;i<balls.length;i++){
      if((balls[i].x+balls[i].vx)>0.8){balls[i].vx=-balls[i].vx;}
      if((balls[i].x+balls[i].vx)<0.0){balls[i].vx=-balls[i].vx;}
      if((balls[i].y+balls[i].vy)>0.8){balls[i].vy=-balls[i].vy;}
      if((balls[i].y+balls[i].vy)<0.0){balls[i].vy=-balls[i].vy;}
      balls[i].x+=balls[i].vx;
      balls[i].y+=balls[i].vy;
      //        x,y,z(0~1),連結強さ,減衰, 色
      //addBall(x,y,z,strength,subtract,colors)
      //半径=sqrt(連結強さ÷減衰) となるらしい
      effect.addBall(balls[i].x+0.1, balls[i].y+0.1, balls[i].z, 1.6, 100);
    }
    effect.update();
    SphereMesh.visible=true;
    effect.visible=false;
    cubeCameraReflection.update(renderer,scene);
    effect.visible=true;
    SphereMesh.visible=false;

    renderer.render( scene, camera );
  }

  window.addEventListener( 'DOMContentLoaded', main, false );
</script>