| 4 min read

今天我们看下 compositor。顾名思义,compositor 应该是你第一个接触到的 访问属性,我们不妨回顾下官方代码 Client 里面的内容:

function init(bundle, parent, options = {}) {
  const r360 = new ReactInstance(bundle, parent, {
    // Add custom options here
    fullScreen: true,
    ...options,
  });

  // Render your app content to the default cylinder surface
  r360.renderToSurface(
    r360.createRoot('hello_360', { /* initial props */ }),
    r360.getDefaultSurface()
  );

  // Load the initial environment
  r360.compositor.setBackground(r360.getAssetURL('360_world.jpg'));
}

window.React360 = {init};

大概这里我们可以看到 r360.compositor 这么一个东西,大概我们所处理 React-360 背景的音乐,图片都是在这里完成初始化和操作的。

这其中最核心的是 Compositor.js ,里面对外暴露了我们经常需要调用的方法;

import * as THREE from 'three';
import {type Quaternion, type Ray, type Vec3} from '../Controls/Types';
import createRemoteImageManager from '../Utils/createRemoteImageManager';
import type ResourceManager from '../Utils/ResourceManager';
import Cursor from './Cursor';
import Environment, {type PanoOptions} from './Environment/Environment';
import Surface from './Surface';
import type {VideoPlayer} from './Video/Types';
import VideoPlayerManager from './Video/VideoPlayerManager';

const LEFT = 'left';
const RIGHT = 'right';

// 用于 VR 设备中的左右眼模式
const leftCamera = new THREE.PerspectiveCamera();
leftCamera.matrixAutoUpdate = false;
const rightCamera = new THREE.PerspectiveCamera();
rightCamera.matrixAutoUpdate = false;

export default class Compositor {
  _camera: THREE.Camera;
  _canvas: HTMLCanvasElement;
  _cursor: Cursor;
  _cursorVisibility: string;
  _defaultSurface: ?Surface;
  _environment: Environment;
  _frame: HTMLElement;
  _isMouseCursorActive: boolean;
  _renderer: THREE.WebGLRenderer;
  _scene: THREE.Scene;
  _surfaceRoot: THREE.Object3D;
  _surfaces: {[name: string]: Surface};
  _resourceManager: ResourceManager<Image>;
  _videoPlayers: VideoPlayerManager;

  constructor(frame: HTMLElement, scene: THREE.Scene) {
    this._frame = frame;
    this._cursorVisibility = 'auto';
    this._isMouseCursorActive = false;
    this._defaultSurface = null;
    this._surfaces = {};
    this._resourceManager = createRemoteImageManager();
    // 用于对视频播放器进行控制
    this._videoPlayers = new VideoPlayerManager();
	// 设置 three.js 的相机配置
    this._camera = new THREE.PerspectiveCamera(
      60,
      frame.clientWidth / frame.clientHeight,
      0.1,
      2000,
    );
    // 设置 render 这些都是最基础 three.js 创建的对象
    this._renderer = new THREE.WebGLRenderer({
      antialias: true,
    });
    this._canvas = this._renderer.domElement;
    // 设置设备的像素比
    this._renderer.setPixelRatio(window.devicePixelRatio);
    this._renderer.setSize(frame.clientWidth, frame.clientHeight);
    // 将 canvas 添加到容器中
    frame.appendChild(this._renderer.domElement);
    // 在之前 ReactInstance.js 中创建的 scene
    this._scene = scene;
	// 获取环境状态
    this._environment = new Environment(
      this._resourceManager,
      this._videoPlayers,
    );
    // 将用于背景的全景Node添加到场景中进行渲染
    scene.add(this._environment.getPanoNode());

    this._surfaceRoot = new THREE.Object3D();
    scene.add(this._surfaceRoot);

    this._cursor = new Cursor();
    // 将用于鼠标显示的图像添加进来
    scene.add(this._cursor.getMesh());
  }

  setCursorVisibility(vis: string) {
    this._cursorVisibility = vis;
  }

  // 设置背景的全景图
  setBackground(src: string, options: PanoOptions = {}): Promise<void> {
    return this._environment.setSource(src, options);
  }

  // 设置背景的视频
  setBackgroundVideo(handle: string, options: PanoOptions = {}): Promise<void> {
    return this._environment.setVideoSource(handle, options);
  }
  // 创建 视频播放器
  createVideoPlayer(handle: string): VideoPlayer {
    return this._videoPlayers.createPlayer(handle);
  }

  getVideoPlayerManager(): VideoPlayerManager {
    return this._videoPlayers;
  }
	
  // 访问环境状态
  getEnvironment(): Environment {
    return this._environment;
  }

  getCursorVisibility(): string {
    return this._cursorVisibility;
  }

  // 设置鼠标与当前场景的交互
  setMouseCursorActive(active: boolean) {
    if (this._isMouseCursorActive !== active) {
      this._isMouseCursorActive = active;
      this._frame.style.cursor = active ? 'pointer' : 'inherit';
    }
  }

  // 添加表面纹理
  addSurface(name: string, surface: Surface) {
    if (this._surfaces[name]) {
      throw new Error(
        `Cannot add Surface with tag '${name}', a Surface with that name already exists.`,
      );
    }
    this._surfaces[name] = surface;
  }

  showSurface(surface: Surface) {
    this._surfaceRoot.add(surface.getNode());
  }

  getSurface(name: string): ?Surface {
    return this._surfaces[name];
  }

  getDefaultSurface(): Surface {
    if (!this._defaultSurface) {
      this._defaultSurface = new Surface(1000, 600);
    }
    return this._defaultSurface;
  }
 // 获取 Canvas 元素
  getCanvas(): HTMLCanvasElement {
    return this._canvas;
  }
  // 获取 Camera 对象
  getCamera(): THREE.Camera {
    return this._camera;
  }
	
  // 获取 three.js 渲染对象
  getRenderer(): THREE.WebGLRenderer {
    return this._renderer;
  }
  // 重新设置当前画布的大小
  resize(width: number, height: number, pixelRatio: number = 1) {
    this._renderer.setPixelRatio(pixelRatio);
    this._renderer.setSize(width, height, false);
  }

  resizeCanvas(width: number, height: number) {
    this._camera.aspect = width / height;
    this._camera.updateProjectionMatrix();
    this._renderer.setSize(width, height, true);
  }

  prepareForRender(eye: ?string) {
    this._environment.prepareForRender(eye);
  }
  // 每一帧的回调函数
  frame(delta: number) {
    this._environment.frame(delta);
    this._videoPlayers.frame();
  }
  // 更新鼠标轨迹
  updateCursor(rays: ?Array<Ray>, depth: number) {
    if (!rays || rays.length < 1) {
      this._cursor.hide();
      return;
    }
    // TODO: extend to multiple rays
    if (!rays[0].drawsCursor) {
      this._cursor.hide();
      return;
    }
    this._cursor.show();
    const origin = rays[0].origin;
    const direction = rays[0].direction;
    const cameraToCursorX = origin[0] + direction[0] * depth;
    const cameraToCursorY = origin[1] + direction[1] * depth;
    const cameraToCursorZ = origin[2] + direction[2] * depth;
    this._cursor.setPosition(cameraToCursorX, cameraToCursorY, cameraToCursorZ);
  }
  
  // 开始只想渲染
  render(position: Vec3, quat: Quaternion) {
    this.prepareForRender(null);
    this._camera.position.set(position[0], position[1], position[2]);
    this._camera.quaternion.set(quat[0], quat[1], quat[2], quat[3]);

    this._renderer.render(this._scene, this._camera);
  }

  renderSurface(surface: Surface) {
    this._renderer.render(surface.getScene(), surface.getCamera());
  }
 // 渲染 VR 模式
  renderVR(display: VRDisplay, frameData: VRFrameData) {
    const preserveAutoUpdate = this._scene.autoUpdate;
    if (preserveAutoUpdate) {
      this._scene.updateMatrixWorld();
      this._scene.autoUpdate = false;
    }

    const size = this._renderer.getSize();
    this._renderer.setScissorTest(true);
    this._camera.updateMatrixWorld();

    leftCamera.matrixWorldInverse.fromArray(frameData.leftViewMatrix);
    rightCamera.matrixWorldInverse.fromArray(frameData.rightViewMatrix);
    leftCamera.matrixWorld.getInverse(leftCamera.matrixWorldInverse);
    rightCamera.matrixWorld.getInverse(rightCamera.matrixWorldInverse);
    leftCamera.projectionMatrix.fromArray(frameData.leftProjectionMatrix);
    rightCamera.projectionMatrix.fromArray(frameData.rightProjectionMatrix);

    let x = 0;
    const y = 0;
    const w = 0.5 * size.width;
    const h = size.height;

    this.prepareForRender(LEFT);
    this._renderer.setViewport(x, y, w, h);
    this._renderer.setScissor(x, y, w, h);
    this._renderer.render(this._scene, leftCamera);

    x = w;

    this.prepareForRender(RIGHT);
    this._renderer.setViewport(x, y, w, h);
    this._renderer.setScissor(x, y, w, h);
    this._renderer.render(this._scene, rightCamera);

    this._renderer.setViewport(0, 0, size.width, size.height);
    this._renderer.setScissorTest(false);

    if (preserveAutoUpdate) {
      this._scene.autoUpdate = true;
    }
    display.submitFrame();
  }
}

大概通过这些方法你可以非常方便的进行全景图的设置和视频的设置。同时你也可以获取 three.js 的一些基本对象比如 Camera, Renderer 等。

You Can Speak "Hi" to Me in Those Ways