How to make a transparent material that filters what can be seen behind it?

I’m trying to create a transparent material that allows you to see objects through it, except for objects that have the same transparent material applied. For example, if I create two spheres with this transparent material, I want each sphere to be invisible when viewed through the other sphere.

const geometry = new THREE.SphereGeometry(1, 32, 32);

const transparentMaterial = new THREE.MeshBasicMaterial({
    color: 0xff0000,
    transparent: true,
    opacity: 0.5,

const sphere1 = new THREE.Mesh(geometry, transparentMaterial);
sphere1.position.set(0, 3, 0);

const sphere2 = new THREE.Mesh(geometry, transparentMaterial.clone()); 
sphere2.position.set(0, 3, 2.1);


I’ve tried playing around with depthWrite and depthTest properties of the material, but this doesn’t seem to be the solution. Does anyone have any suggestions on how to achieve this effect?

Thank you in advance for any suggestions!

1 Like

This is an interesting problem, if I understand the situation correctly, the only way I can think to solve this is to conditionally render either transparent object based on camera location. If camera is in a position to look through Sphere1 then Sphere2 is flagged visible=false, and vice versa.
Interested to see how others would handle this situation.

Yes, try with a custom sorting function, like this:

renderer.setTransparentSort( (a,b)=>a.z-b.z );

Here is a live demo, the cyan and the red spheres are blocking each other, but are semitransparent to all other spheres. There is OrbitControls, so you can inspect the model from different points of views:



material.transparent=true objects are always sorted back to front so that they layer properly.
To make this effect, you actually need the reverse order…
So perhaps simply removing .transparent=true will get you the correct sorting order… or at least allow you to manually control the order via .renderOrder, and then you can perform the depth sort yourself so that the front one is always rendered first… and you’ll need both depthTest=true and depthWrite = true (the default)

edit: Ahh @PavelBoytchev that’s a much cleaner/easier solution than mucking with renderOrder. Nice.