Using Three.js to Project Objects onto a Plane that is Perpendicular to the Camera Orientation

I have a basic setup in Three.js involving some planes. Currently, when clicked, the planes move to random positions.

Instead of the random movement, I want the planes to transition into a new grid that is perpendicular to the camera, aligning the projected grid's x and y axes parallel to the screen's x and y axes.

Here is the current scene:

// Scene setup
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);

// Camera setup
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.01, 10000);

// Position the camera
camera.position.set(51.389, -451.056, 839.455);
var rotObjectMatrix = new THREE.Matrix4();
var q = {_x: 0.0184, _y: -0.2122, _z: 0.9770, _w: -0.0081};
rotObjectMatrix.makeRotationFromQuaternion(q);
camera.quaternion.setFromRotationMatrix(rotObjectMatrix);
camera.up.set(0.00806, -0.91008, -0.41432);

// Renderer setup
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Controls setup
var controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.target.set(19.053, -111.316, 93.996);

// Lights setup
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);

// Render function
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  controls.update();
};

// Grid creation
var grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000);
grid.material.opacity = 0.2;
grid.material.transparent = true;
grid.rotation.x = -Math.PI;
scene.add(grid);

// Adding planes
planes = [];
for (var i=0; i<2**10; i++) {
  var geometry = new THREE.PlaneGeometry(20, 20, 32);
  var material = new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide});
  var plane = new THREE.Mesh(geometry, material);
  var x = ((i % 2**5) * 40) - (2**5 * 40)/2;
  var z = (Math.floor(i/2**5) * 40) - (2**5 * 40)/2;
  plane.position.set(x, 0, z);
  scene.add(plane);
  planes.push(plane);
}

// Transition for planes on click
document.querySelector('body').addEventListener('click', function() {
  planes.forEach(function(plane) {
    // Placeholder
    plane.position.set(
      Math.random() * 500 - (Math.random() * 500)/2,
      0,
      Math.random() * 500 - (Math.random() * 500)/2,
    )
  })
})

render();
html, body { width: 100%; height: 100%; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

The following code snippet gets close to the desired projection but doesn't tilt the planes to be perpendicular to the camera:

planes.forEach(function(plane) {
  // Approach similar to the discussed projection
  plane.position.set(
    plane.position.x,
    plane.position.z,
    0,
  )
})

If anyone has suggestions on achieving the described projection or any other input, it would be greatly appreciated!

Answer №1

Here's a handy shortcut to avoid lengthy math calculations: Organize your planes into a Group and then use Group.lookAt(camera.position) to align them all towards the camera simultaneously.

// Simplifying the setup process
// Allowing planes to follow the camera's movements with ease
html, body { width: 100%; height: 100%; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

For a fun twist, if you decide to shuffle the plane positions randomly, simply remember to revert them back to their original x,y,0 coordinates, and they'll automatically align themselves towards the camera.

UPDATE: Don't forget to mirror the adjustments made to camera.up and controls.target when modifying them. This step is crucial for the method to function correctly with planeGroup.

Explore the documentation on the .lookAt() method for further insights

Answer №2

When it comes to handling sprites, Threejs has you covered with its convenient built-in class.

Check out THREE.Sprite

Simply utilize this class tailored to your needs.

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

Tips for choosing or moving to an iframe

What is the best method for choosing or transitioning to an Iframe (as the currently targeted document) using Selenium webdriver in Firefox? Are there alternative approaches for selecting an iframe with or without webdriver? driver.switchTo.frame("Frame ...

React - assigning a value to an input using JavaScript does not fire the 'onChange' event

In my React application with version 15.4.2, I am facing an issue where updating the value of a text input field using JavaScript does not trigger the associated onChange event listener. Despite the content being correctly updated, the handler is not being ...

The Zip file generated in memory is experiencing corruption

I'm encountering an issue while attempting to serve a zip file generated in memory by Flask to a JavaScript front-end. However, the downloaded file appears corrupted and I am unsure of what mistake I may be making. @app.route('/route') def ...

Creating a versatile "add new entry" form in Angular that appends a new record to the parent scope

In my Angular application setup, I have an "Edit Invitation" state where a single invitation is scoped. This invitation contains a list of guests controlled by a guestList controller and iterated over in the view. <div ng-controller="guestListCtrl as g ...

Showing attributes of models using Sequelize and Handlebars

As a novice in the world of programming, I am currently immersed in a website project where users can write and post articles. One crucial aspect I am focusing on is displaying the history of articles written by each user on their account page. Despite uti ...

Attach a click event listener to content loaded through AJAX using only JavaScript, without relying on jQuery

I'm curious about how to attach a click event to an element that will be added later through an ajax call. I know jQuery can handle this like so: $(document).on('click', '.ajax-loaded-element' function(){}); However, I'm not ...

Using JavaScript to create a JSON object within a table

Currently, I am facing a challenge in creating a JSON object from an HTML table using JavaScript. While I can successfully retrieve the values of each cell in JavaScript, the issue lies in organizing and retrieving them as per my requirements. Here is the ...

Function that yields promise result

I need help figuring out how to make this recursive function return a promise value. I've attempted various approaches, but they all resulted in the search variable ending up as undefined. public search(message: Message) { let searchResult: strin ...

Learn the process of updating a nested document within an array in MongoDB

I have a data structure as shown below: { "name":"xxxxxx", "list":[ { "listname":"XXXXX1", "card":[ { "title":"xxxxxx", "descip":"xxxxxxx ...

Step-by-step guide to rapidly resolve all issues in VS Code using TypeScript

After extensive searching in VS code, I have not been able to find a quick fix all solution in the documentation or plugins. Is this feature actually non-existent, or is it possible that I am overlooking a keybinding? (I am currently utilizing typescript s ...

Tips for creating a TypeScript-compatible redux state tree with static typing and immutability:

One remarkable feature of TypeScript + Redux is the ability to define a statically typed immutable state tree in the following manner: interface StateTree { readonly subState1: SubState1; readonly subState2: SubState2; ...

Can't seem to res.send using Express framework

Hello, I'm encountering an issue when trying to send a response using Express. I've seen suggestions in other questions that changing the variables err and res may resolve this problem, but it hasn't worked for me. router.post('/checkP ...

Tips for creating read-only checkboxes with multipledropdown.js in Angular.js

I am trying to make all checkboxes in a multiple dropdown list readonly using Angular.js with the multipledropdown.js plugin. My code snippet is as follows: <div class="col-md-6" ng-show="sub_sub_menu"> <div class="input-group bmargindiv1 col-md- ...

Ways to determine the count of arrays within a JSON data structure

Displayed below is a JSON object with multiple arrays. The goal is to extract the number and name of each array within this object. Since the object is dynamically generated, the quantity and names of the arrays are unknown. In this example, there are tw ...

Access several different dynamic URLs through a single Ajax request

I have a scenario where I am showing multiple dynamic links within the same div. The first link works perfectly fine with ajax loading the content, but the subsequent links do not work. How can I make it so that the content of all links can be loaded withi ...

What is the proper method for utilizing colspan within the footerData of a jqGrid?

Are you looking to customize the footer of your jqgrid as shown in the example below? I am trying to set up a custom footer for my jqgrid similar to the one displayed above. I have already enabled the footerrow:true option and used $self.jqGrid("footerDat ...

Incorporating jQuery to Load Content into a DIV while preserving the original JavaScript

I am attempting to implement the following <script> $(document).ready( function() { var data = 'testing' $("#about").on("click", function() { $("#main-content").load("/about.html"); ...

Having trouble importing d3.js and typescript in IntelliJ?

Encountering errors in browsers' console when utilizing d3.select() in typescript code. Despite trying alternative methods like d3-timer.now(), the issue persists. As a newcomer to typescript, I am utilizing intelliJ Ultimate 2019.1. Through npm, I h ...

The validation feature is ineffective when used with Material UI's text input component

I need a function that can validate input to make sure it is a number between 1 and 24. It should allow empty values, but not characters or special symbols. function handleNumberInput(e) { const re = /^[0-9\b]+$/; const isNumber = re.test(e.target.val ...

Creating a real-time clock in a single line console log format using NodeJS

To create a live clock in a single line display within a browser, we can utilize setInterval and manipulate the content inside an HTML element like so: var span = document.getElementById('span'); function time() { var d = new Date ...