在Threejs中将实时更新纹理的编码设置为sRGB时,fps减少了一半

问题描述 投票:0回答:1

我有一个 Threejs 应用程序,必须更新每一帧中的纹理,THREE.WebGLRenderer 的输出编码是 sRGB。

当我将纹理编码设置为sRGB时,渲染结果是正确的,Mac Chrome上的fps约为26~29,HUAWEI Meta40上的fps约为30~40;如果我不设置纹理编码,渲染结果是错误的,在 Mac Chrome 和 HUAWEI Meta40 上 fps 都是 60 并且稳定。

我想知道当我将纹理编码设置为 sRGB 时发生了什么,这降低了 fps?是否可以提高 fps 并保持结果正确?

这是演示代码:

<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>sRGB Demo</title>
  <style>
    body {
      /* overflow: hidden; */
      border: none;
      margin: 0;
      background-color: #f7f5f4;
    }

    #container {
      position: absolute;
      left: 0;
      top: 0;
      width: 100vw;
      height: 100vh;
    }
    #canvas {
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.149.0/three.min.js"></script>
  <div id="container">
    <canvas id="canvas"></canvas>
  </div>
  <script>
    class App {
      constructor(container, image) {
        this.image = image;

        this.fpsDom = document.createElement('div');
        this.fpsDom.style.position = 'fixed';
        this.fpsDom.style.left = '20px';
        this.fpsDom.style.top = '20px';
        this.fpsDom.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        this.fpsDom.style.color = '#fff';
        this.fpsDom.style.padding = '5px';
        this.fpsDom.innerText = '0.0'
        document.body.appendChild(this.fpsDom);
        this.frameCount = 0;
        this.lastFpsTime = 0;

        this.webglCanvas = document.getElementById('canvas');
        const rect = this.webglCanvas.getBoundingClientRect();
        this.webglCanvas.width = rect.width * window.devicePixelRatio;
        this.webglCanvas.height = rect.height * window.devicePixelRatio;
        // console.log(this.webglCanvas.width, this.webglCanvas.height);

        this.initThree();

        const render = () => {
          const tm = performance.now();

          requestAnimationFrame(render);

          ++this.frameCount;
          const now = performance.now();
          if (now - this.lastFpsTime>1000) {
            this.fpsDom.innerText = (this.frameCount*1000/(now-this.lastFpsTime)).toFixed(1);
            this.frameCount = 0;
            this.lastFpsTime = now;
          }

          if (!this.videoMesh.material.map) {
            this.videoMesh.material.map = new THREE.CanvasTexture(this.image);
            this.videoMesh.material.map.generateMipmaps = false;
            this.videoMesh.material.map.minFilter = THREE.LinearFilter;
            this.videoMesh.material.map.magFilter = THREE.LinearFilter;
            this.videoMesh.material.map.encoding = THREE.sRGBEncoding;
          } else {
            this.videoMesh.material.map.needsUpdate = true;
          }
          this.renderer.render(this.scene, this.camera);

          // console.log('render', (performance.now() - tm).toFixed(1));
        }

        render();

      }

      initThree() {
        this.renderer = new THREE.WebGLRenderer({
          alpha: false,
          antialias: false,
          canvas: this.webglCanvas,
        });

        this.renderer.setPixelRatio(1);
        this.renderer.setSize(this.webglCanvas.width, this.webglCanvas.height, false);
        this.renderer.outputEncoding = THREE.sRGBEncoding;

        this.camera = new THREE.PerspectiveCamera();
        this.camera.position.set(0, 0, 1);

        const geometry = new THREE.PlaneGeometry( 1, 1 );
        const material = new THREE.MeshBasicMaterial();
        this.videoMesh = new THREE.Mesh( geometry, material );
        this.videoMesh.position.set(0, 0, 0);

        this.scene = new THREE.Scene();
        this.scene.add(this.videoMesh);
      }
    }

    const container = document.getElementById('container');
    const img = new Image();
    img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAB+FBMVEUAAAA/mUPidDHiLi5Cn0XkNTPmeUrkdUg/m0Q0pEfcpSbwaVdKskg+lUP4zA/iLi3msSHkOjVAmETdJSjtYFE/lkPnRj3sWUs8kkLeqCVIq0fxvhXqUkbVmSjwa1n1yBLepyX1xxP0xRXqUkboST9KukpHpUbuvRrzrhF/ljbwaljuZFM4jELaoSdLtElJrUj1xxP6zwzfqSU4i0HYnydMtUlIqUfywxb60AxZqEXaoifgMCXptR9MtklHpEY2iUHWnSjvvRr70QujkC+pUC/90glMuEnlOjVMt0j70QriLS1LtEnnRj3qUUXfIidOjsxAhcZFo0bjNDH0xxNLr0dIrUdmntVTkMoyfL8jcLBRuErhJyrgKyb4zA/5zg3tYFBBmUTmQTnhMinruBzvvhnxwxZ/st+Ktt5zp9hqota2vtK6y9FemNBblc9HiMiTtMbFtsM6gcPV2r6dwroseLrMrbQrdLGdyKoobKbo3Zh+ynrgVllZulTsXE3rV0pIqUf42UVUo0JyjEHoS0HmsiHRGR/lmRz/1hjqnxjvpRWfwtOhusaz0LRGf7FEfbDVmqHXlJeW0pbXq5bec3fX0nTnzmuJuWvhoFFhm0FtrziBsjaAaDCYWC+uSi6jQS3FsSfLJiTirCOkuCG1KiG+wSC+GBvgyhTszQ64Z77KAAAARXRSTlMAIQRDLyUgCwsE6ebm5ubg2dLR0byXl4FDQzU1NDEuLSUgC+vr6urq6ubb29vb2tra2tG8vLu7u7uXl5eXgYGBgYGBLiUALabIAAABsElEQVQoz12S9VPjQBxHt8VaOA6HE+AOzv1wd7pJk5I2adpCC7RUcHd3d3fXf5PvLkxheD++z+yb7GSRlwD/+Hj/APQCZWxM5M+goF+RMbHK594v+tPoiN1uHxkt+xzt9+R9wnRTZZQpXQ0T5uP1IQxToyOAZiQu5HEpjeA4SWIoksRxNiGC1tRZJ4LNxgHgnU5nJZBDvuDdl8lzQRBsQ+s9PZt7s7Pz8wsL39/DkIfZ4xlB2Gqsq62ta9oxVlVrNZpihFRpGO9fzQw1ms0NDWZz07iGkJmIFH8xxkc3a/WWlubmFkv9AB2SEpDvKxbjidN2faseaNV3zoHXvv7wMODJdkOHAegweAfFPx4G67KluxzottCU9n8CUqXzcIQdXOytAHqXxomvykhEKN9EFutG22p//0rbNvHVxiJywa8yS2KDfV1dfbu31H8jF1RHiTKtWYeHxUvq3bn0pyjCRaiRU6aDO+gb3aEfEeVNsDgm8zzLy9egPa7Qt8TSJdwhjplk06HH43ZNJ3s91KKCHQ5x4sw1fRGYDZ0n1L4FKb9/BP5JLYxToheoFCVxz57PPS8UhhEpLBVeAAAAAElFTkSuQmCC';
    img.onload = () => {
      const c = document.createElement('canvas');
      c.width = 1080;
      c.height = 1920;
      c.getContext('2d').drawImage(img, 0, 0, c.width, c.height);
      const app = new App(container, c);
    }
  </script>
</body>

</html>

更新:

我还发现渲染函数本身运行速度非常快(大约1ms),但是当我将纹理编码设置为sRGB时,requestAnimationFrame调用周期变得非常长,所以我认为GPU一定做了什么阻止浏览器更新下一帧。

javascript browser three.js webgl
1个回答
0
投票

我发现了这个问题的问题:https://github.com/mrdoob/ Three.js/issues/26183

所以我在着色器中手动将纹理从 sRGB 转码为 Linear 来解决这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.