How can I transform a CSS3D rendering plane into a floor in Three.js?

For my project, I need to create a CSS3D rendering plane that acts as a floor with a 3D cube positioned on top of the plane. I have attempted to achieve this by sharing the same scene and camera between the plane and cube in the code below. However, I faced difficulties in aligning the cube on top of the plane accurately due to differences in rotation.

    <!DOCTYPE html>
      <html lang="en">
      <head>
      <title>3JS CODE</title>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
      <style>
        body {
            font-family: Monospace;
            background-color: #f0f0f0;
            margin: 0px;
            overflow: hidden;
        }
      </style>
      </head>
      <body>

    <script src="js/three.js"></script>

    <script src="js/controls/TrackballControls.js"></script>
    <script src="js/renderers/CSS3DRenderer.js"></script>
    <script src="js/libs/stats.min.js"></script>
    <script>

        var HtmlElement = function ( id, x, y, z, rx ) {
            var div = document.createElement( 'div' );
            div.innerHTML = "Hello";
            div.id = id; //'googleMap';
            div.style.width = '1200px';
            div.style.height = '950px';
            div.style.backgroundColor = 'blue';

            var object = new THREE.CSS3DObject( div );
            object.position.set( x, y, z );
            object.rotation.x = rx;
            return object;
        };

    </script>

    <script>

        var container, stats;
        var camera, controls, mapScene, group, renderer1,renderer2;
        var objects = [];

        init();
        animate();

        function init() {

            container = document.createElement( 'div' );
            document.body.appendChild( container );

            camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 5000 );
            camera.position.z = 1000;
            camera.position.set(0.18348775328760136, -334.5971567493426, 800.8398185862801);



            controls = new THREE.TrackballControls( camera );
            controls.rotateSpeed = 3.0;
            controls.zoomSpeed = 2.2;
            controls.panSpeed = 2.8;
            controls.noZoom = false;
            controls.noPan = false;
            controls.staticMoving = true;
            controls.dynamicDampingFactor = 1.3;


            mapScene = new THREE.Scene();

            // scene.background = new THREE.Color( 0xf0f0f0 );

            group = new THREE.Group();

            var mapObject = new HtmlElement( 'googleMap', 0, 0, 240, 270 );
            group.add( mapObject );


            ///////////////
            var geometry = new THREE.BoxGeometry( 200, 200, 200 );
            for ( var i = 0; i < geometry.faces.length; i += 2 ) {
                    var hex = Math.random() * 0xffffff;
                    geometry.faces[ i ].color.setHex( hex );
                    geometry.faces[ i + 1 ].color.setHex( hex );
            }
            var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5 } );
            cube = new THREE.Mesh( geometry, material );
            cube.position.x = 0;
            cube.position.y = -300;
            cube.position.z = 500;

            group.add( cube );


            mapScene.add( group );

            // renderer
            renderer1 = new THREE.CSS3DRenderer();
            renderer1.setSize( window.innerWidth, window.innerHeight );

            renderer1.domElement.style.position = 'absolute';
            renderer1.domElement.style.top = 0;

            container.appendChild( renderer1.domElement );

            renderer2 = new THREE.WebGLRenderer( { antialias: true } );
            renderer2.setSize( window.innerWidth, window.innerHeight );
            container.appendChild( renderer2.domElement );


            stats = new Stats();
            container.appendChild( stats.dom );

            window.addEventListener( 'resize', onWindowResize, false );

            render();
            // initMap();

        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer1.setSize( window.innerWidth, window.innerHeight );
            renderer2.setSize( window.innerWidth, window.innerHeight );
        }

        function animate() {
            requestAnimationFrame( animate );
            render();
            stats.update();
        }

        function render() {         
            controls.update();
            renderer1.render( mapScene, camera );
            renderer2.render( mapScene, camera );
        }

    </script>
</body>
</html>

Check out the output image here.

Answer №1

THREE.CSS3DObject doesn't concern itself with the depth buffer of the WebGLRenderingContext. It has no knowledge of the depth buffer or depth testing. The stacking order of CSS objects can be determined using the z-index.

You're trying to combine two disparate technologies that don't interact in that manner.


However, it is still possible to make them work together.

To achieve this, you need to set the z-index of the CSS3DRenderer element lower than the z-index of the WebGLRenderer element so that the WebGLRenderer appears "in front of" the CSS3DRenderer. Additionally, transparency must be enabled for the WebGLRenderer:

renderer1 = new THREE.CSS3DRenderer();
renderer1.setSize( window.innerWidth, window.innerHeight );
renderer1.domElement.style.position = 'absolute';
renderer1.domElement.style.zIndex = 0;
renderer1.domElement.style.top = 0;
container.appendChild( renderer1.domElement );

renderer2 = new THREE.WebGLRenderer( { antialias: true, alpha:true } );
renderer2.setSize( window.innerWidth, window.innerHeight );
renderer2.domElement.style.position = 'absolute';
renderer2.domElement.style.zIndex = 1;
renderer2.domElement.style.top = 0;
container.appendChild( renderer2.domElement );

Then, you need to ensure that the WebGLRenderer handles the CSS3DObject. While this isn't technically supported, there are workarounds available.

One possible solution is to render a transparent plane in the WebGLRenderer which matches the size and position of the CSS3DObject:

var HtmlElement = function ( id, x, y, z, w, h ) {
    // implementation details omitted for brevity
};

var WebGlObject = function ( x, y, z, w, h ) {
    // implementation details omitted for brevity
};

var mapObject = HtmlElement('googleMap', 0, 0, 0, 800, 800);
var planeMesh = WebGlObject(0, 0, 0, 800, 800);

Check out the example below, which is based on your original code:

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

Unable to modify div style using a JS function

I am attempting to show different divs based on the button clicked, with all starting with a display style of "none" except for one default div called "atualizacoes". After clicking a button, all divs should be set to display="none", and then the specific ...

Boundaries on Maps: A guide to verifying addresses within a boundary

User provides address on the website. If the address falls within the defined boundary, it is marked as "Eligible". If outside the boundary, labeled as "Ineligible". Are there any existing widgets or code snippets available to achieve this functio ...

Performing real-time arithmetic calculations on dynamic text input fields using AngularJS

In the process of creating a dynamic form, the number of textboxes is determined by the situation at hand. Each textbox is assigned an arithmetic formula, which is obtained from a database table. The structure of my form will be as follows: <div ng-ap ...

What is preventing me from passing arguments containing a hyphen to my node module through the command line?

Whenever I run my module using a script in package.json, I encounter an issue with passing command line arguments. Specifically, only the arguments that do not begin with a dash (-) are being transmitted successfully: npm run myscript -one two The argume ...

Guide to implementing dynamic conditional rendering in Vue.js loops (utilizing v-if within v-for)

I am currently working on a table component in a .vue file where I want to display icons based on the direction of the order clicked. For example: <th v-for="(column, index) in columns" :key="index" @click="sort( index )"> <span& ...

Invoke a jQuery method in a dynamic manner

Is it possible to achieve the following? var todo = "text"; $this.eval(todo).split(/\b[\s,\.-:;]*/).length; The desired function is: $this.text().split(/\b[\s,\.-:;]*/).length; I'm struggling to find a solution... Ca ...

Wait until the ajax request is fully completed before executing the javascript function

Is there a way to ensure that a JavaScript function is only executed after a jQuery ajax call has fully completed, including the success and error events? In other words, I want the function to run only after the incoming data from the ajax call has been s ...

Radiate luminosity from an item

I'm creating a captivating Three.js scene with stars objects and I want to give them a radiant "glow". When I say glow, I mean I want them to emit actual light rather than just having a simple "halo" effect around them. I attempted placing a PointLi ...

Validating the value of a Material UI TextField

I am currently working with a TextField that has some validation requirements: minValue = 1 maxValue = 10 These validations are effective when using the arrows in the TextField, however, if I directly type into it, I am able to enter any number. How can ...

The argument for the e2e test is invalid because it must be a string value

Currently, I am conducting an end-to-end test for an Angular application using Protractor. However, I encountered an error while running the test for a specific feature file. The UI value that I am checking is - WORKSCOPES (2239) Below is the code snippe ...

Use jQuery to set the onclick attribute for all elements rather than relying on inline JavaScript

I am currently facing a challenge with converting inline JS to jQuery. My goal is to eliminate all inline onclick events and instead target them by class. HTML - checkbox <td class="center"> <?php if ($product['selected']) { ?> ...

How can one ensure the preservation of array values in React?

I'm struggling to mount a dynamic 'select' component in React due to an issue I encountered. Currently, I am using a 'for' loop to make API calls, but each iteration causes me to lose the previous values stored in the state. Is t ...

Pass the chosen option to PHP using Ajax within the same webpage, utilizing HTML

My Goal: I am working on a HTML/PHP page that displays the home page of my website. One section on this page is dedicated to highscores, with data retrieved from a database. Users can use a Select box to choose how the highscores are sorted. View the high ...

Identifying and retrieving elements in JavaScript

I'm trying to extract the unique selector path of an HTML element using a script I found at this source. The generated path appears as follows: html>body>section:eq(3)>ul>li:eq(1)>div Can someone guide me on how to use JavaScript or j ...

Filtering DataGrid columns with an external button in Material-UI: A step-by-step guide

Imagine having a datagrid table structured as shown below: import * as React from 'react'; import { DataGrid, GridToolbar } from '@mui/x-data-grid'; import { useDemoData } from '@mui/x-data-grid-generator'; const VISIBLE_FIEL ...

Anomalous Snackbar and Alert Interactions

Why am I getting an error with this code snippet: import Snackbar from "@mui/material/Snackbar"; import MuiAlert from "@mui/material/Alert"; const Alert = ({children}) => <MuiAlert>{children}</MuiAlert> <Snackbar ope ...

Unknown Parameters Issue with Vue.js Router Links

Within my Vue.js project, I am utilizing params in my navigation.vue component to pass data onto the next page for dynamic routing purposes. Below is an example of how I am using this: <router-link tag="p" :to="{name: 'Main', ...

Tips for hiding the overflow scrollbar on Microsoft Chrome when there is no content to scroll

Looking for a solution to hide scroll bars in Microsoft Chrome, but only when there is no content to scroll? The current div and styles always show the horizontal and vertical scroll bars, even when the height exceeds the content. <div style="backgroun ...

When attempting to build and run in a production environment, an error message stating "Module './static' cannot be located" is displayed

I've been working on setting up the Forge-RCDB project for production environment. Using Windows Powershell, I executed the following commands: npm run build-prod $env:NODE_ENV ="production" npm start However, I encountered the following errors afte ...

What is the process for converting this Greasemonkey code to JavaScript specifically for Android devices?

Having trouble loading a page and running a JavaScript code on it? Don't worry, you're not alone. I came across a Greasemonkey script that does the trick, but now I'm struggling to make it work on Android. It's probably because of my la ...