WKWebViewでthree.jsを表示した時にglbのテクスチャが真っ黒になる問題

WKWebViewでthree.jsのコンテンツを表示している際に、Blenderで作ったglbを読み込むと、WKWebViewをリロードした時に画像のテクスチャが真っ黒になる症状が発生しました。

その対策のためにやったことがこれらです。

three.jsのキャッシュを無効に

THREE.Cache.enabled = false;

効果なし🥲

GLTFLoaderを使いまわさない

function loadModel(path) {
  const loader = new GLTFLoader(); // 毎回新規作成
  loader.load(path, (gltf) => {
    scene.add(gltf.scene);
  });
}

効果なし🥲

読み込むglbファイルのパスに?${timestamp}をつける

const timestamp = Date.now();
loader.load(`model.glb?ts=${timestamp}`, ...);

効果なし🥲

webViewを作る際に毎回新しいプロセスプールを作成

let config = WKWebViewConfiguration()
config.processPool = WKProcessPool()
let webView = WKWebView(frame: .zero, configuration: config)

効果なし🥲

glbではなくgltf + bin + pngでエクスポートしたものを読み込む

const timestamp = Date.now();
loader.load(`model.gltf?ts=${timestamp}`, ...); //読み込むのはgltfだけでOK

効果なし🥲

GLTF読み込み後、テクスチャに needsUpdate = true

gltf.scene.traverse((child) => {
  if (child.isMesh && child.material.map) {
    child.material.map.needsUpdate = true;
  }
});

効果なし🥲

手動でテクスチャを読み込んでマテリアルに割り当てる

const tex = textureLoader.load('model/texture.png?ts=' + Date.now());

gltf.scene.traverse((child) => {
  if (child.isMesh && child.material.map) {
    const originalMap = child.material.map;
    const newMap = tex.clone();
    newMap.offset.copy(originalMap.offset);
    newMap.repeat.copy(originalMap.repeat);
    newMap.rotation = originalMap.rotation;
    newMap.center.copy(originalMap.center);
    newMap.wrapS = originalMap.wrapS;
    newMap.wrapT = originalMap.wrapT;
    newMap.flipY = originalMap.flipY;
    newMap.encoding = THREE.sRGBEncoding;
    child.material.map = newMap;
    child.material.needsUpdate = true;
  }
});

検証中…

バネみたいな動きの式

自分の中で「魔法の式」と呼んでいる式です。以下のような動きが実現できます。マウスカーソルを枠内に入れてみてください。スライダーで動きの調整も可能です。

  • 加速力
  • 摩擦力
  • 速さ

この動きを実現しているのがこんな式です。

移動量 = (移動量 + (目的地 - 現在地) / 摩擦力) * 加速力
現在地 += 移動量 * 速さ

上記のサンプルではX軸とY軸でそれぞれ移動量を出しています。具体的にはこんな感じ。

//moveX, moveY: 移動量
//toX, toY: 目的地(今回はカーソルの位置)
//x, y: 現在地(赤い丸の位置)
//friction: 摩擦力
//accel: 加速力

moveX = (moveX + (toX - x) / friction) * accel;
moveY = (moveY + (toY - y) / friction) * accel;
x += moveX * speed;
y += moveY * speed;
ball.style.left = x + "px";
ball.style.top = y + "px";

ゲームなんかで使えそうですかね。