ThreeJS WebXR场景在React中显示为黑色,但在静态应用程序中则不显示

时间:2020-10-17 00:47:36

标签: three.js webxr

我正在尝试从React中的ThreeJS示例中重新创建WebXR dragging demo场景,但是在启动WebXR场景时发现了一个问题。

在我的React应用程序中,无论文档或场景颜色如何,它都会启动为黑色场景。当我退出VR场景时,渲染会暂停并且控件不会更新。

我有两个场景相同的演示:一个工作的静态演示和一个损坏的React演示。

静态工作演示(live via codesandbox

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>WebXR Test</title>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, user-scalable=no"
    />
    <style>
      body {
        margin: 0;
        background-color: #000;
        overscroll-behavior: none;
      }

      canvas {
        bottom: 0;
        left: 0;
        position: absolute;
        right: 0;
        top: 0;
        width: 100vw;
      }
    </style>
  </head>
  <body>
    <script type="module">
      import * as THREE from "https://unpkg.com/three/build/three.module.js";
      import { OrbitControls } from "https://unpkg.com/three/examples/jsm/controls/OrbitControls.js";
      import { VRButton } from "https://unpkg.com/three/examples/jsm/webxr/VRButton.js";
      import { XRControllerModelFactory } from "https://unpkg.com/three/examples/jsm/webxr/XRControllerModelFactory.js";

      let container;
      let camera, scene, renderer;
      let controller1, controller2;
      let controllerGrip1, controllerGrip2;

      let controls;

      init();
      animate();

      function init() {
        container = document.createElement("div");
        document.body.appendChild(container);

        scene = new THREE.Scene();
        scene.background = new THREE.Color(0x808080);

        camera = new THREE.PerspectiveCamera(
          50,
          window.innerWidth / window.innerHeight,
          0.1,
          10
        );
        camera.position.set(0, 1.6, 3);

        controls = new OrbitControls(camera, container);
        controls.target.set(0, 1.6, 0);
        controls.update();

        const floorGeometry = new THREE.PlaneBufferGeometry(4, 4);
        const floorMaterial = new THREE.MeshStandardMaterial({
          color: 0xeeeeee,
          roughness: 1.0,
          metalness: 0.0
        });
        const floor = new THREE.Mesh(floorGeometry, floorMaterial);
        floor.rotation.x = -Math.PI / 2;
        floor.receiveShadow = true;
        scene.add(floor);

        scene.add(new THREE.HemisphereLight(0x808080, 0x606060));

        const light = new THREE.DirectionalLight(0xffffff);
        light.position.set(0, 6, 0);
        light.castShadow = true;
        light.shadow.camera.top = 2;
        light.shadow.camera.bottom = -2;
        light.shadow.camera.right = 2;
        light.shadow.camera.left = -2;
        light.shadow.mapSize.set(4096, 4096);
        scene.add(light);

        //

        renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.outputEncoding = THREE.sRGBEncoding;
        renderer.shadowMap.enabled = true;
        renderer.xr.enabled = true;
        container.appendChild(renderer.domElement);

        document.body.appendChild(VRButton.createButton(renderer));

        // controllers

        controller1 = renderer.xr.getController(0);
        controller1.name = "left";
        scene.add(controller1);

        controller2 = renderer.xr.getController(1);
        controller2.name = "right";
        scene.add(controller2);

        const controllerModelFactory = new XRControllerModelFactory();

        controllerGrip1 = renderer.xr.getControllerGrip(0);
        controllerGrip1.add(
          controllerModelFactory.createControllerModel(controllerGrip1)
        );
        scene.add(controllerGrip1);

        controllerGrip2 = renderer.xr.getControllerGrip(1);
        controllerGrip2.add(
          controllerModelFactory.createControllerModel(controllerGrip2)
        );
        scene.add(controllerGrip2);

        //

        window.addEventListener("resize", onWindowResize, false);
      }

      function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize(window.innerWidth, window.innerHeight);
      }

      //

      function animate() {
        renderer.setAnimationLoop(render);
      }

      function render() {
        renderer.render(scene, camera);
      }
    </script>
  </body>
</html>

反应不灵的演示(live via codesandbox

body {
  margin: 0;
  background-color: #000;
  overscroll-behavior: none;
}

canvas {
  bottom: 0;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
  width: 100vw;
}
import React, { useRef, useEffect } from "react";
import { render } from "react-dom";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { VRButton } from "three/examples/jsm/webxr/VRButton.js";
import { XRControllerModelFactory } from "three/examples/jsm/webxr/XRControllerModelFactory.js";
import "./styles.css"; // inlined above

const App = () => {
  const canvasRef = useRef();
  const camera = useRef();
  const scene = useRef();
  const renderer = useRef();
  const controller1 = useRef();
  const controller2 = useRef();
  const controllerGrip1 = useRef();
  const controllerGrip2 = useRef();
  const controls = useRef();

  useEffect(() => {
    renderer.current = new THREE.WebGLRenderer({
      canvas: canvasRef.current,
      antialias: true
    });
    renderer.current.setPixelRatio(window.devicePixelRatio);
    renderer.current.setSize(window.innerWidth, window.innerHeight);
    renderer.current.outputEncoding = THREE.sRGBEncoding;
    renderer.current.shadowMap.enabled = true;
    renderer.current.xr.enabled = true;

    document.body.appendChild(VRButton.createButton(renderer));

    camera.current = new THREE.PerspectiveCamera(
      50,
      window.innerWidth / window.innerHeight,
      0.1,
      10
    );
    camera.current.position.set(0, 1.6, 3);

    controls.current = new OrbitControls(
      camera.current,
      renderer.current.domElement
    );
    controls.current.target.set(0, 1.6, 0);
    controls.current.update();

    scene.current = new THREE.Scene();
    scene.current.background = new THREE.Color(0x808080);

    const floorGeometry = new THREE.PlaneBufferGeometry(4, 4);
    const floorMaterial = new THREE.MeshStandardMaterial({
      color: 0xeeeeee,
      roughness: 1.0,
      metalness: 0.0
    });
    const floor = new THREE.Mesh(floorGeometry, floorMaterial);
    floor.rotation.x = -Math.PI / 2;
    floor.receiveShadow = true;
    scene.current.add(floor);

    scene.current.add(new THREE.HemisphereLight(0x808080, 0x606060));

    const light = new THREE.DirectionalLight(0xffffff);
    light.position.set(0, 6, 0);
    light.castShadow = true;
    light.shadow.camera.top = 2;
    light.shadow.camera.bottom = -2;
    light.shadow.camera.right = 2;
    light.shadow.camera.left = -2;
    light.shadow.mapSize.set(4096, 4096);
    scene.current.add(light);

    // controllers

    controller1.current = renderer.current.xr.getController(0);
    controller1.current.name = "left";
    scene.current.add(controller1.current);

    controller2.current = renderer.current.xr.getController(1);
    controller2.current.name = "right";
    scene.current.add(controller2.current);

    const controllerModelFactory = new XRControllerModelFactory();

    controllerGrip1.current = renderer.current.xr.getControllerGrip(0);
    controllerGrip1.current.add(
      controllerModelFactory.createControllerModel(controllerGrip1.current)
    );
    scene.current.add(controllerGrip1.current);

    controllerGrip2.current = renderer.current.xr.getControllerGrip(1);
    controllerGrip2.current.add(
      controllerModelFactory.createControllerModel(controllerGrip2.current)
    );
    scene.current.add(controllerGrip2.current);
  }, []);

  useEffect(() => {
    const handleResize = () => {
      camera.current.aspect = window.innerWidth / window.innerHeight;
      camera.current.updateProjectionMatrix();

      renderer.current.setSize(window.innerWidth, window.innerHeight);
    };

    window.addEventListener("resize", handleResize, false);
    handleResize();

    return () => {
      window.removeEventListener("resize", handleResize, false);
    };
  }, []);

  useEffect(() => {
    let animation;

    const animate = () => {
      animation = requestAnimationFrame(animate);

      renderer.current.render(scene.current, camera.current);
    };

    animate();

    return () => {
      cancelAnimationFrame(animation);
    };
  }, []);

  return <canvas ref={canvasRef} />;
};

render(<App />, document.getElementById("root"));

1 个答案:

答案 0 :(得分:0)

似乎是我处理渲染的罪魁祸首。

在两个场景中同时使用renderer.setAnimationLoop()将正常启动WebXR。

useEffect(() => {
  const animate = () => {
    renderer.current.render(scene.current, camera.current);
  };

  renderer.current.setAnimationLoop(animate);

  return () => {
    renderer.current.setAnimationLoop(null);
  };
}, []);