11
5

More than 5 years have passed since last update.

Three.jsを使っていると「問題が起きたため、このWebページを再度読み込みました。」と出る

Last updated at Posted at 2015-12-09

iPadでThree.jsを使ったコンテンツを開発していたのですが、しばらくすると「問題が起きたため、このWebページを再度読み込みました。」というメッセージが表示されてページが再読み込みしてしまう現象に遭遇しました。
調べてみたところ、たくさんの画像を取っ替え引っ替え読み込んでいるうちにだんだん動作が重くなり落ちるという現象だということがわかりました。おそらくテクスチャーを切り替える時に前のテクスチャーがメモリ上に残り続けてしまってだんだん空きメモリがなくなっていくのだろうなと思い、実験してみました。

<html>

<head>
  <title>My first Three.js app</title>
  <style>
  body {
    margin: 0;
  }

  canvas {
    width: 100%;
    height: 100%
  }
  </style>
</head>

<body>
  <!-- 読み込み回数を表示 -->
  <div id="out">0</div>

  <script src="bower_components/three.js/three.min.js"></script>
  <script>
  // 以下、three.jsのサンプルより
  var scene = new THREE.Scene();
  var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

  var renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  var geometry = new THREE.BoxGeometry(1, 1, 1);
  var material = new THREE.MeshBasicMaterial({
    color: 0x00ff00
  });
  var cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  camera.position.z = 5;

  var render = function() {
    requestAnimationFrame(render);

    cube.rotation.x += 0.1;
    cube.rotation.y += 0.1;

    renderer.render(scene, camera);
  };

  render();

  var count = 0; // 読み込み回数
  var loader = new THREE.TextureLoader();

  function loadTexture() {
    loader.load('image.jpg', function(texture) {
      document.getElementById('out').innerHTML = ++count;

      // 読み込んだテクスチャーでマテリアルを作成
      cube.material = new THREE.MeshBasicMaterial({
        map: texture
      });
    });
  }

  // ひたすら読み込む!!!
  setInterval(loadTexture, 100);
  </script>
</body>

</html>

image.jpgは1024x1024の画像にしました。
上記のコードをiPadで走らせてみたところ、countが280くらいになったところで「問題が起きたため、このWebページを再度読み込みました。」と出ることがわかりました。
ちなみにPCのChromeでもcountが800くらいになったところでWebGLが無効になって3Dの表示が消えました。

対処法

新しいテクスチャーを読み込んで前のテクスチャーを上書きする場合は、texture.dispose()を呼ぶようにしましょう。
上記コードでいえば、// 読み込んだテクスチャーでマテリアルを作成の箇所を次のようにしてみます。

if (cube.material.map) {
  // 2回目以降
  cube.material.map.dispose(); // これを必ず呼ぶこと!!!
  cube.material.map = texture;
} else {
  // テクスチャーを使用しないマテリアルが設定されている場合は、マテリアルを新たに作成する
  cube.material = new THREE.MeshBasicMaterial({
    map: texture
  });
}

これで、1000回でも2000回でもテクスチャーを読み込めるようになります。

JavaScriptは自動でガーベジコレクションしてくれると思って安心してたらいけませんね!!!

11
5
0

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
11
5