Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make objects visible to only one camera in a three.js scene

I have created an inset trackball camera controller for a 3D scene, using three.js. Currently, this uses a tiny cube, a circle and an orthographic camera placed at the origin of the world. However, these three objects are still visible in the scene itself, as viewed through the main camera. (In my demo code below, I have deliberately made the cube 10x10x10 so that it is clearly visible, but it could be made much smaller.)

Also, elements that are part of the main scene that pass through the origin are visible in the inset. For example: the AxisHelper that belongs to the main scene can be seen in the inset.

Is it possible in three.js/webgl to make certain objects visible to only certain cameras?

If not, then a workaround would be to place the objects required for the trackball feature way off out into deep space, where the main camera cannot see them, but I would prefer a purer solution if possible.

Demo: http://codepen.io/anon/pen/MKWrOr

<!DOCTYPE html>
<html>

<head>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.js"></script>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>
<body>

<div id="WebGL-output"></div>

<script>
function init() {
  var scene = new THREE.Scene()
  var renderer = new THREE.WebGLRenderer()
  var camera
  var cameras = []

  var WIDTH = window.innerWidth
  var HEIGHT = window.innerHeight

  ;(function createPerspectiveCamera(){
    var FOV = 45
    var ASPECT = WIDTH / HEIGHT
    var NEAR = 1
    var FAR = 360
    camera = new THREE.PerspectiveCamera(FOV, ASPECT, NEAR, FAR)

    camera.position.x = 100
    camera.position.y = 100
    camera.position.z = 100
    camera.viewport = { x: 0, y: 0, width: WIDTH, height: HEIGHT }
    camera.lookAt(scene.position)
    cameras.push(camera)
  })()

  ;(function initializeRenderer(){
    renderer.setClearColor(new THREE.Color(0xEEEEFF))
    renderer.setSize(WIDTH, HEIGHT)
    renderer.autoClear = false;

    document.getElementById("WebGL-output").appendChild(renderer.domElement)

    ;(function render() {
      var viewport
      renderer.setViewport( 0, 0, WIDTH, HEIGHT );
      renderer.clear();

      cameras.forEach(function (camera) {
        viewport = camera.viewport // custom property
        renderer.setViewport(
          viewport.x
        , viewport.y
        , viewport.width
        , viewport.height
        )
        renderer.render(scene, camera)
      })

      requestAnimationFrame(render)
    })()
  })()

  ;(function createCameraController(){
    var viewport = {
      x: WIDTH - 100
    , y: HEIGHT - 100
    , width: 100
    , height: 100
    }
    var circle = {
      x: WIDTH - 50
    , y: 50
    , radius: 50
    }
    var settings = {
      viewport: viewport
    , circle: circle
    }
    addCameraController(scene, camera, cameras, settings)
  })()

  // Something to look at
  scene.add(new THREE.AxisHelper(70))
}

function addCameraController(scene, camera, cameras, settings) {
  var controlCamera
  var viewport = settings.viewport

  // For mouse interactions
  var centreX = settings.circle.x
  var centreY = settings.circle.y
  var radius = settings.circle.radius
  var radius2 = radius * radius
  var rotationMatrix = new THREE.Matrix4()
  var pivotMatrix = new THREE.Matrix4()
  var startMatrix = new THREE.Matrix4()
  var start = new THREE.Vector3()
  var end = new THREE.Vector3() 
  var angle

  camera.matrixAutoUpdate = false /** takes control of main camera **/

  ;(function createControlCameraCubeAndCircle(){
    var side = 10
    var radius = Math.sqrt(side/2 * side/2 * 3)

    ;(function createCamera(){
      controlCamera = new THREE.OrthographicCamera(
       -radius, radius
      , radius, -radius
      , -radius, radius
      );
      controlCamera.viewport = viewport
      controlCamera.rotation.copy(camera.rotation)

      // If matrixAutoUpdate is set immediately, the camera rotation is
      // not applied
      setTimeout(function () {
        controlCamera.matrixAutoUpdate = false
      }, 1)

      scene.add(controlCamera)
      cameras.push( controlCamera )
    })()

    ;(function createCompanionCube(){
      var cube = new THREE.Object3D()
      var cubeGeometry = new THREE.BoxGeometry( side, side, side )

      var lineMaterial = new THREE.LineBasicMaterial({
        color: 0xffffff
      , transparent: true
      , opacity: 0.5
      })

      var faceMaterial = new THREE.MeshPhongMaterial({
        color: 0x006699
      , emissive: 0x006699
      , shading: THREE.FlatShading
      , transparent: true
      , opacity: 0.2
      })

      cube.add(
        new THREE.LineSegments(
          new THREE.WireframeGeometry( cubeGeometry )
        , lineMaterial
        )
      )
      cube.add(
        new THREE.Mesh(
          cubeGeometry
        , faceMaterial
        )
      )

      // cube.add(new THREE.AxisHelper(radius))
      scene.add(cube);
    })()

    ;(function createCircle(){
      var circleGeometry = new THREE.CircleGeometry( radius, 36 );
      var material = new THREE.MeshBasicMaterial( {
        color: 0xccccff
      } );
      var circle = new THREE.Mesh( circleGeometry, material );
      controlCamera.add( circle );
      circle.translateZ(-radius)
    })()
  })()

  window.addEventListener("mousedown", startDrag, false)

  function startDrag(event) {
    var x = event.clientX - centreX
    var y = centreY - event.clientY
    var delta2 = x * x + y * y
    if (delta2 > radius2) {
      return
    }

    var z = Math.sqrt(radius2 - delta2)
    start.set(x, y, z)

    window.addEventListener("mousemove", drag, false)
    window.addEventListener("mouseup", stopDrag, false)

    function drag(event) {     
      var delta
      x = event.clientX - centreX
      y = centreY - event.clientY
      delta2 = x * x + y * y

      if (delta2 > radius2) {
        // constrain to adge of sphere
        delta = Math.sqrt(delta2)
        x = x / delta * radius
        y = y / delta * radius
        z = 0
      } else {
        z = Math.sqrt(radius2 - delta2)
      }

      end.set(x, y, z)
      angle = start.angleTo(end)
      start.cross(end).normalize()

      rotationMatrix.makeRotationAxis(start, -angle)
      controlCamera.matrix.multiply(rotationMatrix)
      controlCamera.matrixWorldNeedsUpdate = true

      rotationMatrix.extractRotation(camera.matrixWorld)
      start.applyMatrix4(rotationMatrix).normalize()
      rotationMatrix.makeRotationAxis(start, -angle)
      camera.applyMatrix(rotationMatrix)
      camera.matrixWorldNeedsUpdate = true

      start.copy(end)
    }

    function stopDrag(event) {
      window.removeEventListener("mousemove", drag, false)
      window.removeEventListener("mouseup", stopDrag, false)      
    }
  }
}

window.onload = init
</script>
</body>
</html>
like image 658
James Newton Avatar asked Dec 05 '15 00:12

James Newton


Video Answer


1 Answers

three.js supports layers.

An object is visible to a camera if the object and the camera share a common layer. The camera and and all objects are by default in layer 0.

For example,

camera.layers.enable( 1 ); // camera now sees default layer 0 and layer 1
camera.layers.set( 1 ); // camera now sees only layer 1

mesh.layers.set( 1 ); // mesh is in layer 1

three.js r.75

like image 74
WestLangley Avatar answered Oct 19 '22 14:10

WestLangley