Three.jsのジオメトリ(Geometry)の総まとめ【前編】

137views

さて、ジオメトリとマテリアルの基本、lil-guiの使い方、ライトの基本的な使い方を説明してきました。

最初の記事でThree.jsにはさまざまな形のジオメトリが用意されていると言いました。基本的なジオメトリから発展的なジオメトリまで用意されています。

またThree.jsの基本的な使い方を理解するとBlenderなどで作った3Dオブジェクトを読み込むことが多くなると思います。ですが、その3Dオブジェクトの構築もここで説明するジオメトリの継承されています。

今回はThree.jsのジオメトリを全て(たぶん)まとめました。

初心者の方はもちろん、普段Three.jsを使っている方も見返した苦くようにまとめます。

前回までの記事

ジオメトリチートシート

今回紹介するジオメトリを一覧で見ることができるチートシートを作成しました!

ジオメトリチートシートはこちら

全てのジオメトリの中核

これからさまざまなジオメトリを紹介しますがその全てのジオメトリは”BufferGeometry”というジオメトリクラスの継承先になっています。

BufferGeometryは、xyzの頂点を3個決めて三角形を作っていくジオメトリです。BufferGeometryで作れる最小のメッシュを見てみます。

...

const geometry = new THREE.BufferGeometry()

const vertices = new Float32Array( [
  -1.0, -1.0,  1.0,
   1.0, -1.0,  1.0,
   1.0,  1.0,  1.0
] )

geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) )
const material = new THREE.MeshBasicMaterial( { wireframe: true, color: 0xffffff } )

const mesh = new THREE.Mesh( geometry, material )

... 

“…”の部分にはレンダラーやシーン、アニメーションフレームの追加します。

Float32Arrayクラスに引数に配列を渡します。この渡した配列はx→y→z→x→y→z→と対応していきます。つまり今回の配列は、点(-1  -1, 1) 点(1, -1, 1) 点(1, 1, 1)の3点を結ぶ三角形になります。

BufferGeometryクラスのsetAttributeメソッドに第1引数に’position’を渡し、第2引数にBuffer属性のインスタンスを渡します。これでジオメトリの設定が完了です。

BufferGeometryのサンプル

BufferGeometryの最小三角形のサンプルを作りました。一つの頂点をlil-guiで操作できるようにしてみました。

Three.jsはこのBufferGeometryを複雑に組み合わせることで全ての3Dオブジェクトを作り出すことができます。

ただこれをそのまま使って点を記述するのは難しいので、BufferGeometryを継承したクラスが用意されています。

それが今回の本題であるジオメトリまとめです。

BoxGeometryとは

BoxGeometryは今までのThree.jsの記事でも使っているとてもわかりやすいジオメトリです。

BoxGeometryは6つの引数を渡すことができます。

初期値が設定されている引数は省略することも可能です。

引数
  • width —— ボックスの横幅【初期値:1】
  • height —— ボックスの縦幅【初期値:1】
  • depth —— ボックスの奥行き【初期値:1】
  • widthSegments —— 横幅を区切る数(セグメント数)【初期値:1】
  • heightSegments —— 縦幅を区切る数(セグメント数)【初期値:1】
  • depthSegments —— 奥行きを区切る数(セグメント数)【初期値:1】

サンプルです。

boxGeometryは引数の内容も直感的でわかりやすいジオメトリですね。

ちなみにボックスの1つの面を見ると斜めにワイヤーフレームのラインが入っているのがわかります。

ボックスの1面の四角形は合同な直角三角形を1つ使って四角形を作っていることからこのようなラインが入るわけですね。先ほどのBufferGeometryを理解していると理由がわかりやすいと思います。

PlaneGeometryとは

このジオメトリも以前の記事で紹介しました。

PlaneGeometryはBoxGeometryよりの奥行きがないだけのジオメトリです。

引数
  • width —— 面の横幅【初期値:1】
  • height —— 面の縦幅【初期値:1】
  • widthSegments —— 横幅を区切る数(セグメント数)【初期値:1】
  • heightSegments —— 縦幅を区切る数(セグメント数)【初期値:1】

先ほどのBoxGeometryのサンプルのdepthを0にすることでほぼ同じような形を作れますので、サンプルは省略します。

正確にはPlaneGeometryはBoxGeometryに比べて頂点数が1/6になります。横の面と後ろの面が存在しないからですね。

SphereGeometryとは

次は球体のジオメトリであるSphereGeometryです。

引数
  • radius —— 球の半径【初期値:1】
  • widthSegments —— 水平方向のセグメント数【初期値:32】
  • heightSegments —— 垂直方向のセグメント数【初期値:16】
  • phiStart —— 水平セグメントラインの開始位置【初期値:0】
  • phiLength —— 水平方向の円周の角度【初期値:Math.PI*2(2π)】
  • thetaStart —— 垂直セグメントラインの開始位置【初期値:0】
  • thetaLength —— 垂直方向の円周の角度【初期値:Math.PI(π)】

セグメント数を上げるほど自然な球体に近づいていきます。

サンプルで確認してみましょう。

サンプルではwidthとheightのセグメントを1まで下げることができますが、実際に下げてみると途中から形が変わらないことに気づくかもしれません。

それぞれ最小値が決まっていて、widthSegmentsは3、heightSegmentsは2になっています。

phiLengthは1周(2π)で球になるのに対して、thetaLengthは半周(π)で球体になることも動かしてみるとわかります。

基本的にはradiusだけを指定して使うことが多いのかなと思っています。

CylinderGeometryとは

CylinderGeometryは円柱のジオメトリです。今までのジオメトリに比べて引数によって様々な形を表現することができるジオメトリだと思います。

引数
  • radiusTop —— 円柱の上面の半径【初期値:1】
  • radiusBottom —— 円柱の下面の半径【初期値:1】
  • height —— 円柱の高さ【初期値:1】
  • raidusSegments —— 上下面のセグメント数【初期値:8】
  • heightSegments —— 高さ方向のセグメント数【初期値:1】
  • openEnded —— 上下面を開けるかどうか(trueでopen)【初期値:false】
  • thetaStart —— 円周セグメントラインの開始位置【初期値:0】
  • thetaLength —— 円周の角度【初期値:Math.PI * 2(2π)】

こちらもraduisセグメント数を上げるほど綺麗な円柱に近づいていきます。

サンプルで確認してみましょう。

上下面の半径やセグメントを操作することで円柱だけでなく円錐や角柱・角錐のような形を作ることもできます。

TorusGeometryとは

TorusGeometryは円環体のジオメトリです。

円環体ということばで形状がピンとこないかもしれません。ドーナツ状の形を作れるジオメトリです。

引数
  • radius —— 円環体の中心から管までの半径【初期値:1】
  • tube —— 管状部分の半径【初期値:0.4】
  • radialSegments —— 管状のセグメント数【初期値:8】
  • tubularSegments —— 円周のセグメント数【初期値:6】
  • arc —— 中心角度【初期値:Math.PI*2(2π)】

サンプルです。

TorusKnotGeometryとは

TorusKnotGeometryは管状のチューブをコイルのように巻き付けるジオメトリです。

これはサンプルをいじくり回して感覚を掴む方がわかりやすいと思います。

まずは引数を確認していきます。

引数
  • radius —— 円環体の半径【初期値:1】
  • tube —— 管状チューブの半径【初期値:0.4】
  • tubularSegments —— 管状チューブのセグメント数【初期値:64】
  • radialSegments —— 管状チューブチューブ円周のセグメント数【初期値:8】
  • p —— 回転対称軸に巻き付く回数【初期値:2】
  • q —— 円環体のチューブ周りに巻き付く回数【初期値:3】

pとqは互いに素の状態(1,-1以外に公約数を持たない状態)であるときに正常に機能します。pとqが同じ値の時は少し捻られた円環体になります。

それ以外(p!=qでpとqが互いに素でない時)はそれぞれ最大公約数で割った状態と同様の形になります。例えば、p=4、q=6の時はp=2、q=3と同様の形になります。

サンプルで試すとわかりますが、形は同様でもセグメント数は異なります。

RingGeometryとは

RingGeometryは単純なリングのジオメトリです。

特徴は見ればわかると思うので引数の説明からしていきます。

引数
  • innerRadius —— 内半径【初期値:.5】
  • outerRadius —— 外半径【初期値:1】
  • thetaSegments —— 円周のセグメント数【初期値:8】
  • phiSegments —— 中心から弧までのセグメント数【初期値:1】
  • thetaStart —— 円周セグメントの開始位置【初期値:0】
  • thetaLength —— 円周の長さ【初期値:Math.PI*2(2π)】

サンプルで確認してみます。

単純なリングを作るジオメトリですね。

形を設定するジオメトリ

最初にBufferGeometryを紹介しました。BufferGeometryはverticesという変数に頂点配列を作成しました。

上で紹介したジオメトリは引数に数値を渡すだけのジオメトリクラスでした。BufferGeometryの継承クラスというだけあって使いやすいクラスになっています。

他に直属の継承クラスが4つあります。この4つは一気に紹介しようと思います。

  • LatheGeometry
  • PolyhedronGeometry
  • ShapeGeometry
  • ExtrudeGeometry

この4種類をまとめた理由は、BufferGeometryのように頂点や形を独自に決めるジオメトリクラスであるからです。

まとめたもののそれぞれ使い所や使い道に違いがありますので紹介します。

LatheGeometry

これは任意に決めた頂点を結んだ直線をy軸周りで回転させて立体を作るジオメトリです。

何を言ってるかわからないので、まずは今まで通り引数とサンプルを見ていきます。

引数
  • points —— 頂点配列
  • segments —— y軸回転方向のセグメント数【【初期値:1】
  • phiStart —— y軸回転の開始位置【初期値:8】
  • phiLength —— y軸回転方向の円周の長さ【初期値:Math.PI*2】

第1引数は必須で頂点配列を設定します。そのほかは今までと同じようなジオメトリの設定です。

サンプルです。

サンプルを見てください。一次関数と二次関数とsin関数というボタンを用意しました。

segmentsを1に合わせてボタンを押すと関数の形を見ることができます。ボタンを押すと第1引数の頂点情報を更新するようにしています。

頂点points引数配列は次のように設定しています。

/**
 *  一次関数
 */
const linearPoints = []
for (let i = 0; i < 10; i++ )
{
  linearPoints.push( new THREE.Vector2( i, i )
}

/**
 *  二次関数
 */
const quadraticPoints = []
for (let i = 0; i < 20; i++ )
{
  linearPoints.push( new THREE.Vector2( Math.pow(i,2)/15, i )
}

/**
 *  sin関数
 */
const sinPoints = []
for (let i = 0; i < 40; i++ )
{
  linearPoints.push( new THREE.Vector2( Math.sin(i * Math.PI/10)*6, i )
}


...

const geo = new THREE.LatheGeometry( linearPoints )

...

for文の中を見てください。new THREE.Vector2は(x, y)の頂点を設定できるクラスです。

一次関数頂点では(0, 0), (1, 1), (2, 2), …のような頂点をlinearPointsという配列に追加していっています。

for文の繰り返す回数によって距離が長くなることに加えてセグメント数が増えていきます。

このように第1引数のpoints配列をfor文で関数を設定して、第2引数のsegmentsの数だけ立体にしていることが理解できます。

PolyhedronGeometry

これはBufferGeometryのように完全に頂点を一つ一つ追加するジオメトリです。

polyhedronとは「多面体」という意味の単語で、実はこれも後で継承するためジオメトリだと思って問題ないと思います。

実際に手作業でポイントを打つためのジオメトリではないと(私は)認識しています。

後に継承元として紹介するので、ここでは引数だけ紹介します。

引数
  • vertices —— 頂点群の配列
  • indices ——配列形式で頂点を構成する面情報
  • radius —— 最終的に作られるオブジェクトの半径
  • detail —— ジオメトリを分割する数。大きくなるほどなめらかな形状になります。

ShapeGeometry

このジオメトリはSVGのように点から直線や曲線を引いて形状を作るジオメトリです。THREE.Shapeクラスとそのメソッドを使って形状を描いていきます。

引数はこのShapeのインスタンスとセグメント数の設定のみです。

引数
  • shapes —— Shapeのインスタンス。配列で複数指定も可
  • curveSegments —— shapeにカーブがある時のセグメント数【初期値:12】

サンプルで砂時計のような形状のShapeを作ってみました。

この砂時計のShapeのコードはlineToメソッドのみを使ったとてもシンプルなものにしました。

const shape = new THREE.Shape()

shape.moveTo(   0,  0 )
shape.lineTo(  .3,  0 )
shape.lineTo(   2,  3 )
shape.lineTo( - 2,  3 )
shape.lineTo( -.3,  0 )
shape.lineTo( - 2, -3 )
shape.lineTo(   2, -3 )
shape.lineTo(  .3,  0 )

const geometry = new THREE.ShapeGeometry(shape)

カーブを作っていないのでSegmentsを動かしても変化しません。

このジオメトリは手書きで使うよりも、今後紹介するSVGLoaderというSVGデータをThree.jsに落とし込むロードクラスで使うことがメインになると思います。

なのでShapeクラスについてはある程度の使い方を理解していれば問題ないと思います。

ExtrudeGeometry

これはShapeGeometryを立体的にできるジオメトリです。第1引数はShapeGeometryと同様のShapeクラスで作成した形状を渡します。

第2引数は、立体的にするための情報をJson形式で指定します。

引数
  • shapes —— Shapeのインスタンス。配列で複数指定も可
  • Options —— 立体状態の設定
    • curveSegments — カーブ部分のセグメント数【初期値:12】
    • steps — Shapeの立体部分のセグメント数【初期値:1】
    • depth — Shapeの立体部分の厚さ【初期値:1】
    • bevelEnabled — 斜角を生成するか【初期値:true】
    • bevelThickness — 斜角の立体的な大きさ【初期値:0.2】
    • bevelSize — 斜角の平面的な大きさ【初期値:bevelThickness-0.1】
    • bevelOffset — Shapeのアウトラインから斜角が始まる部分までの距離【初期値:0】
    • bevelSegments — 斜角部分のセグメント数【初期値:3】
    • extrudePath — Shapeを押し出すパス

サンプルで先ほどの砂時計のような形状を立体的にしてみました。

このクラスもSVGを立体的にするために使うことが多いジオメトリです。

一旦まとめ

さてここまで11種類のジオメトリクラスを紹介しました。まだ紹介すべきジオメトリがあるのですが、記事が重くなってしまうので後編に分けようと思います。

ここまでだけでもジオメトリの理解がとても深まるのではないでしょうか、?

後編は残りのBufferGeometryの継承クラスをさらに継承したジオメトリと、応用的な使い方をするジオメトリを紹介しようと思います。

カテゴリー