Implementing HTML into three js

Hello everybody. I’m new to Three Js, but I have managed to set up the view you see on the image.
I want to have individual HTML files displayed on all of the screens. The HTML will only be displayed when the object is clicked.
My question is, how would you guys recommend me to handle this? Should I work with CSS3dRenderer, or can you set up HTML files in a 3D world, where they are placed in the position of the screen, and if so how do you do that?
Note: OrbitControls are not enabled, however, I am moving the camera when the different machines are clicked. I don’t know if that affects the answer.


I hope you can help :slight_smile:

CSS3DRenderer is a valid choice for this use case. You can try to embed external web site to how YouTube videos are included via iFrames in: three.js css3d - youtube

I have tried looking at this example: Mixing HTML and WebGL w/ occlusion - CodeSandbox
However, I have looked at it for 30 minutes, and I still don’t understand how it works, and I don’t think I can implement it into my code

I have now tried all kinds of solutions for 2 hours, and I think I might be getting somewhere.
I have managed to set up a CSS3DRenderer() correctly, which seems to be working:

import * as THREE from "three"
import Experience from "./experience.js";

import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js"
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js"
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js"
import { CSS3DRenderer } from "three/examples/jsm/renderers/CSS3DRenderer.js"

export default class Renderer {
  constructor() {
    this.experience = new Experience()
    this.sizes = this.experience.sizes
    this.scene = this.experience.scene
    this.canvas = this.experience.canvas
    this.camera = this.experience.camera

    this.setRenderer()
  }

  setRenderer() {
    THREE.ColorManagement.enabled = false
    this.renderer = new THREE.WebGLRenderer({
      canvas: this.canvas,
    });

    this.renderer.physicallyCorrectLight = true
    this.renderer.outputColorSpace = THREE.SRGBColorSpace
    this.renderer.toneMapping = THREE.ReinhardToneMapping
    this.renderer.toneMappingExposure = 1
    this.renderer.shadowMap.enabled = true
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
    this.renderer.setSize(this.sizes.width, this.sizes.height)
    this.renderer.setPixelRatio(this.sizes.pixelRatio)

    //Unreal Bloom
    this.target = new THREE.WebGLRenderTarget(this.sizes.width, this.sizes.height, {
      type: THREE.HalfFloatType,
      format: THREE.RGBAFormat,
      colorSpace: THREE.SRGBColorSpace,
    })
    this.target.samples = 8

    this.renderScene = new RenderPass(this.scene, this.camera.perspectiveCamera)
    this.bloomPass = new UnrealBloomPass( new THREE.Vector2(this.sizes.width, this.sizes.height), 1, 1.5, 1 )
    this.composer = new EffectComposer(this.renderer, this.target)

    this.composer.addPass( this.renderScene )
    this.composer.addPass( this.bloomPass)

    //CSS3DRenderer
    this.css3DRenderer = new CSS3DRenderer()
    this.css3DRenderer.setSize(this.sizes.height, this.sizes.width)
    this.css3DRenderer.domElement.style.position = 'absolute'
    this.css3DRenderer.domElement.style.top = '0px'
    document.body.appendChild( this.css3DRenderer.domElement )
  }

  resize() {
    this.renderer.setSize(this.sizes.width, this.sizes.height)
    this.renderer.setPixelRatio(this.sizes.pixelRatio)
  }

  update() {
    this.composer.render()
    this.css3DRenderer.render( this.scene, this.camera.perspectiveCamera)
  }
}

I have then tried to add a div element to the screen (aboutMeScreen), but for some reason, I can’t see it in the view. I have changed the position and looked for it using OrbitControls, but it is not visible. So I believe I have miscoded something and I therefore can’t see the div. Here is my code for setting up the div to the screen:

import * as THREE from "three"
import Experience from "../experience.js";
import GSAP from "gsap"
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';

export default class Room {
  constructor() {
    this.experience = new Experience()
    this.scene = this.experience.scene
    this.resoruces = this.experience.recources
    this.renderer = this.experience.renderer.renderer
    this.room = this.resoruces.items.room
    this.actualRoom = this.room.scene

    this.setModel()
  }

  setModel() {
    console.log(this.actualRoom.children)
    this.actualRoom.children.forEach((child) => {
      child.castShadow = true;
      // child.material.toneMapped = false

      if (child instanceof THREE.Group) {
        child.children.forEach((groupChild) => {
          groupChild.castShadow = true
          groupChild.material.toneMapped = false
        })
      }

      //Set tonemapp to neon lights
      if (child.name === "About_Text" || child.name === "Alexander_Text") {
        child.material.toneMapped = false
      }

      //Set up Brick Wall
      if (child.name === "Painted_Wall001") {
        child.material.map.rotation = 90 * Math.PI / 180
        child.material.map.repeat.set(1.7, 1.7)
      }
    });
    
    this.setupScreens()

    this.scene.add(this.actualRoom)
    this.actualRoom.scale.set(0.08, 0.08, 0.08)
  }

  //Screens
  setupScreens() {
    var pos = new THREE.Vector3(2, 2, 2);
    
    var div = document.createElement( 'div' );
    div.className = 'label';
    div.textContent = 'SDJCOAJSFKASJKFHASKÆJHFKJASHGKLJASBHGJKLBASGKJBASJKGHBASJKBGJKASBGJKABSGJKBASJKGBAKSJBGJKASBGJKAS';
    div.fontSize = "20"
    var label = new CSS3DObject( div );
    label.position.copy(pos);
    label.rotation.y = Math.PI * 0.5;
    label.scale.set(0.025, 0.025, 1);

    var aboutMeScreen = this.actualRoom.children[5].children[4]
    aboutMeScreen.add( label );
    console.log(aboutMeScreen)
  }

  resize() {
    
  }

  update() {
    this.raycast()
  }
}

I am starting to get a bit crazy over this problem… hope you can help

Ok. I’m close to my breaking point. I have been trying for about 7 hours now, and my screen looks rather weird…

I have imported a div with the letter “1” and a border, which you can see in the middle of the screenshot. But for some reason, the div gets cut in half at the center! But if I move the div just a little bit to the right you can see it fully.

I am completely lost and I think I have tried everything, so I will REALLY appreciate it if someone could help. This has driven me crazy all night now.
Here is my code:

Rendering the scene:

import * as THREE from "three"
import Experience from "./experience.js";

import { CSS3DRenderer } from "three/examples/jsm/renderers/CSS3DRenderer.js"
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

export default class Renderer {
  constructor() {
    this.experience = new Experience()
    this.sizes = this.experience.sizes
    this.scene = this.experience.scene
    this.canvas = this.experience.canvas
    this.camera = this.experience.camera

    this.setRenderer()

    var controls = new OrbitControls(this.camera.perspectiveCamera, this.labelRenderer.domElement);
  }

  setRenderer() {
    this.renderer = new THREE.WebGLRenderer({antialias: true});
    this.renderer.setSize(this.sizes.width, this.sizes.height); //Note: This is the same as window.innerWidth and window.innerHeight
    document.body.appendChild(this.renderer.domElement);

    this.labelRenderer = new CSS3DRenderer();
    this.labelRenderer.setSize( this.sizes.width, this.sizes.height );
    this.labelRenderer.domElement.style.position = 'absolute';
    this.labelRenderer.domElement.style.top = '0px';
    this.labelRenderer.domElement.style.width = '100%';
    document.body.appendChild( this.labelRenderer.domElement );

  resize() {
    this.renderer.setSize(this.sizes.width, this.sizes.height)
    this.renderer.setPixelRatio(this.sizes.pixelRatio)
  }

  update() {
    this.renderer.render( this.scene, this.camera.perspectiveCamera )
    this.labelRenderer.render( this.scene, this.camera.perspectiveCamera)
  }
}

Here I am adding the glb file and the div:

import * as THREE from "three"
import Experience from "../experience.js";
import GSAP from "gsap"
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';

export default class Room {
  constructor() {
    this.experience = new Experience()
    this.scene = this.experience.scene
    this.resoruces = this.experience.recources
    this.renderer = this.experience.renderer.renderer
    this.room = this.resoruces.items.room
    this.actualRoom = this.room.scene

    this.setModel()
  }

  setModel() {
    this.actualRoom.children.forEach((child) => {
      child.castShadow = true;
      // child.material.toneMapped = false

      if (child instanceof THREE.Group) {
        child.children.forEach((groupChild) => {
          groupChild.castShadow = true
          groupChild.material.toneMapped = false
        })
      }

      //Set tonemapp to neon lights
      if (child.name === "About_Text" || child.name === "Alexander_Text") {
        child.material.toneMapped = false
      }

      //Set up Brick Wall
      if (child.name === "Painted_Wall001") {
        child.material.map.rotation = 90 * Math.PI / 180
        child.material.map.repeat.set(1.7, 1.7)
      }
    });
    
    this.setupScreens()

    this.scene.add(this.actualRoom)
    this.actualRoom.scale.set(0.08, 0.08, 0.08)
  }

  //Screens
  setupScreens() {
    var div = document.createElement( 'div' );
    div.className = 'label';
    div.textContent = '1';
    var label = new CSS3DObject( div );
    label.rotation.y = Math.PI * 0;
    label.position.set(0.5,0.5,1);
    label.scale.set(0.02, 0.02, 1);

    this.scene.add( label );
  }

  resize() {
    //Nothing happening yet
  }

  update() {
    //Nothing happening yet    
  }
}

I also have some css for the “.label” (div):

.label {
  text-align: center;
  z-index: 10000;
  width: 20px;
  height: 20px;
  border: 1px solid white;
  color: #4f8;
  font-family: sans-serif;
  background: rgba( 0, 0, 0, .6 );
}