빠른 WebGL/3js 3D 그래픽 및 Offscreencavas 및 네트워크 작업자

번역:Russian
Three.js 라이브러리를 사용하여 복잡한 장면을 만들 때 WebGL 성능을 향상시키는 방법을 알아보려면 렌더링을 주 스레드에서 OffscreenCanvas을 사용하는 웹 워크맨으로 이동하는 것이 좋습니다.로우엔드 장치에서는 3D 효과가 더 좋고 평균 성능도 향상됩니다.
personal website에 지구의 3D WebGL 모형을 만든 후에 나는 내가 Google Lighthouse에서 즉시 5%의 손실을 보았다는 것을 발견했다.
본고에서 크로스 브라우저 호환성을 희생하지 않고 성능을 다시 얻을 수 있는 방법을 보여 드리며, 이를 위해 작성한 소형 라이브러리를 사용합니다.

문제.


세 개.복잡한 WebGL 장면을 쉽게 만들 수 있습니다.불행히도, 이것은 대가가 있는 것이다.세 개.js는 js 패키지 크기를 약 563 KB 증가시킵니다. (구조상 실제 나무가 흔들리지 않습니다.)
평균 배경 이미지가 같은 500KB가 있을 수 있다고 말할 수 있다.그러나 천 바이트당 자바스크립트가 사이트 전체 성능에 미치는 영향은 천 바이트의 이미지 데이터보다 크다.만약 당신의 목표가 빠른 사이트라면, 지연과 대역폭은 유일하게 고려해야 할 일이 아니다. CPU가 당신의 내용을 처리하는 데 얼마나 많은 시간을 들일지 고려하는 것도 중요하다.로우엔드 디바이스에서 리소스를 다운로드하는 것보다 처리하는 데 더 많은 시간이 걸릴 수 있습니다.
3.5초 170KB JS, 0.1초 170KB JPEG 처리.Addy Osmani
브라우저가 세 개의 500KB 데이터를 처리할 때, 당신의 웹 페이지는 효과적으로 동결될 것입니다.JavaScript를 실행하는 데 주 스레드가 사용되기 때문에 js 코드입니다.장면을 완전히 렌더링하기 전에는 페이지와 상호 작용할 수 없습니다.

인터넷 종사자와 스크린 아웃도어


WebWorkers는 JS가 실행되는 동안 페이지가 동결되지 않도록 하는 솔루션입니다.이것은 일부 JavaScript 코드를 개별 스레드로 이동하는 방법입니다.
불행히도 다중 루틴 프로그래밍은 매우 어렵다.작업을 단순화하기 위해 웹 작업자는 DOM에 액세스할 수 없습니다.이 액세스 권한은 주 JavaScript 스레드에만 있습니다.하지만 세 가지가 있다.js는 DOM에 있는 <canvas> 노드에 접근해야 합니다.
OffscreenCanvas 은 이 문제의 해결 방안이다.Web Worker로 캔버스 액세스를 이동할 수 있습니다.이 솔루션을 선택하면 주 스레드가 <canvas>에 접근할 수 없기 때문에 여전히 스레드가 안전합니다.
Google의 기본은 덮어썼지만 문제는 화면 밖의 캔버스 API는 Google Chrome에서만 지원된다는 점입니다.
2019년 4월, 외부 캔버스를 지원하는 브라우저는 Can I Use
그러나 우리의 주요 적, 크로스 브라우저 문제에 직면해도 우리는 두려워하지 않는다.점진적 향상: Chrome과 향후 브라우저의 성능을 향상시킬 것입니다.기타 세 가지 브라우저가 실행됩니다.js는 기본 JavaScript 스레드에서 이전 방식을 사용합니다.
우리는 두 개의 서로 다른 환경을 위해 파일을 작성하는 방법을 생각해 내야 한다. 많은 DOM API가 웹 워커에서 일할 수 없다는 것을 기억해야 한다.

솔루션


모든 해커 공격을 숨기고 코드의 가독성을 유지하기 위해 나는 작은 offscreen-canvas JS 라이브러리 (400바이트만) 를 만들었다.아래의 예는 그것에 의존할 것이지만, 나는 그것이 어떻게 엔진 뚜껑 아래에서 작동하는지 설명할 것이다.
먼저 프로젝트에 offscreen-canvas npm 패키지를 추가합니다.
npm install offscreen-canvas
웹 워커에 대해 별도의 JS 파일을 제공해야 합니다.웹 팩 또는 Parcel 구성에서 별도의 JS 패키지를 만듭니다.
  entry: {
    'app': './src/app.js',
+   'webgl-worker': './src/webgl-worker.js'
  }
Bundler는 제품에 bundle의 파일 이름에 캐시 버스터를 추가합니다.주 JS 파일에서 이 이름을 사용하려면 preload 태그를 추가합니다.구체적인 코드는 HTML을 생성하는 방법에 따라 달라집니다.
    <link type="preload" as="script" href="./webgl-worker.js">
  </head>
이제 우리는 주 JS 파일에서 캔버스 노드와 작업 URL을 얻어야 한다.
import createWorker from 'offscreen-canvas/create-worker'

const workerUrl = document.querySelector('[rel=preload][as=script]').href
const canvas = document.querySelector('canvas')

const worker = createWorker(canvas, workerUrl)
createWorker 에서 canvas.transferControlToOffscreen을 찾아 OffscreenCanvas 지원을 테스트합니다.브라우저가 지원하는 경우 라이브러리는 JS 파일을 웹 워커로 로드합니다.그렇지 않으면 JS 파일이 일반 스크립트로 로드됩니다.
이제 webgl-worker.js을 열겠습니다.
import insideWorker from 'offscreen-canvas/inside-worker'

const worker = insideWorker(e => {
  if (e.data.canvas) {
    // Here we will initialize Three.js
  }
})
insideWorker 웹 워커에 로드되었는지 확인합니다.환경에 따라 주선과 서로 다른 방식으로 통신할 것이다.
라이브러리는 주 라인에서 온 모든 메시지에 대한 리셋을 실행합니다.createWorker에서 온 첫 번째 메시지는 항상 { canvas, width, height }이 화포를 초기화하는 대상이다.
+ import {
+   WebGLRenderer, Scene, PerspectiveCamera, AmbientLight,
+   Mesh, SphereGeometry, MeshPhongMaterial
+ } from 'three'
  import insideWorker from 'offscreen-canvas/inside-worker'

+ const scene = new Scene()
+ const camera = new PerspectiveCamera(45, 1, 0.01, 1000)
+ scene.add(new AmbientLight(0x909090))
+
+ let sphere = new Mesh(
+   new SphereGeometry(0.5, 64, 64),
+   new MeshPhongMaterial()
+ )
+ scene.add(sphere)
+
+ let renderer
+ function render () {
+   renderer.render(scene, camera)
+ }

  const worker = insideWorker(e => {
    if (e.data.canvas) {
+     // canvas in Web Worker will not have size, we will set it manually to avoid errors from Three.js
+     if (!canvas.style) canvas.style = { width, height }
+     renderer = new WebGLRenderer({ canvas, antialias: true })
+     renderer.setPixelRatio(pixelRatio)
+     renderer.setSize(width, height)
+
+     render()
    }
  })
장면의 초기 상태를 만들 때, 우리는 세 가지 측면에서 오류 메시지를 찾을 수 있다.js.모든 DOM API를 웹 워커에서 사용할 수 있는 것은 아닙니다.예를 들어, SVG 텍스쳐를 로드할 document.createElement이 없습니다.Web Worker와 일반 스크립트 환경에 대해 다른 로드 프로그램이 필요합니다.worker.isWorker을 통해 환경을 감지할 수 있습니다.
      renderer.setPixelRatio(pixelRatio)
      renderer.setSize(width, height)

+     const loader = worker.isWorker ? new ImageBitmapLoader() : new ImageLoader()
+     loader.load('/texture.png', mapImage => {
+       sphere.material.map = new CanvasTexture(mapImage)
+       render()
+     })

      render()
우리는 장면의 초기 상태를 과장했다.그러나 대부분의 WebGL 장면은 사용자 작업에 반응해야 합니다.그것은 아마도 마우스로 카메라를 회전시킬 것이다.또는 canvas의 창 크기를 업데이트합니다.불행하게도 웹 워커는 DOM 이벤트에 액세스할 수 있는 권한이 없습니다.주 스레드에서 이벤트를 수신하고 작업 스레드에 메시지를 보내야 합니다.
  import createWorker from 'offscreen-canvas/create-worker'

  const workerUrl = document.querySelector('[rel=preload][as=script]').href
  const canvas = document.querySelector('canvas')

  const worker = createWorker(canvas, workerUrl)

+ window.addEventListener('resize', () => {
+   worker.post({
+     type: 'resize', width: canvas.clientWidth, height: canvas.clientHeight
+   })
+ })
  const worker = insideWorker(e => {
    if (e.data.canvas) {
      if (!canvas.style) canvas.style = { width, height }
      renderer = new WebGLRenderer({ canvas, antialias: true })
      renderer.setPixelRatio(pixelRatio)
      renderer.setSize(width, height)

      const loader = worker.isWorker ? new ImageBitmapLoader() : new ImageLoader()
      loader.load('/texture.png', mapImage => {
        sphere.material.map = new CanvasTexture(mapImage)
        render()
      })

      render()
-   }
+   } else if (e.data.type === 'resize') {
+     renderer.setSize(width, height)
+     render()
+   }
  })

결과는요

OffscreenCanvas을 사용하여 Chrome로 개인 웹 사이트의 UI 동결을 수정했고 Google Lighthouse에서 100점 만점을 받았습니다.나의 WebGL 장면은 모든 다른 브라우저에서 정상적으로 작동할 수 있다.
결과: demomain thread, worker의 원본 코드를 검사할 수 있습니다.
과 OffscreenCanvas 구글 등대의 성능 비율이 95에서 100으로 높아졌다

좋은 웹페이지 즐겨찾기