Double views how to load css2D(only one mesh)

Regarding double views, based on this case, I have made some changes and would like to try displaying one mesh, two cameras, two controls, and two renderers in the scene. I want to add a css2D tag to the mesh, like mesh add (css2D). The problem now is that I cannot fulfill this requirement

How can I implement css2D double views display , thanks. :thinking:

<!DOCTYPE html>
<html lang="en">

<head>
  <title>three.js webgl - multiple views</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  <link type="text/css" rel="stylesheet" href="main.css">
</head>
<style>
  .putCon {
    display: flex;
  }

  .butStyle {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 200px;
    height: 100px;
    background-color: #9e117b;
    text-align: center;
  }
</style>

<body>
  <div class="putCon">
    <div id="container_0"></div>
    <div id="container_1"></div>
  </div>
  <div class="butStyle" id="myButton">
    <h1 style="line-height: 30px;">changePosition</h1>
  </div>

  <script type="importmap">
			{
				"imports": {
					"three": "../build/three.module.js",
					"three/addons/": "./jsm/"
				}
			}
		</script>

  <script type="module">

    import * as THREE from 'three';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
    import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';

    let scene;
    let windowWidth, windowHeight;
    let mesh;
    let frustumSize = 4;

    const views = [
      {
        background: new THREE.Color().setRGB(0.5, 0.5, 0.7, THREE.SRGBColorSpace),
        eye: [0, 1.81, -4.88],
        aspect: window.innerWidth / window.innerHeight,
        renderer: new THREE.WebGLRenderer({ antialias: true }),
        labelRenderer: new CSS2DRenderer(),
        container: document.getElementById('container_0'),
        name: "showLeft",
        top: '-10em',
        distance: 1
      },
      {
        background: new THREE.Color().setRGB(0.5, 0.7, 0.7, THREE.SRGBColorSpace),
        eye: [0, 1.81, -4.88],
        aspect: window.innerWidth / window.innerHeight,
        renderer: new THREE.WebGLRenderer({ antialias: true }),
        labelRenderer: new CSS2DRenderer(),
        container: document.getElementById('container_1'),
        name: "showRight",
        top: '-5em',
        distance: 1
      }
    ];

    init();
    animate();
    updateSize();

    window.onresize = function () {
      updateSize();
    };

    function init() {
      scene = new THREE.Scene();

      const geometry = new THREE.SphereGeometry(0.1, 32, 16);
      const material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
      mesh = new THREE.Mesh(geometry, material);
      mesh.name = "testSphere"
      mesh.position.set(0, 0, 0);
      sphereList.push(mesh);
      scene.add(mesh);

      for (let i = 0; i < views.length; ++i) {
        const view = views[i];
        const camera = new THREE.OrthographicCamera(
          (0.5 * frustumSize * view.aspect) / -2,
          (0.5 * frustumSize * view.aspect) / 2,
          frustumSize / 2,
          frustumSize / -2,
          1,
          100
        );
        camera.position.fromArray(view.eye);
        view.camera = camera;
        view.renderer.setPixelRatio(window.devicePixelRatio);
        view.container.appendChild(view.renderer.domElement);

        view.labelRenderer.setSize(view.container.clientWidth, view.container.clientHeight);
        view.labelRenderer.domElement.style.position = 'absolute';
        view.labelRenderer.domElement.style.top = '0px';
        view.container.appendChild(view.labelRenderer.domElement);

        view.controls = new OrbitControls(camera, view.labelRenderer.domElement);
        view.renderer.setClearColor(view.background);

        // css2D
        const sphereTextDiv = document.createElement('div');
        sphereTextDiv.textContent = view.name;
        sphereTextDiv.style.fontSize = "30px";
        sphereTextDiv.style.fontWeight = "600";
        sphereTextDiv.style.marginTop = view.top;
        const sphereLabel = new CSS2DObject(sphereTextDiv);
        mesh.add(sphereLabel);
        // view.container.appendChild(sphereTextDiv);

      }
    }

    function updateSize() {
      if (windowWidth != window.innerWidth || windowHeight != window.innerHeight) {
        windowWidth = window.innerWidth;
        windowHeight = window.innerHeight;
        for (let i = 0; i < views.length; ++i) {
          const view = views[i];
          const camera = view.camera;

          view.aspect = window.innerWidth / window.innerHeight;

          camera.left = -(0.5 * frustumSize * view.aspect) / 2;
          camera.right = (0.5 * frustumSize * view.aspect) / 2;
          camera.top = frustumSize / 2;
          camera.bottom = -frustumSize / 2;
          camera.updateProjectionMatrix();
          view.renderer.setSize(windowWidth / 2, windowHeight);
          view.labelRenderer.setSize(windowWidth / 2, windowHeight);
        }
      }
    };

    function animate() {
      render();
      requestAnimationFrame(animate);
    }

    function render() {
      for (let i = 0; i < views.length; ++i) {
        const view = views[i];
        view.renderer.render(scene, view.camera);
        view.labelRenderer.render(scene, view.camera);
      }
    }

  </script>

I have no experience with CSS2D/3D renderer, but here is what I think might happens:

  • The sphere is a Three.js object. When it is rendered, it behaves like a 3D stamp – it simply generates an image onto the canvas. If you render it in several views, it will make a 3D stamp in each of them.

  • CSS2D and CSS3D objects are DOM objects. They are browser objects that are inserted in the web page as HTML elements. When you render a CSS2D object it it actually inserts the HTML element in the corresponding place of web page. If you render it again, the same element will be removed from the first place and inserted in the second place.

  • You create two CSS2D labels but you insert them in one mesh. When you draw the mesh in the first view, the mesh is drawn and the labels are placed there. When you draw the second view, the mesh is drawn, but the labels are moved from the first view to the second view.

BTW, is this the intended result:

I did the following changes:

function init() {
   :
   const sphereLabel = new CSS2DObject(sphereTextDiv);
   view.label = sphereLabel; // each label stored in its view
   :
}

function render() {
   :
   mesh.children[0] = view.label; // update mesh to contain only one label
   view.renderer.render(scene, view.camera);
   view.labelRenderer.render(scene, view.camera);
   :
}

Thank you very much for your reply. Your response was in line with my expectations. You solved the problem that I had not solved in a very complex way in the simplest way possible. Thinking about it, I still didn’t grasp the essence of the problem at first. Thank you again.

1 Like