Search code examples
aframe

A-frame: How to render 2nd camera to canvas


I have a scene with two cameras - a primary camera and plan view camera. How can I render the plan view camera to a plane attached to the primary camera?

I understand that I need to attach a draw-canvas component to the plane as outlined in the Aframe docs - https://aframe.io/docs/0.5.0/components/material.html#canvas-textures. Yet this is as far as I can get with it.

<script>
  AFRAME.registerComponent('draw-canvas', {
  schema: {default: ''},

  init: function () {
    this.canvas = document.getElementById(this.data);
    this.ctx = this.canvas.getContext('2d');

    // Draw on canvas...
   }
  });
</script>

Is there a working example on how to render a camera to a canvas texture with Aframe?

Please see my demo - https://codepen.io/MannyMeadows/pen/OgxwGm


Solution

  • I found a spectator component to resolve this...

    AFRAME.registerComponent('spectator', {
      'schema': {
        canvas: {
          type: 'string',
          default: ''
        },
        // desired FPS of spectator dislay
        fps: {
          type: 'number',
          default: '10.0'
        }
      },
      'init': function() {
        var targetEl = document.querySelector(this.data.canvas);
    
        this.counter = 0;
        this.renderer = new THREE.WebGLRenderer({
          antialias: false,
          alpha: true
        });
    
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.el.object3DMap.camera.aspect = 1;
        this.el.object3DMap.camera.updateProjectionMatrix()
        this.renderer.setSize(targetEl.offsetWidth, targetEl.offsetHeight);
    
        // creates spectator canvas
        targetEl.appendChild(this.renderer.domElement);
      },
      'tick': function(time, timeDelta) {
    
        var loopFPS = 1000.0 / timeDelta;
        var hmdIsXFasterThanDesiredFPS = loopFPS / this.data.fps;
        var renderEveryNthFrame = Math.round(hmdIsXFasterThanDesiredFPS);
    
        if (this.counter % renderEveryNthFrame === 0) {
          this.render(timeDelta);
        }
        this.counter += 1;
      },
      'render': function() {
        this.renderer.render(this.el.sceneEl.object3D, this.el.object3DMap.camera);
      }
    });
    .container {
      position: absolute;
      width: 300px;
      height: 300px;
      top: 10px;
      z-index: 3;
      right: 20px;
    }
    <html>
    
    <head>
      <script src="https://aframe.io/releases/0.6.0/aframe.min.js"></script>
      <script src="//cdn.rawgit.com/donmccurdy/aframe-extras/v3.8.5/dist/aframe-extras.min.js"></script>
    </head>
    
    <body>
      <a-scene stats>
        <a-assets>
          <img id="map-tex" src="https://cdn0.iconfinder.com/data/icons/architecture-construction/128/1-512.png" crossorigin>
        </a-assets>
        <!-- Scene geometry -->
        <a-sphere position="0 1.25 -1" radius="1.25" color="#EF2D5E">
        </a-sphere>
        <a-box position="-1 0.5 1" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box>
        <a-cylinder position="1 0.75 1" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
        <a-plane rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
        <a-sky color="#ECECEC"></a-sky>
        <!-- 1st person camera -->
        <a-entity>
          <a-camera id="primaryCamera" position="0 0 3">
          </a-camera>
          <a-camera position="0 20 3" active="false">
            <a-sphere id="icon" position="0 0 0" radius="0.1" color="#EF2D5E"></a-sphere>
          </a-camera>
        </a-entity>
        <!-- spectator camera -->
        <a-entity camera="active:false; fov:1; far:280" spectator="canvas:#spectatorDiv;" active="false" look-controls="enabled: false" wasd-controls="enabled: false" id="secondaryCamera" position="0 300 0" rotation="-90 0 0">
        </a-entity>
        <!-- mini-map -->
        <a-plane position="0 21 0" rotation="-90 0 0" width="4" height="4" src="#map-tex"></a-plane>
      </a-scene>
      <div class="container" id="spectatorDiv">
      </div>
    </body>
    
    </html>