2016年4月14日木曜日

Three.jsで波紋エフェクトをつくる

Three.js(r74)で画像に波紋のようなエフェクトをかけてみたので、その方法をまとめる。大まかな方法としては、画像をテクスチャにしたマテリアルで平面オブジェクトを作成し、x軸、y軸に沿って配置(z軸上の位置は0)する。波紋が起きたときにz軸上の位置をずらして波紋のように見えるようにする。z軸上の位置のずれを求めるには正弦波を使う。厳密には波動方程式を利用するらしいが、詳しい知識はないので深入りはしない。以下のような式で波紋のように見えるz軸上の位置が求められる。

z = a * sin2π(r/λ+vt)
a: 振幅
r: 中心からの距離
λ: 波長
v: 速度
t:  時間

1.オブジェクト作成
PlaneGeometryの第3,4引数の分割数は、少ないと滑らかに波紋が変化しないので、ある程度の数値にしておく。
// 平面ジオメトリ作成
var geometry = new THREE.PlaneGeometry( 4, 3, 32, 32);
// マテリアルの作成
var material = new THREE.MeshPhongMaterial( { map: new THREE.TextureLoader().load( 'persimmon.jpg' ), color: 0xffffff } );
// オブジェクトの作成
var plane = new THREE.Mesh( geometry, material );
// オブジェクトをシーンに追加
scene.add( plane );

2.z軸上のずれを計算する関数を作成
// r: 中心からの距離
// time: 時間
// 波の移動の向きを逆にするために速度-1として計算
// 波長は1として計算
function calcDisp( r, time ) {
return 0.2*Math.sin(2*Math.PI*(r*2 - time));
}

3.レンダリングループの中でオブジェクトの頂点情報を変化させる
レンダリングループの中でz軸上の変化を計算し、オブジェクトの位置情報である頂点情報をアップデートする。

// 減衰率
var mu0 = 0.995;
// 減衰係数
var mu = 1.0;

function render(){
// requestAnimationFrameで自分自身を呼び出し続けることでレンダリングを繰り返す
requestAnimationFrame( render );

var time = Date.now() * 0.0001;
var data = [];
var vertices = plane.geometry.vertices;

// 頂点情報のx、y座標から計算した中心からの距離と時間情報をもとにz軸上のずれを計算
for ( var i = 0; i < vertices.length; i++ ) {
var pos = new THREE.Vector2( vertices[i].x, vertices[i].y );
data[i] = mu*calcDisp( pos.length(), time );
}

// 減衰係数を減らしていくことで波紋の振幅を徐々に減衰させる
mu *= mu0;

// 計算結果を頂点情報に反映させる
for( var i = 0; i < data.length; i++ ) {
vertices[i].z = data[i];
}
plane.geometry.verticesNeedUpdate = true;

// レンダリング
renderer.render(scene,camera);
}

以上とその他のThree.jsに必要な処理を記述したhtmlを開くと、画像の中心から波紋が広がる。静止画だとわかりづらいが、以下のようになる。

0 件のコメント:

コメントを投稿