Occlusion errors with CSS3DRenderer and WebGL

I’m having a lot of issues using CSS3Drenderer to render an iframe within my scene. The iframe is not being occluded by other objects in the scene after I’ve tried numerous fixes. My current approach is using a webGL plane to cut a hole through the canvas. I’m a beginner with three but any insight would be extremely helpful. My code is below

import React, { useEffect, useRef } from 'react';
import { extend, useThree, useLoader } from '@react-three/fiber';
import * as THREE from 'three';
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { Environment } from '@react-three/drei';
import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass.js';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';

extend({ CSS3DRenderer });

function CSS3DScene() {
    const { scene, gl, camera } = useThree();
    const cssScene = new THREE.Scene();
    const ref = useRef();
    const cssRendererRef = useRef();
    const deskGltf = useLoader(GLTFLoader, "../Assets/DeskScene.glb");

    useEffect(() => {
        // Setup the CSS3DRenderer
        const cssRenderer = new CSS3DRenderer();
        cssRenderer.setSize(window.innerWidth, window.innerHeight);
        cssRenderer.domElement.style.position = 'absolute';
        cssRenderer.domElement.style.top = '0';
        cssRenderer.domElement.style.left = '0';
        cssRenderer.domElement.style.zIndex = '1';
        document.body.appendChild(cssRenderer.domElement);

        cssRendererRef.current = cssRenderer;

        gl.domElement.style.position = "absolute";
        gl.domElement.style.top = "0";
        gl.domElement.style.zIndex = "0";

        // Film grain
        const composer = new EffectComposer(gl);
        const renderPass = new RenderPass(scene, camera);
        const filmPass = new FilmPass(0.1, 0.025, 648, false);

        composer.addPass(renderPass);
        composer.addPass(filmPass);

        // OrbitControls
        const controls = new OrbitControls(camera, gl.domElement);
        const controlsCss = new OrbitControls(camera, cssRenderer.domElement);

        // Lighting
        scene.add(<Environment preset="warehouse" />);
        const ambientLight = new THREE.AmbientLight(0x404040, 3); // Soft white light
        const directionalLight = new THREE.DirectionalLight(0xffffff, 3);
        directionalLight.position.set(5, 10, 5);
        scene.add(ambientLight, directionalLight);

        // Box for containing elements
        const boxGeometry = new THREE.BoxGeometry(45, 45, 45);
        const material = new THREE.MeshStandardMaterial({
            color: 0xffffff,
            roughness: 0.5,
            metalness: 0.1,
            side: THREE.BackSide // Render the inside of the box
        });
        const box = new THREE.Mesh(boxGeometry, material);
        box.position.set(0, 22.5, 0); // Adjust position as needed
        scene.add(box);

        const ambientLight1 = new THREE.AmbientLight(0xffffff, .4);
        scene.add(ambientLight1);

        const directionalLight1 = new THREE.DirectionalLight(0xffffff, .4);
        directionalLight1.position.set(10, 20, 10);
        scene.add(directionalLight1);

        // Create the iframe element
        const element = document.createElement("iframe");
        element.style.width = "720px";
        element.style.height = "640px";
        element.src = "https://www.bing.com";
        element.style.border = 'none';

        const domObject = new CSS3DObject(element);
        domObject.position.set(-0.15, 2.98, 0.12);
        domObject.rotation.y = Math.PI / 2;
        domObject.scale.set(0.0012, 0.0012, 0.0011);
        domObject.renderOrder = 1;  // Set render order
        cssScene.add(domObject);

        // WebGL plane for occluding CSS plane
        const createOccludingPlane = () => {
            const geometry = new THREE.PlaneGeometry(720, 640);
            const material = new THREE.MeshBasicMaterial({
                color: 0x000000,
                opacity: 0,
                transparent: true,
                depthTest: true,
                depthWrite: true,
                blending: THREE.NoBlending
            });

            const mesh = new THREE.Mesh(geometry, material);
            mesh.position.copy(domObject.position);
            mesh.rotation.copy(domObject.rotation);
            mesh.scale.copy(domObject.scale);
            mesh.renderOrder = 0;
            return mesh;
        }

        const occlusionMesh = createOccludingPlane();
        scene.add(occlusionMesh);

        // Add the Desk model
        deskGltf.scene.position.set(0, 0, 0); 
        deskGltf.scene.renderOrder = 2;
        scene.add(deskGltf.scene);

        // Animation loop for CSS3D rendering
        const animate = () => {
            ref.current = requestAnimationFrame(animate);
            gl.clear();
            composer.render();  // using composer instead of gl.render
            gl.clearDepth();  // Clear depth before rendering CSS3DRenderer
            cssRenderer.render(cssScene, camera);
            controls.update();
            controlsCss.update();
        };
        animate();

        // Cleanup
        return () => {
            cancelAnimationFrame(ref.current);
            document.body.removeChild(cssRenderer.domElement);
            cssScene.remove(domObject);
            scene.remove(occlusionMesh);
            scene.remove(deskGltf.scene);
        };
    }, [camera, scene, gl, deskGltf.scene]);

    return null;
}

export default CSS3DScene;

css3d cannot occlude, it never could. but you’re using react, drei/html can. why would you want to even use css3d? GitHub - pmndrs/drei: 🥉 useful helpers for react-three-fiber

all your have to do is

<group>
  <mesh />
  <SomeObject />
  <Html transform occlude="blending">
    <div>hello world 👋<div />
  </Html>
  ...

here’s an example Occluded HTML (forked) - CodeSandbox

also, i don’t understand the rest of the code. so you have that iframe and you want to render threejs into it, again? so a threejs in a threejs scene? but why don’t you use portals for that, why iframes?

portals, like so https://codesandbox.io/p/sandbox/drei-rendertexture-0z8i2c

even if you wanted threejs in an iframe inside three, … you could just render a <Canvas> into the <Html> component. though i start to ask myself why you would do that, it’s certainly not ideal/efficient. maybe you want to explain in more detail what you need to do.