如何在three.js场景中使对象仅对一台摄像机可见

Posted

技术标签:

【中文标题】如何在three.js场景中使对象仅对一台摄像机可见【英文标题】:How to make objects visible to only one camera in a three.js scene 【发布时间】:2016-03-10 01:30:29 【问题描述】:

我使用 three.js 为 3D 场景创建了一个嵌入轨迹球相机控制器。目前,这使用了一个小立方体、一个圆圈和一个放置在世界原点的正交相机。但是,这三个对象在场景本身中仍然可见,如通过主摄像机查看的那样。 (在下面的演示代码中,我特意将立方体设置为 10x10x10,以便清晰可见,但可以做得更小。)

此外,作为主场景一部分的元素通过原点在插图中可见。例如:插图中可以看到属于主场景的AxisHelper。

是否可以在 three.js/webgl 中使某些对象仅对某些摄像机可见?

如果不是,那么一种解决方法是将跟踪球功能所需的对象放置在主摄像头无法看到的深空,但如果可能的话,我更喜欢更纯粹的解决方案。

演示: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>

【问题讨论】:

相关帖子:***.com/questions/16226693/…. 【参考方案1】:

three.js 支持图层。

如果对象和相机共享一个公共层,则该对象对相机可见。默认情况下,相机和所有对象都在第 0 层。

例如,

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

【讨论】:

请记住在用户帮助您时接受答案。谢谢。

以上是关于如何在three.js场景中使对象仅对一台摄像机可见的主要内容,如果未能解决你的问题,请参考以下文章

Three.js-如何限制3D场景中的摄像机视图?

如何将fbxloader加载的摄像头添加到three.js中的场景?

如何在旋转场景中查找对象的全局位置 THREE.JS

three.js引擎基础知识—摄像机场景及渲染器

Three.js - 如何修复控件与最后一个摄像头位置相关的工作?

如何在three.js中的位置半径内获取其他3D对象