Kai
Kai

Reputation: 891

overlapping semitransparent objects are not rendered as expected

I have two overlapping semitransparent boxes and I'd expect to see both of them independent of the viewing angle. The first image shows a rendering from aside and the small box is visible within the bigger box. The second image shows the same scene but form another viewing angle. As you can see, the smaller box is visible but the part which is with the bigger box is invisible. What am I missing?

small box visible within big box small box not visible within big box

var camera, scene, renderer;
init();
animate();

function init() {
  // Renderer.
  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  
  // Add renderer to page
  document.body.appendChild(renderer.domElement);

  // Create camera.
  camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50);
  camera.position.set(2, 2, 2);
  camera.lookAt(new THREE.Vector3(0.0, 0.0, 0.0));

  // Create scene.
  scene = new THREE.Scene();

  // Create material
  var material = new THREE.MeshStandardMaterial();
  material.transparent = true;
  material.opacity = 0.5;

  // Create cube and add to scene.
  var geometry1 = new THREE.BoxGeometry(1, 1, 1);
  var mesh1 = new THREE.Mesh(geometry1, material);
  mesh1.position.set(0, 0, 0);
  //mesh1.castShadow = true;
  scene.add(mesh1);

// Create cube and add to scene.
  var geometry2 = new THREE.BoxGeometry(0.5, 0.5, 0.5);
  var mesh2 = new THREE.Mesh(geometry2, material);
  mesh2.position.set(0.0, 0, 0.5);
  //mesh2.castShadow = true;
  scene.add(mesh2);

  var spotLight = new THREE.SpotLight(0xffffff, 0.32);
  spotLight.position.set(0, 5, 0);
  spotLight.castShadow = true;
  spotLight.shadow.mapSize.width = 2048;
  spotLight.shadow.mapSize.height = 2048;
  spotLight.shadow.camera.near = 0.1;
  spotLight.shadow.camera.far = 20;  
  scene.add(spotLight);

	let hemiLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.8);
	scene.add(hemiLight);
  
  // Ground plane
  var groundGeo = new THREE.PlaneBufferGeometry(50, 50);
  var groundMat = new THREE.MeshStandardMaterial({color: 0xffffff});
  var ground = new THREE.Mesh(groundGeo, groundMat);
  ground.rotation.x = -Math.PI / 2;
  ground.position.y = -0.5;
  ground.receiveShadow = true;
  scene.add(ground);

  // Add listener for window resize.
  window.addEventListener('resize', onWindowResize, false);

    let controls = new THREE.OrbitControls(camera);
    controls.enableZoom = true;
    controls.enablePan = false;
    controls.maxDistance = 20.0;
    controls.minPolarAngle = 0;
    controls.maxPolarAngle = Math.PI / 2;
    controls.target.set(0, 0, 0);
    controls.update();

}

function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}
body {
  padding: 0;
  margin: 0;
}

canvas {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 0

Views: 268

Answers (1)

Berthur
Berthur

Reputation: 4495

Transparent objects in WebGL are sometimes rather problematic. It's all about the rendering order: If the small cube is rendered after the large cube, how should the rendering behave? This question has some information you might find useful.

In your particular case (though not necessarily always), one solution could be to disable renderer object sorting:

renderer.sortObjects = false;

and(!) make sure you add your objects in the correct order, i.e. the small cube first and the large one second. Here is an updated version of your snippet:

var camera, scene, renderer;
init();
animate();

function init() {
  // Renderer.
  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.sortObjects = false;
  
  // Add renderer to page
  document.body.appendChild(renderer.domElement);

  // Create camera.
  camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50);
  camera.position.set(2, 2, 2);
  camera.lookAt(new THREE.Vector3(0.0, 0.0, 0.0));

  // Create scene.
  scene = new THREE.Scene();

  // Create material
  var material = new THREE.MeshStandardMaterial();
  material.transparent = true;
  material.opacity = 0.5;
  
  // Create cube and add to scene.
  var geometry2 = new THREE.BoxGeometry(0.5, 0.5, 0.5);
  var mesh2 = new THREE.Mesh(geometry2, material);
  mesh2.position.set(0.0, 0, 0.5);
  //mesh2.castShadow = true;
  scene.add(mesh2);

  // Create cube and add to scene.
  var geometry1 = new THREE.BoxGeometry(1, 1, 1);
  var mesh1 = new THREE.Mesh(geometry1, material);
  mesh1.position.set(0, 0, 0);
  //mesh1.castShadow = true;
  scene.add(mesh1);

  var spotLight = new THREE.SpotLight(0xffffff, 0.32);
  spotLight.position.set(0, 5, 0);
  spotLight.castShadow = true;
  spotLight.shadow.mapSize.width = 2048;
  spotLight.shadow.mapSize.height = 2048;
  spotLight.shadow.camera.near = 0.1;
  spotLight.shadow.camera.far = 20;  
  scene.add(spotLight);

	let hemiLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.8);
	scene.add(hemiLight);
  
  // Ground plane
  var groundGeo = new THREE.PlaneBufferGeometry(50, 50);
  var groundMat = new THREE.MeshStandardMaterial({color: 0xffffff});
  var ground = new THREE.Mesh(groundGeo, groundMat);
  ground.rotation.x = -Math.PI / 2;
  ground.position.y = -0.5;
  ground.receiveShadow = true;
  scene.add(ground);

  // Add listener for window resize.
  window.addEventListener('resize', onWindowResize, false);

    let controls = new THREE.OrbitControls(camera);
    controls.enableZoom = true;
    controls.enablePan = false;
    controls.maxDistance = 20.0;
    controls.minPolarAngle = 0;
    controls.maxPolarAngle = Math.PI / 2;
    controls.target.set(0, 0, 0);
    controls.update();

}

function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}
body {
  padding: 0;
  margin: 0;
}

canvas {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 1

Related Questions