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;