three.jsでカスタム図形を作る(3):曲がるパイプを作る

ボーンの作成

ボーンに沿って曲がるようなチューブ状のメッシュを作成する。

ボーンとなるベジエ曲線を作成し、セクション数ごとに分割した地点の座標を取得する。

//start、end、controlポイントの座標作成
const center2D = new THREE.Vector2();  
const ep = new THREE.Vector2( 10,20 );
const cp = new THREE.Vector2( 10,0 );
//ベジエ曲線を作成
const curve = new THREE.QuadraticBezierCurve(center2D, cp, ep);
//セクション数に分割した座標の配列を取得
const sect = 4;
const bone = curve.getPoints(sect);
bone配列を作成

※sect数4でgetPointsするとbone配列のサイズは5になる。

分割したボーン間の角度を求める

let zpos = bone[0];
for(let i=0; i<(sect+1); i++){
    //zpos(bone[n-1])とbone[n]の角度を求める
    const diff = new THREE.Vector2().subVectors(bone[i], zpos);
    const angle = Math.atan2(diff.y, diff.x);
}

ボーンに沿った円の座標を取得

const edge = 12;
const size = 5;
const pt = [];
for(let i=0; i<(sect+1); i++){
    //上記angleを求める式(省略)
    pt[i] = [];
    for(let j=0; j<edge; j++){
        const theta = j * 2 * Math.PI / edge;
        const w = size * Math.cos(theta);  //奥行(z軸上の幅)
        const h = size * Math.sin(theta);  //縦
        const v = new THREE.Vector2(0, h); //回転させるためベクトルに変換
        v.add(bone[i]);                    //boneを中心に移動
        v.rotateAround(bone[i], angle);    //angle分回転
        pt[i][j] = [v.x, v.y, w];
    }
}

z-y平面上に半径=sizeの円を描き、z軸で回転させているイメージ。

この時、boneの座標を加算して、各円の回転の中心をboneに持ってきている。

メッシュの作成

前回の記事と同じく、BufferGeometryを使ってジオメトリを作成する。

以下は関数化してまとめたコードの全体。

//ページの読み込みを待つ
window.addEventListener('load', init);
function init() {

    ///////////////////////////////////////////////
    //           画面設定                   //
    //////////////////////////////////////////////

    //画面サイズを指定
    const width = window.innerWidth;
    const height = window.innerHeight;
    //レンダラーを作成
    const renderer = new THREE.WebGLRenderer({
        canvas: document.querySelector('#myCanvas')
    });
    renderer.setClearColor(new THREE.Color('grey'));
    document.body.appendChild( renderer.domElement );
    renderer.setSize(width, height);
    //シーンを作成
    const scene = new THREE.Scene();
    //カメラを作成
    const camera = new THREE.PerspectiveCamera(45, width / height);
    camera.position.set(-50, 50, 100);
    camera.lookAt(new THREE.Vector3(0, 0, 0));
    //ライトを設置
    const envlight = new THREE.AmbientLight(0xffffff, 1);
    scene.add(envlight);

    ///////////////////////////////////////////////
    //         マテリアル作成                  //
    ///////////////////////////////////////////////

  //パイプの設定
    const sect = 5;
    const edge = 12;
    const size = 5;
    const center2D = new THREE.Vector2();
    const ep = new THREE.Vector2( 10, 10 );
    const cp = new THREE.Vector2( 10, 0 );
    //パイプを作成
    const pt = makePipe(sect, edge, size, cp, ep);
  //メッシュの作成
    const geometry = makeGeometry(sect, edge, pt);
    const material = new THREE.MeshNormalMaterial({
        side:THREE.DoubleSide,
    });
    const plane = new THREE.Mesh( geometry, material );
    scene.add( plane );

    ///////////////////////////////////////////////
    //            共通関数                  //
    ///////////////////////////////////////////////

    //パイプ作成
    function makePipe(sect, edge, size, cp, ep){
        //make bone
        const curve = new THREE.QuadraticBezierCurve(center2D, cp, ep);
        const bone = curve.getPoints(sect);
        let zpos = bone[0];
        //set points
        const pt = [];
        for(let i=0; i<(sect+1); i++){
            //calc angle
            const diff = new THREE.Vector2().subVectors(bone[i], zpos);
            const angle = Math.atan2(diff.y, diff.x);
            //calc coords
            pt[i] = [];
            for(let j=0; j<edge; j++){
                const theta = j * 2 * Math.PI / edge;
                const w = size * Math.cos(theta);
                const h = size * Math.sin(theta);
                const v = new THREE.Vector2(0, h);
                v.add(bone[i]);
                v.rotateAround(bone[i], angle);
                pt[i][j] = [v.x, v.y, w];
            }
            zpos = bone[i];
        }
        return pt;
    }

  //カスタムvertexの作成
    function setVertices(sect, edge, pt){
        const vert = [];
        for(let i=0; i<sect; i++){
            vert[i] = [];
            for(let j=0; j<edge; j++){
                vert[i][j] = [];
                vert[i][j][0] = pt[i][j];
                vert[i][j][1] = pt[i][(j+1)%edge];
                vert[i][j][2] = pt[i+1][(j+1)%edge];
                vert[i][j][3] = pt[i+1][j];
            }
        }
        return new Float32Array(vert.flat(3));
    }

  //カスタムindexの作成
    function setIndices(sect, edge){
        const num_rect = sect * edge;
        const order = [0,3,2,2,1,0];
        const index = [];
        for(let i=0; i<num_rect; i++){
            for(let j=0; j<order.length; j++){
                index.push(order[j]+(4*i));
            }
        }
        return new Uint16Array(index);
    }

    //BufferGeometryの作成
    function makeGeometry(sect, edge, pt){
      const vertices = setVertices(sect, edge, pt);
      const indices = setIndices(sect, edge);
      const geometry = new THREE.BufferGeometry();
      geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
      geometry.setIndex(new THREE.BufferAttribute(indices, 1));
      geometry.computeVertexNormals();
      return geometry;
    }

    ///////////////////////////////////////////////
    //         レンダリング開始               //
    //////////////////////////////////////////////

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

「three.jsでカスタム図形を作る(3):曲がるパイプを作る」への1件のフィードバック

コメントは停止中です。