10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Three.js faceVertexUvsを利用したテクスチャのアニメーション

Last updated at Posted at 2019-11-04

この記事はThree.jsテクスチャの部分切り抜きや回転・反転・伸縮 の続編になります。Three.jsやWebGLのテクスチャの仕組みは前回に詳しく書いていますので一読してみてください。

animated.png

マイクラのアニメーションするブロックのテクスチャファイルを見ると、縦にタイル状に並んだテクスチャが使われているのがわかります。
smoker_front_on.png
こういった画像を使ってアニメーションする場合、物理的に切り貼りしてテクスチャイメージを動的に読み直したりはせずに、CSSの background-image のoffset のように、見せたい部分だけをずらして表示させるとメモリ軽減になります。

See the Pen Minecraft Smoker Animation by Urushibara (@pneuma01) on CodePen.

[CodePen - Minecraft Smorker Animation](https://codepen.io/pneuma01/pen/RwwQQRW)

ここで表示させているのは、実際にMinecraftで使われている models/smoker.json (読込用にBlockbenchで変換)を使って生成した3Dオブジェクトです。
3Dオブジェクトは独自クラス化したコードで作成したものなので省きますが、基本的にはTHREE.BoxGeometryなどと同じ構造だと思ってください。

faceVertexUvs のVector2の値をずらすだけ

見てほしいのは、このアニメーションを実現させている箇所です。

呼び出し元

	let animate = (mesh) => {
		vectorAdd(mesh.geometry.faceVertexUvs[0][10], 16, 48, 0, 16);
		vectorAdd(mesh.geometry.faceVertexUvs[0][11], 16, 48, 0, 16);
		mesh.geometry.uvsNeedUpdate = true;
	}
Vector2を指定ピクセルでずらす関数
function vectorAdd(face, width, height, x, y)
{
	//Vector2をリアル座標系に変換してから座標を加算する関数
	let add = vector => {
		let xx = vector.x * width + x;
		let yy = (1 - vector.y) * height + y;

		return {x: xx, y: yy};
	}

	//すべての座標をリアル座標になおして加算
	let vexs = []; //Vector2の配列
	let maxX = 0, maxY = 0;
	face.forEach(vector => {
		let pos = add(vector)
		vexs.push(pos)
		//はみ出しチェック用
		maxX = Math.max(maxX, pos.x)
		maxY = Math.max(maxY, pos.y)
	});

	//最大幅よりもはみ出してたらマイナスする
	let minusX = maxX > width ? width: 0;
	let minusY = maxY > height ? height: 0;

	//座標系を変換して実際のfaceVertexUvsに代入
	for(let i=0; i < face.length; i++){
		face[i].x = (vexs[i].x - minusX) / width
		face[i].y = 1 - (vexs[i].y - minusY) / height
	}
}

少し複雑に見えるかもしれませんが、実際にやっていることはこんな感じです。

  • テクスチャ座標系からリアル座標に戻す(実際のテクスチャのWidth/Heightを掛ける)
  • 指定されたピクセルを加算
  • テクスチャ座標系にもどす(実際のテクスチャのWidth/Heightで割る)
  • faceVertexUvsに代入

前回に説明しましたが、テクスチャ座標系の場合はy軸が上下逆になるので注意。

クロスフェード?

マイクラでは、これを複数レイヤーで使ってクロスフェードまでやっています。 ~~Three.jsはどうもレイヤーの概念が無い?(faceVertexUvs[Layers]は存在するがマテリアルを個別指定できない)のでどうやっているのかちょっとわかりません。~~ →下記参照 同じ形状のGeometryを重ねて、2つのジオメトリのOpacity(透明度)をアニメーションして同じことができます。

image.png
+ merge

追記:
複数のテクスチャの合成は現在(rev120時点)のTHREE.jsが提供する機能では再現できないようです。
これを実現するには Canvasオブジェクトにwebglで2枚の画像を指定した値で合成するfragment shaderを書いて、THREE.CanvasTextureでテクスチャに割り当てる方法が考えられます。
THREE.ShaderMaterialでもfragment shaderを扱えますが、こちらはライティング処理も自力で書く必要があるためテクスチャの合成を行うだけの場合には向いてなさそうです。

10
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?