Help us understand the problem. What is going on with this article?

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

この記事は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.

ここで表示させているのは、実際に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(透明度)をアニメーションして同じことができますが、なにか別な方法がありそう。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away