これは Babylon.js Advent Calendar 2023 の22日目の記事です。
この記事について
前回、前々回で、基本メッシュの頂点を編集、着色を行いました。
今回はそれらを使用して、クリスマスツリーを描画してみます。
原型のオブジェクトを用意する
編集用に、円柱を2つ作成します。
const leaf = BABYLON.MeshBuilder.CreateCylinder("leaf",
{height:6,diameterTop: 0,diameterBottom:2,subdivisions:100,updatable:true});
leaf.position.y = 4;
const trunk = BABYLON.MeshBuilder.CreateCylinder("trunk",
{height:2,diameterTop: 0.7,diameterBottom:0.8,subdivisions:100,updatable:true});
円柱を2つ用意しました。
1つは、diameterTop: 0 と上面のサイズを0、diameterBottom:2 で下面のサイズを3に指定し、円錐を作成。
もう一つは、上面を0.7,下面を0.8にし、少し末広がりの円柱にしています。この後頂点の編集をするので、updatable:true も忘れず指定しておきます。
randomで原型の木のオブジェクトをディティールアップ
それでは、上記をベースにツリーっぽくしていきます。
getVerticesDataで、頂点データを配列に取得し、着色用の配列も用意します。
var leafPos = leaf.getVerticesData(BABYLON.VertexBuffer.PositionKind);
var leafColors = [];
var trunkPos = trunk.getVerticesData(BABYLON.VertexBuffer.PositionKind);
var trunkColors = [];
次に葉の部分を三角錐の半径を変形させて、🎄っぽくさせていきます。
上で取得した葉の頂点座標(x、y、z)を、乱数である程度間隔をあけながらx座標、z座標を2.5倍にして外側へ広げ、y座標は-0.5して下に下がるように変形させました。
var idx=10;
for(var i = 0; i<(leafPos.length/3); i++) {
if(i==idx){
idx = i + Math.floor(Math.random() * 4) + 2;
leafPos[i*3] = leafPos[i*3] * 2.5;
leafPos[i*3+1] = leafPos[i*3+1] - 0.5;
leafPos[i*3+2] = leafPos[i*3+2] * 2.5;
}
}
leaf.updateVerticesData(BABYLON.VertexBuffer.PositionKind, leafPos);
枝葉っぽさが出たので、緑色を塗ります
用意しておいた配列に、位置を2.5倍した外側の頂点には緑色を、拡大しなかった内側の頂点は影っぽくしたいので、やや暗めの緑色をPushしていきます。
用意した色の配列は、setVerticesDataでオブジェクトに色を反映します。
var idx=10;
for(var i = 0; i<leafPos.length/3; i++) {
if(i==idx){
idx = i + Math.floor(Math.random() * 4) + 4;
leafPos[i*3] = leafPos[i*3] * 2.5;
leafPos[i*3+1] = leafPos[i*3+1] - 0.5;
leafPos[i*3+2] = leafPos[i*3+2] * 2.5;
leafColors.push(0, 0.8, 0.2, 1);
}else{
leafColors.push(0, 0.6, 0.1, 1);
}
}
leaf.updateVerticesData(BABYLON.VertexBuffer.PositionKind, leafPos);
leaf.setVerticesData(BABYLON.VertexBuffer.ColorKind, leafColors);
幹についても同様に編集します。
こちらは元の座標に少しランダム性を加えるだけにしました。
var idx=0;
for(var i = 0; i<trunkPos.length/3; i++) {
idx = i + Math.floor(Math.random() * 4) + 4;
trunkPos[i*3] = trunkPos[i*3] + Math.random()*0.05;
trunkPos[i*3+1] = trunkPos[i*3+1] + Math.random()*0.4-0.2;
trunkPos[i*3+2] = trunkPos[i*3+2] + Math.random()*0.05;
trunkColors.push(0.3+Math.random()*0.2, 0.1+Math.random()*0.1, Math.random()*0.1, 1);
}
trunk.updateVerticesData(BABYLON.VertexBuffer.PositionKind, trunkPos);
trunk.setVerticesData(BABYLON.VertexBuffer.ColorKind, trunkColors);
ここまでの実行結果とPlaygroundのURLは下の通りです。
https://playground.babylonjs.com/#UY72D0#22
飾り付け
最期に飾り付けです。
CreateSphereで球体を作成したのち、木と同じように頂点の編集をしてみます。
元の頂点座標を、1つ置き、2つ置きに外側へずらしてみましょう。
var spherePos = sphere.getVerticesData(BABYLON.VertexBuffer.PositionKind);
for(var i = 0; i<spherePos.length/3; i++) {
spherePos[i*3] = spherePos[i*3] * 2;
spherePos[i*3+1] = spherePos[i*3+1] * 2;
spherePos[i*3+2] = spherePos[i*3+2] * 2;
i=i+【+1,+2,…ここでカウンタを少し飛ばす】;
}
sphere.updateVerticesData(BABYLON.VertexBuffer.PositionKind, spherePos);
下図の左が通常の球体、中央が上のソースで i=i+1 を指定した場合、右側がi=i+2 を指定した場合です。
少しずつずらすと、螺旋が現れました。
枝を伸ばした処理の部分で、ランダムで球体、編集した球体を追加するようにして下図のような結果になります。
Playgroundはこちら。
https://playground.babylonjs.com/#UY72D0#24
最後に
テクスチャや3Dモデルを使用せずに基本メッシュの頂点座標の変更、色の指定機能で🎄を作成してみました。
外部ファイルを参照しないので、実行環境を気にせず動かせるのはちょっと試したい時に外的要因を考えなくていいので、粗々でなにかする時にはいいかもしれませんね。