0

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>
gman
  • 100,619
  • 31
  • 269
  • 393
Kai
  • 871
  • 2
  • 18
  • 39
  • you basically can't render that situation in three.js. There's only 2 ways I know of to render 2 semi-transparent intersecting objects (1) subdivide the objects where the intersect. This way you can then draw everything in the correct order. Unfortunately it's super CPU intensive. For 2 cubes it's doable but for more complex shapes it's too slow for real time rendering. (2) use a form of [order independent transparency (OIT)](https://www.google.com/search?q=order+independent+transparency+three.js) which brings it's own set of problems. I've never actually seen OIT work – gman Nov 27 '18 at 14:12

1 Answers1

1

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>
Berthur
  • 4,300
  • 2
  • 14
  • 28
  • Thank you very much for your help. When using `renderer.sortObjects = false;` the smaller box disappears when seen from behind. The linked question advises to use the renderOrder property which seems to work fine (http://jsfiddle.net/dskb80jq/11/). – Kai Nov 27 '18 at 14:41
  • That's because you didn't change the order in which you add the cubes, as described in my answer. But sure, `renderOrder` is definitely a valid, and perhaps better, solution. Glad the problem was solved :) – Berthur Nov 27 '18 at 14:51