Tips for ensuring that objects are only seen by a specific camera in a three.js environment

Utilizing three.js, I have developed an inset trackball camera controller for a 3D scene. The current setup involves a tiny cube, a circle, and an orthographic camera positioned at the world's origin. However, these elements remain visible in the scene when viewed through the main camera. In the demo code provided below, you can see that the cube has been intentionally enlarged to make it conspicuous, but it could be scaled down significantly.

There is also an issue where certain objects from the main scene passing through the origin are visible in the inset view. For instance, the AxisHelper linked to the primary scene can also be observed in the inset.

My query is whether it is feasible in three.js/webgl to render specific objects visible only to particular cameras?

If not supported by the framework, one workaround would involve relocating the objects essential for the trackball functionality far away into deep space where they are out of sight of the main camera. Nonetheless, I am inclined towards finding a more straightforward solution if there exists one.

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>

Answer №1

three.js offers layer functionality.

In three.js, an object will be visible to a camera if they are assigned the same layer. By default, both the camera and objects are in layer 0.

For instance,

camera.layers.enable( 1 ); // camera can now see layers 0 and 1
camera.layers.set( 1 ); // camera will only see layer 1

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

This feature is available in three.js release r.75

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using JSP to send variables from an external Javascript file

After creating a timer function, I am looking to display the results on a different page. The setup involves a JSP file calling functions from a separate JS file in order to output the information to another screen: Instructions in the JSP file: <butt ...

JavaScript ACTING UP -> CROSS-ORIGIN RESOURCE ACCESS ERROR

After extensive research and troubleshooting, it dawned on me that the issue was not with JavaScript itself. Instead, I was facing a cross origin resource exception, which occurred because the ajax request was unable to access my server script due to lac ...

When taking a vertical picture on my iPhone, my Vue component does not function properly

Having trouble with displaying recently uploaded images on a form. If the image is taken from a phone, it may have an exif property that browsers do not handle correctly, causing the picture to appear flipped or sideways. Upside down photo To fix this is ...

Making an AJAX POST request using jQuery to send the current date and time to an ASP

I am working with an ASP.NET Web API endpoint where I need to send POST data. Suppose I want to post the following information: var myObject = { name: "Alice Johnson", age: "29", accountCreated: "CURRENT DATE TIME" }; What is the JavaScript equivalent o ...

How to manipulate a jade element with JavaScript

How can I capture the value of an input tag in real time and store it in a JSON object? In my Jade file, this is what it looks like: extends layout block content h1 todos #task <input type="text" id="task" name="task"/> #todos for todo ...

What is preventing me from utilizing the Jquery show() method within a Div's onclick event without incorporating data-toggle="dropdown"?

I am facing an issue with a div that should act like a button. When this div is clicked, another hidden div is supposed to be displayed. However, the behavior is not as expected. I have included the data data-toggle="dropdown" attribute in the div acting a ...

Menu design with child element creatively integrated into parent element

For my mobile project, I am working on creating a window blind effect menu. The process is quite simple: Clicking on the menu icon moves the bar to the top while the menu drops down Upon selecting an item, the menu smoothly "rolls" up in a window blind s ...

"Typescript throws a mysterious 'Undefined value' error post-assignment

I'm currently working on a task to fetch my customer's branding information based on their Id using Angular. First, I retrieve all the customer data: this.subscription = this.burstService.getBurst().subscribe(async(response) => { if (r ...

Issue with moving files after successful upload - encountering ENOENT Error

I am encountering a frustrating issue while trying to transfer files from the temp directory to the profile_pictures directory. This seemingly straightforward task has proven to be quite challenging as I have been stuck on it for an hour! The process shou ...

Having issues with the FixedHeader plugin

UPDATE: After discussing with the creator of FixedHeader, it appears that the plugin is not compatible with my setup. The issue seems to be related to my table being nested within a tab, causing various problems as shown in the screenshots from my previous ...

Update changes simultaneously while navigating away using AJAX onreadystatechange

When a user clicks on a link, I want to initiate an AJAX request to save the current page's content and navigate away simultaneously. Typically, when the window is attempting to navigate away, all AJAX requests are interrupted prematurely. This could ...

Looking to construct a multidimensional array in JavaScript for efficiently reading arrays along with their corresponding options?

I am looking to create a multidimensional array in JavaScript to store questions along with their related options. Here is my current code snippet: demoArray[i] = new Array(); var id=results.rows.item(i).del_ques_id; demoArray[i]["question"] = results.row ...

Is `send()` considered an anonymous function?

I am testing ajax on a local server, but encountering an issue where the console is showing that send() is being identified as an anonymous function and I am receiving a blank document. The console displays: XHR finished loading: GET "http://localhost/js ...

Proper utilization of ngIf in conjunction with mat-cell

I am attempting to show a specific value only if the item possesses a certain property, but I keep seeing [object Object] instead. Here is my current method: <ng-container matColumnDef="name"> <th mat-header-cell *matHeaderCellDe ...

What steps can be taken to hide empty items in a list from being shown?

When I map over an array of books to display the titles in a list, I encounter an issue with the initial empty string value for the title. This causes the list to render an empty item initially. Below is my book array: @observable books = [ {title:" ...

Generating a JSON response. NodeJS is sending a response with no content

I'm facing an issue with my code where I need to retrieve data from the database multiple times and send a response containing this data. However, for some reason, the response is coming back empty. var express = require('express'), router ...

The premature reveal of the back side in the Kendo UI flip effect on Internet Explorer

Currently, I am in the process of creating a BI dashboard for a business application using JavaScript Kendo UI version v2014.1.416. Unfortunately, I have encountered an issue with certain visuals while testing on IE11. I must mention that due to practical ...

Here is a unique version of the text: "Adding an Ajax variable from a loop to a <a href='' ></a> tag: A Simple Guide"

Hello all, I am new to Ajax and I am using it to retrieve values from my database in the form of an object array from a separate PHP file. I am then displaying them in a table, which is working fine. However, I want to create a link and append the ID obtai ...

What is the best way to modify the values of this variable across different modules?

Within my module called "bot.js", there is a continuous process where messages are checked and assigned to a variable called db_users. As my application is executed from "app.js" and I am passing functions that update db_users regularly, I am unsure how to ...

React default class name ... Before conditional operator takes effect

import './App.css' import {Button} from "antd" <Button shape="round" className={ istrue === "itstrue" ? "trueclass" : "trueclass active" } > TEXT </Button> ...