youkantoe

fSpyのデータをthree.jsに取り込んで写真合成を行う

2020 / 4 / 20

完成物はこちら

fSpyから吐き出したjsonデータです。

(普通にexportから吐き出せます)

このファイルのcameraTransformの箇所を使用します。

{
  "principalPoint": {
    "x": 0,
    "y": 0
  },
  "viewTransform": {
    "rows": [
      [
        -0.973413820702548,
        0.09650546518281947,
        -0.2077311456068043,
        1.671711489614533
      ],
      [
        0.04196681158657708,
        -0.8164170790874034,
        -0.5759357079567554,
        1.701876094251098
      ],
      [
        -0.22517619854355025,
        -0.5693415918095568,
        0.7906616415668813,
        -10
      ],
      [
        0,
        0,
        0,
        1
      ]
    ]
  },
  "cameraTransform": {
    "rows": [
      [
        -0.9734138207025481,
        0.04196681158657723,
        -0.22517619854355045,
        -0.6959172306086093
      ],
      [
        0.0965054651828195,
        -0.8164170790874036,
        -0.5693415918095569,
        -4.4653045032151235
      ],
      [
        -0.2077311456068041,
        -0.5759357079567556,
        0.7906616415668815,
        9.234054171727681
      ],
      [
        0,
        0,
        0,
        1
      ]
    ]
  },
  "horizontalFieldOfView": 1.3678550657796624,
  "verticalFieldOfView": 1.097495385241209,
  "vanishingPoints": [
    {
      "x": 0.3222986638911469,
      "y": 0.8935747627994785
    },
    {
      "x": -5.303011682125709,
      "y": 0.22862885996889182
    },
    {
      "x": 0.20793450169726774,
      "y": -1.7590846093077017
    }
  ],
  "vanishingPointAxes": [
    "zNegative",
    "xPositive",
    "yNegative"
  ],
  "relativeFocalLength": 1.2267259679457512,
  "imageWidth": 4032,
  "imageHeight": 3024
}

HTMLは非常にシンプルですね。

このあとのスクリプトも外部ファイルにするなら読み込みます。

  <canvas id="myCanvas"></canvas>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js"></script>

CSSはこんな感じで、キャンバスの後ろにbackgroundを設定する感じですね。

#myCanvas {
  background-image: url(./image.jpg);
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
}

スクリプト全体はこんな感じです。

ちょっとデータの取扱にコツがいりますが、基本はシンプルですね。

window.addEventListener('load',init);

function init(){

  // 元画像は4032 * 3024なのですが、でかいので 25%のサイズに変更しています。デモでは0.125倍にしています。
  const width = 1013;
  const height = 756;
  // 4x4の行列オブジェクトを作成します
  const m = new THREE.Matrix4();
  // fSpyから取得したデータを入れます。jsonを取ってきて入れてもいいかも。
  m.set(
    -0.9734138207025481,
    0.04196681158657723,
    -0.22517619854355045,
    -0.6959172306086093,

    0.0965054651828195,
    -0.8164170790874036,
    -0.5693415918095569,
    -4.4653045032151235,

    -0.2077311456068041,
    -0.5759357079567556,
    0.7906616415668815,
    9.234054171727681,

    0,
    0,
    0,
    1,

  );
  // レンダラーを設定します。Three.js側で背景画像を描画しない場合はalphaを有効にします
  const renderer = new THREE.WebGLRenderer({
    canvas: document.querySelector('#myCanvas'),
    alpha: true,
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);
  renderer.setClearColor( 0x000000 , 0 );


  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(45, width / height);
  // 各行列の4つめの値を入れて、カメラ位置を確定します
  camera.position.set( -0.6959172306086093 , -4.4653045032151235 , 9.234054171727681 );
  // カメラの回転を行列から取得します
  camera.setRotationFromMatrix( m );

  // ここでは適当にモデルを生成しています。
  const geometry = new THREE.BoxGeometry(3,3,3);
  const material = new THREE.MeshNormalMaterial();
  const box = new THREE.Mesh(geometry, material);
  scene.add(box);

  anim();

  // 関数の中に関数入れるのはあれですが、、、デモなので許してください...。
  function anim() {
    requestAnimationFrame(anim);
    renderer.render( scene , camera );
    box.rotation.y += 0.01;
  }
}

ファイルはここからダウンロードできます

gifhubはこちら

参考

fSpy

three.jsのMatrix4説明ページ

補足

  • fSpyのxyzの指定によっては方法が異なる可能性は高いです\

    • 今回はx,yでやっていたはず(たぶん)
  • なにか間違ってたら教えて下さい…(githubのPRとかissueで…)