【问题标题】:Three.js: Setting `texture.needsUpdate = true` is very slowThree.js:设置`texture.needsUpdate = true`很慢
【发布时间】:2018-07-26 23:26:48
【问题描述】:

我正在处理 Three.js 场景,我想在一段时间后更新一些纹理。但是,我发现更新纹理非常慢,并且将 FPS 拖到仅 1-2 FPS 几秒钟(仅更新单个纹理时)。

有什么可以加快纹理更新的吗?其他人可以就这个问题提供的任何见解将不胜感激。

要查看此行为,请单击以下示例的窗口。这将加载第一个纹理更新(再次单击将触发第二个纹理更新)。如果您在其中一次单击后尝试缩放,您会发现屏幕冻结并且 FPS 将大幅下降。有谁知道如何解决这个问题?

<html>
<head>
  <style>
  html, body { width: 100%; height: 100%; background: #000; }
  body { margin: 0; overflow: hidden; }
  canvas { width: 100%; height: 100%; }
  </style>
</head>
<body>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js'></script>
  <script src='https://rawgit.com/YaleDHLab/pix-plot/master/assets/js/trackball-controls.js'></script>
  <script src='https://rawgit.com/mrdoob/stats.js/master/build/stats.min.js'></script>

    <script type='x-shader/x-vertex' id='vertex-shader'>
    precision highp float;

    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;

    uniform vec3 cameraPosition;

    attribute vec3 position; // sets the blueprint's vertex positions
    attribute vec3 translation; // x y translation offsets for an instance
    attribute float texIdx; // the texture index to access

    varying float vTexIdx;

    void main() {
      // set point position
      vec3 pos = position + translation;
      vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
      gl_Position = projected;

      // assign the varyings
      vTexIdx = texIdx;

      // use the delta between the point position and camera position to size point
      float xDelta = pow(projected[0] - cameraPosition[0], 2.0);
      float yDelta = pow(projected[1] - cameraPosition[1], 2.0);
      float zDelta = pow(projected[2] - cameraPosition[2], 2.0);
      float delta  = pow(xDelta + yDelta + zDelta, 0.5);
      gl_PointSize = 40000.0 / delta;
    }
    </script>

    <script type='x-shader/x-fragment' id='fragment-shader'>
    precision highp float;

    uniform sampler2D a;
    uniform sampler2D b;

    varying float vTexIdx;

    void main() {
      int textureIndex = int(vTexIdx);
      vec2 uv = vec2(gl_PointCoord.x, gl_PointCoord.y);
      if (textureIndex == 0) {
        gl_FragColor = texture2D(a, uv);
      } else if (textureIndex == 1) {
        gl_FragColor = texture2D(b, uv);
      }
    }
    </script>

  <script>

  /**
  * Generate a scene object with a background color
  **/

  function getScene() {
    var scene = new THREE.Scene();
    scene.background = new THREE.Color(0xaaaaaa);
    return scene;
  }

  /**
  * Generate the camera to be used in the scene
  **/

  function getCamera() {
    var aspectRatio = window.innerWidth / window.innerHeight;
    var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 100000);
    camera.position.set(0, 1, -6000);
    return camera;
  }

  /**
  * Generate the renderer to be used in the scene
  **/

  function getRenderer() {
    // Create the canvas with a renderer
    var renderer = new THREE.WebGLRenderer({antialias: true});
    // Add support for retina displays
    renderer.setPixelRatio(window.devicePixelRatio);
    // Specify the size of the canvas
    renderer.setSize(window.innerWidth, window.innerHeight);
    // Add the canvas to the DOM
    document.body.appendChild(renderer.domElement);
    return renderer;
  }

  /**
  * Generate the controls to be used in the scene
  **/

  function getControls(camera, renderer) {
    var controls = new THREE.TrackballControls(camera, renderer.domElement);
    controls.zoomSpeed = 0.4;
    controls.panSpeed = 0.4;
    return controls;
  }

  /**
  * Generate the points for the scene
  **/

  function addPoints(scene) {
    var BA = THREE.BufferAttribute;
    var IBA = THREE.InstancedBufferAttribute;
    var geometry  = new THREE.InstancedBufferGeometry();

    // add data for each observation
    var n = 10000; // number of observations
    var rootN = n**(1/2);
    var cellSize = 20;
    var translation = new Float32Array( n * 3 );
    var texIdx = new Float32Array( n );
    var translationIterator = 0;
    var texIterator = 0;
    for (var i=0; i<n*3; i++) {
      var x = Math.random() * n - (n/2);
      var y = Math.random() * n - (n/2);
      translation[translationIterator++] = x;
      translation[translationIterator++] = y;
      translation[translationIterator++] = Math.random() * n - (n/2);
      texIdx[texIterator++] = (x + y) > (n/8) ? 1 : 0;
    }
    var positionAttr = new BA(new Float32Array( [0, 0, 0] ), 3);
    var translationAttr = new IBA(translation, 3, 1);
    var texIdxAttr = new IBA(texIdx, 1, 1);
    positionAttr.dynamic = true;
    translationAttr.dynamic = true;
    texIdxAttr.dynamic = true;
    geometry.addAttribute('position', positionAttr);
    geometry.addAttribute('translation', translationAttr);
    geometry.addAttribute('texIdx', texIdxAttr);

    var canvases = [
      getElem('canvas', { width: 16384, height: 16384, }),
      getElem('canvas', { width: 16384, height: 16384, }),
    ]

    var textures = [
      getTexture( canvases[0] ),
      getTexture( canvases[1] ),
    ];

    var material = new THREE.RawShaderMaterial({
      uniforms: {
        a: {
          type: 't',
          value: textures[0],
        },
        b: {
          type: 't',
          value: textures[1],
        }
      },
      vertexShader: document.getElementById('vertex-shader').textContent,
      fragmentShader: document.getElementById('fragment-shader').textContent,
    });
    var mesh = new THREE.Points(geometry, material);
    mesh.frustumCulled = false; // prevent the mesh from being clipped on drag
    scene.add(mesh);

    // on the first window click, paint red points
    // on the second window click, paint blue points
    var clicks = 0;
    window.addEventListener('click', function() {
      if (clicks == 0 || clicks == 1) {
        var canvas = canvases[clicks];
        var ctx = canvas.getContext('2d');
        ctx.fillStyle = clicks == 0 ? 'red' : 'blue';
        ctx.rect(0, 0, 16384, 16384);
        ctx.fill();
        textures[clicks].needsUpdate = true;
        clicks++;
      }
    })
  }

  function getTexture(canvas) {
    var tex = new THREE.Texture(canvas);
    tex.needsUpdate = true;
    tex.flipY = false;
    return tex;
  }

  /**
  * Create an element
  **/

  function getElem(tag, obj) {
    var obj = obj || {};
    var elem = document.createElement(tag);
    Object.keys(obj).forEach(function(attr) {
      elem[attr] = obj[attr];
    })
    return elem;
  }

  /**
  * Add stats
  **/

  function getStats() {
    var stats = new Stats();
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.top = '65px';
    stats.domElement.style.right = '5px';
    stats.domElement.style.left = 'initial';
    document.body.appendChild(stats.domElement);
    return stats;
  }


  /**
  * Render!
  **/

  function render() {
    requestAnimationFrame(render);
    renderer.render(scene, camera);
    controls.update();
    stats.update();
  };

  /**
  * Main
  **/

  var stats = getStats();
  var scene = getScene();
  var camera = getCamera();
  var renderer = getRenderer();
  var controls = getControls(camera, renderer);
  addPoints(scene);
  render();

  </script>
</body>
</html>

【问题讨论】:

    标签: javascript three.js


    【解决方案1】:

    你的画布是 16384 x 16384。这基本上是大得离谱。

    对于 RGBA 格式,即 1073741824 字节。当您设置该 texture.needsUpdate = true 时,一个 千兆字节 的纹理数据将从 CPU 发送到您的 GPU

    你肯定会注意到这个被上传到卡上。

    如果您的用例绝对需要那么大的纹理......那么您可能需要考虑通过 gl.texSubImage2D 进行增量更新,或者使用一堆较小的纹理并且只更新每帧中的一个,或者只更新这些纹理应用程序的开始,而不是之后。

    作为参考,我所见过的每边纹理 > 4k 的情况很少。

    这大约是纹理大小的 1/16。

    顺便说一句,这与 three.js 无关。这是 GPU/CPU 交互的基本特征。上传和状态变化很慢,必须仔细安排和监控。

    【讨论】:

    • 这很尴尬。我在纹理设置为gl.MAX_TEXTURE_SIZE 的更大、更复杂的场景中注意到了这种行为,并且在缩小画布时没有看到性能提升,但实际上并没有缩小画布。感谢gl.texSubImage2D 的参考。作为记录,您是否知道使用更少的大纹理(我正在绘制约 1M 图像)与更多更小的纹理进行性能研究?我假设并快速测试了 frag 着色器中较少的 tex 条件会带来更好的性能,但我并没有全面。不管怎样,谢谢你!
    • 其实我为我的回答语气道歉.. 这有点居高临下。通常,您对着色器条件和纹理绑定需要时间的直觉是正确的。有些渲染架构的工作方式有点像您正在朝着的方向发展……例如 Carmacs MegaTexture 的东西。
    • 但这些方案主要通过使用 subImage2d 或类似的类似物进行小的纹理更新来工作。使用大型纹理图集的一个棘手部分是它使模型上的平铺纹理更加复杂。您还可能会遇到沿单个纹理样本边缘过滤的问题,需要扩展样本的边界像素以创建额外的边缘数据以供过滤抓取。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 2015-08-15
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2013-03-04
    • 1970-01-01
    相关资源
    最近更新 更多