Three.jsでパーティクルを作ろうと思った際に調べたこと
Three.jsでパーティクルをつくるにはどうすればいいのか調べてみました。
THREE.Spriteでパーティクル
THREE.SpriteオブジェクトはTHREE.Object3Dオブジェクトを継承しており、THREE.Meshと同じように動作します。
Spriteは、何もプロパティをあたえないと、常に面をカメラの方に向けている2Dの平面です。
let material = new THREE.SpriteMaterial(); let sprite = new THREE.Sprite(material); scene.add(sprite);
See the Pen YQNZqg by nogson (@satofaction) on CodePen.
THREE.Spriteに渡せるマテリアルは、THREE.SpriteMaterialか、THREE.SpriteCanvasMaterialのいずれかです。
パーティクルを作成する際に、Spriteを大量に作成してもそれっぽいのができそう。 ただし、オブジェクト大量に作成するとパフォーマンス的によくありません。
See the Pen LLxWBv by nogson (@satofaction) on CodePen.
THREE.Points
THREE.Pointsは大量の頂点データを管理するのに便利です。 頂点座標と塗のデータをジオメトリに追加します。 パーティクルのサイズはマテリアルのプロパティで調整します。 頂点カラーを変更するためにはvertexColors
とtrueに設定します。
let geometry = new THREE.Geometry(); let material = new THREE.PointsMaterial({ //pointsを使用する場合はMaterialのサイズで調整する size:0.1, vertexColors:true }); let range = 1000; for (let i = 0; i < range; i++) { let pos = getPostion( Math.random() * width, Math.random() * height, Math.random() * width ); //頂点データを生成 let particle = new THREE.Vector3(pos.x,pos.y,pos.z); //頂点色を設定 let color = new THREE.Color(0xffff); //頂点データをジオメトリに追加 geometry.vertices.push(particle); //頂点色をジオメトリに追加 geometry.colors.push(color); } point = new THREE.Points(geometry,material); scene.add(point);
See the Pen owBZKv by nogson (@satofaction) on CodePen.
THREE.PointsMaterialのプロパティ
プロパティ | 説明 |
---|---|
color | Points内のすべてのパーティクルの色を一括指定できる。vertexColorsプロパティをtrueにして、ジオメトリに頂点カラーを設定した場合は、ジオメトリの頂点カラーが有効になる。| |map|マップを使うとパーティクルにテクスチャを設定できる |
size | パーティクルのサイズ |
sizeAttenuation | falseを設定すると、カメラからの距離にかかわらずパーティクルが同じ大きさになる |
vertexColors | パーティクルの頂点カラーの設定を有効にする |
opacity | 透明度を設定 |
transparent | trueを設定すると、opacityプロパティが有効になる |
blendind | パーティクルのブレンドモードを設定 |
fog | パーティクルがシーンのfogの影響を受けるかを設定。デフォルトはtrue |
depthTes | パーティクルがシーンのfogの影響を受けるかを設定。デフォルトはtrue |
パーティクルにテクスチャを貼ってみる
画像を読み込んでmapプロパティに下のテクスチャを貼ってみます。
loader = new THREE.TextureLoader(); loader.load('images/img.png', function (texture) { createPoints(texture); render(); }); function createPoints(texture) { let geometry = new THREE.Geometry(); let material = new THREE.PointsMaterial({ //pointsを使用する場合はMaterialのサイズで調整する size: 0.2, color: 0xffffff, vertexColors: true, map: texture, transparent: true, }); let maxLength = 2000; for (let i = 0; i < maxLength; i++) { let pos = getPostion( Math.random() * width, Math.random() * height, Math.random() * width ); //頂点データを生成 let particle = new THREE.Vector3(pos.x, pos.y, pos.z); //頂点色を設定 let color = new THREE.Color(0xffffff); //頂点データをジオメトリに追加 geometry.vertices.push(particle); //頂点色をジオメトリに追加 geometry.colors.push(color); } point = new THREE.Points(geometry, material); scene.add(point); }
こんな感じになりました。
しかしいくつか問題がありました。 頂点の描画順で重なり順が決まる為、パーティクルが重なった場合に深度テストが影響して、画像の透明部分がうまく描画できませんでした。
震度テストを無効(depthTest: false )にしましたが、今度はサイズの小さいパーティクルがサイズの大きなオブジェクトより前に出てしまいました。
かと言って、z座標をいじるとそもそもの動きがおかしくなってしまいました。
まとめ
1万頂点ぐらいまでなら、PCが唸らずに描画できました。 どうも10万頂点以上いくと、パーフォーマンスなどに影響があるので気をつけたほうが良いようです。
実際今回つくったサンプルはこちらです。