[Three.js] BufferGeometry vs Geometry

:::note alert
この記事は非推奨です。Three.jsのコアからGeometryクラスは完全に削除されているため、この記事を読む必要はありません。
:::

## はじめに

WebGLライブラリ[Three.js](https://threejs.org/)には、[Geometry](https://threejs.org/docs/index.html#api/en/core/Geometry)と[BufferGeometry](https://threejs.org/docs/index.html#api/en/core/BufferGeometry)の2種類の形状を表す形式がありました。現バージョンではGeometryは削除されています。

この記事では

-   2つの形式の関係
-   2つの形式の操作の違い
-   パフォーマンス

の3点について解説します。

### 対象とする読者

この記事はThree.jsをすでに利用しているユーザーを想定しています。
そのためThree.jsのインストールガイドなどの内容は取り扱いません。

### 環境

この記事はThree.js `r125`を前提にしています。
バージョンが変わった場合、この記事の内容は適用できない場合があります。
記事を読む前にお手元の環境の確認をお願いします。

## 先に結論だけ

- `r144`で`**BufferGeometry`クラスがThree.jsのコアから削除された。互換性維持のため`**BufferGeometry`クラスは`**Geometry`クラスのエイリアスとして機能していたが、完全に削除されたためインスタンス化するとエラーになる。[Release Note](https://github.com/mrdoob/three.js/releases/tag/r144) 
-  `r125`でGeometryクラスはThree.jsのコアから削除された。[Release Note](https://github.com/mrdoob/three.js/releases/tag/r125)
-   現状のThree.jsではBufferGeometryクラスが形状を表す唯一の基本クラスである。Geometryクラスは完全に削除された。
-   BufferGeometryとGeometryクラスでは、頂点データなどにアクセスする方法が違う。
-   2つのクラスをインスタンス化したり、頂点データを操作した場合大きなパフォーマンスの差が出る。
-   データの操作なしに同一の形状を再描画するだけなら、パフォーマンスの差は少ない。

## 2つの形式の関係

Three.jsでは2つの形式を

-   BufferGeometry : 形状を表す基本的なクラス
-   Geometry : ユーザーフレンドリーなBufferGeometryの代替手段

と位置付けていました。

> BufferGeometry
> An efficient representation of mesh, line, or point geometry.

[BufferGeometry 公式ドキュメント](https://threejs.org/docs/index.html#api/en/core/BufferGeometry)

> Geometry
> Geometry is a user-friendly alternative to BufferGeometry.

[Geometry 公式ドキュメント](https://threejs.org/docs/index.html#api/en/core/Geometry)[削除済み]

### ソースコードの依存関係

2つの形式は、ソースコード上ではどちらも依存関係がありません。
よく似た関数が用意されていますが、それぞれ独自に実装されています。

-   [BufferGeometryのソースコード](https://github.com/mrdoob/three.js/blob/master/src/core/BufferGeometry.js)
-   [Geometryのソースコード](https://github.com/mrdoob/three.js/blob/master/src/core/Geometry.js)

2つのクラスはそれぞれ[EventDispatcher](https://threejs.org/docs/#api/en/core/EventDispatcher)を継承しています。

#### 相互変換

2つの形式は、[BufferGeometry.fromGeometry関数](https://threejs.org/docs/index.html#api/en/core/BufferGeometry.fromGeometry)および[Geometry.fromBufferGeometry関数](https://threejs.org/docs/index.html#api/en/core/Geometry.fromBufferGeometry)で相互変換が可能です。

#### 〇〇BufferGeometryと〇〇Geometryの関係

Three.jsには基本的な形状を簡単に生成できるよう、プリミティブな形状を表すGeometryクラスが用意されています。立方体を表すBoxGeometryや平面を表すPlaneGeometry、球体を表すSphereGeometryなどがこれにあたります。
これらの〇〇Geometryクラスには、それぞれ対応する〇〇BufferGeometryクラスも存在します。

〇〇Geometryクラスは頂点データの生成を自身で行わず、コンストラクター内で〇〇BufferGeometryを生成し、それをGeometry.fromBufferGeometry関数で変換して利用しています。
[BoxGeometryの該当箇所](https://github.com/mrdoob/three.js/blob/master/src/geometries/BoxGeometry.js#L28)はこちらです。

BufferGeometryとGeometryにはソースコードの依存関係はありませんが、〇〇BufferGeometryと〇〇Geometryの間には依存関係があります。

### ユーティリティクラス

2つの形式は、それぞれユーティリティクラスをもっています。このユーティリティクラスは、複数の頂点データのマージなどに威力を発揮します。

-   [BufferGeometryUtils](https://github.com/mrdoob/three.js/blob/master/examples/jsm/utils/BufferGeometryUtils.js)
-   [GeometryUtils](https://github.com/mrdoob/three.js/blob/master/examples/jsm/utils/GeometryUtils.js)

よく似た関数がそれぞれ用意されていますので、操作感はかわりません。

## 2つの形式の操作の違い

2つの形式では、頂点データへのアクセス方法が異なります。

-   BufferGeometry : [BufferGeometry.attributesオブジェクト](https://threejs.org/docs/index.html#api/en/core/BufferGeometry.attributes)を操作する
-   Geometry : [Geometry.vertices配列](https://threejs.org/docs/index.html#api/en/core/Geometry.vertices)を操作する

という違いがあります。

```js
//Geometry
geometry.vertices[i].setX(posX);
//BufferGeometry
geometry_B.attributes.position.setX(i, posX);
```

Geometry形式の方が、頂点データの編集を短く記述できます。

BufferGeometryで扱うattributesオブジェクトとは、WebGLでの頂点データ形式です。
参考記事 : [シェーダの記述と基礎](https://wgld.org/d/webgl/w008.html)
WebGLのシェーダーに直接渡せる形式なので、Geometryよりもパフォーマンスが向上します。

2つの形式の頂点操作に関するより詳しい内容は、こちらの記事をご参照ください。
[three.jsのGeometryからBufferGeometryへの書き換えで躓いたところ](https://qiita.com/izumi_ikezaki/items/9c43901c9b8f67423f49)

## パフォーマンス

2つの形式のもっとも大きな違いはパフォーマンスです。パフォーマンスの差を確認するために[デモページ](https://masatomakino.github.io/threejs-lab/demo/StudyBufferGeometry.html)を作成しました。

:::note alert
追記 : 2022/10/23
`r144`以降、Geometryクラスをインスタンス化する方法がなくなりました。`isBufferGeometry`チェックボックスのON / OFFに関わらず、完全に同じ球体が生成されてしまうため描画パフォーマンスも同一です。
:::

![](https://www.evernote.com/l/AAm16rutet5MNbifmqNzhLBj9HRPOAXxJMkB/image.png)
<https://masatomakino.github.io/threejs-lab/demo/StudyBufferGeometry.html>

このデモページでは、画面中央に縦横256分割された球体が表示されています。

-   `updateGeometry`チェックボックスがONなら、毎フレーム球体のジオメトリを破棄して再生成する。
-   `isBufferGeometry`チェックボックスがONならBufferGeometryを、OFFならGeometryを生成する。

という条件で再描画を行なっています。

動作環境によって結果は異なりますが、以下の結果がデモページから読み取れます。

-   毎フレーム破棄と再生成を繰り返した場合、BufferGeometryの方がGeometryより10倍程度高速である。
-   破棄と再生成を行わず描画するだけの場合、数万ポリゴン程度のデータでは2つの形式の差は出ない。
    -   (より巨大で複雑なデータを使用すれば、描画だけでもパフォーマンスの差が出る可能性はあります。)

2つの形式でパフォーマンスの差が大きく出るのは生成と頂点データの書き換え時でした。頂点データにアクセスせず同一の形状を再描画しつづけるならばあまり大きな差は出ません。

以上、ありがとうございました。