Let's say I'm rendering Wonder Woman piloting her Invisible Jet. The jet is made up of multiple meshes, and is mostly transparent. Where the transparent meshes overlap, it bees more opaque. I'd like to not have that overlap, such that the transparent parts are still shaded, but the material discards transparent fragments that are behind other fragments, as if Wonder Woman is sitting inside a transparent shell.
Perhaps a good way to putting it is that I want to render transparent meshes like opaque meshes, but still allow them to be transparent.
As an example, here is Average Woman driving her Mostly-Visible Car: /
I've done different permutations of depth testing and writing, and tried all the various depth test functions. I've also tried different blending settings (I saw several suggestions for THREE.NoBlending
, but that's not what I want because I lose transparency). If I have to write a custom shader for this, I'll go that route, but I'm not sure where to start.
HTML:
<script>
// INITIALIZE
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
document.getElementById('host').appendChild(renderer.domElement);
var stats= new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 250;
var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
trackballControl.rotateSpeed = 5.0; // need to speed it up a little
var scene = new THREE.Scene();
var light = new THREE.PointLight(0xffffff, 1, Infinity);
light.position.copy(camera.position);
scene.add(light);
function draw(){
light.position.copy(camera.position);
renderer.render(scene, camera);
stats.update();
}
trackballControl.addEventListener('change', draw);
function navStartHandler(e) {
renderer.domElement.addEventListener('mousemove', navMoveHandler);
renderer.domElement.addEventListener('mouseup', navEndHandler);
}
function navMoveHandler(e) {
trackballControl.update();
}
function navEndHandler(e) {
renderer.domElement.removeEventListener('mousemove', navMoveHandler);
renderer.domElement.removeEventListener('mouseup', navEndHandler);
}
renderer.domElement.addEventListener('mousedown', navStartHandler);
renderer.domElement.addEventListener('mousewheel', navMoveHandler);
</script>
CSS:
html *{
padding: 0;
margin: 0;
width: 100%;
overflow: hidden;
}
#host {
width: 100%;
height: 100%;
}
JavaScript:
// NOTE: To run this demo, you MUST use the HTTP protocol, not HTTPS!
var msh = new THREE.Mesh(new THREE.SphereGeometry(10, 20, 20), new THREE.MeshLambertMaterial({color: "red"}));
msh.position.set(0, 10, 0);
scene.add(msh);
var mat = new THREE.MeshLambertMaterial({
color: "silver",
transparent: true,
opacity: 0.5
});
msh = new THREE.Mesh(new THREE.CylinderGeometry(15, 15, 75, 20), mat);
msh.rotation.set(0, 0, Math.PI / 2);
msh.position.set(0,10,0);
scene.add(msh);
msh = new THREE.Mesh(new THREE.CylinderGeometry(15, 15, 50, 20), mat);
msh.rotation.set(Math.PI / 2, 0, 0);
msh.position.set(-20,-10,0);
scene.add(msh);
msh = new THREE.Mesh(new THREE.CylinderGeometry(15, 15, 50, 20), mat);
msh.rotation.set(Math.PI / 2, 0, 0);
msh.position.set(20,-10,0);
scene.add(msh);
draw();
Let's say I'm rendering Wonder Woman piloting her Invisible Jet. The jet is made up of multiple meshes, and is mostly transparent. Where the transparent meshes overlap, it bees more opaque. I'd like to not have that overlap, such that the transparent parts are still shaded, but the material discards transparent fragments that are behind other fragments, as if Wonder Woman is sitting inside a transparent shell.
Perhaps a good way to putting it is that I want to render transparent meshes like opaque meshes, but still allow them to be transparent.
As an example, here is Average Woman driving her Mostly-Visible Car: http://jsfiddle/TheJim01/e6ccfo24/
I've done different permutations of depth testing and writing, and tried all the various depth test functions. I've also tried different blending settings (I saw several suggestions for THREE.NoBlending
, but that's not what I want because I lose transparency). If I have to write a custom shader for this, I'll go that route, but I'm not sure where to start.
HTML:
<script>
// INITIALIZE
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
document.getElementById('host').appendChild(renderer.domElement);
var stats= new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 250;
var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
trackballControl.rotateSpeed = 5.0; // need to speed it up a little
var scene = new THREE.Scene();
var light = new THREE.PointLight(0xffffff, 1, Infinity);
light.position.copy(camera.position);
scene.add(light);
function draw(){
light.position.copy(camera.position);
renderer.render(scene, camera);
stats.update();
}
trackballControl.addEventListener('change', draw);
function navStartHandler(e) {
renderer.domElement.addEventListener('mousemove', navMoveHandler);
renderer.domElement.addEventListener('mouseup', navEndHandler);
}
function navMoveHandler(e) {
trackballControl.update();
}
function navEndHandler(e) {
renderer.domElement.removeEventListener('mousemove', navMoveHandler);
renderer.domElement.removeEventListener('mouseup', navEndHandler);
}
renderer.domElement.addEventListener('mousedown', navStartHandler);
renderer.domElement.addEventListener('mousewheel', navMoveHandler);
</script>
CSS:
html *{
padding: 0;
margin: 0;
width: 100%;
overflow: hidden;
}
#host {
width: 100%;
height: 100%;
}
JavaScript:
// NOTE: To run this demo, you MUST use the HTTP protocol, not HTTPS!
var msh = new THREE.Mesh(new THREE.SphereGeometry(10, 20, 20), new THREE.MeshLambertMaterial({color: "red"}));
msh.position.set(0, 10, 0);
scene.add(msh);
var mat = new THREE.MeshLambertMaterial({
color: "silver",
transparent: true,
opacity: 0.5
});
msh = new THREE.Mesh(new THREE.CylinderGeometry(15, 15, 75, 20), mat);
msh.rotation.set(0, 0, Math.PI / 2);
msh.position.set(0,10,0);
scene.add(msh);
msh = new THREE.Mesh(new THREE.CylinderGeometry(15, 15, 50, 20), mat);
msh.rotation.set(Math.PI / 2, 0, 0);
msh.position.set(-20,-10,0);
scene.add(msh);
msh = new THREE.Mesh(new THREE.CylinderGeometry(15, 15, 50, 20), mat);
msh.rotation.set(Math.PI / 2, 0, 0);
msh.position.set(20,-10,0);
scene.add(msh);
draw();
Share
Improve this question
edited Apr 30, 2016 at 0:39
Peter O.
32.9k14 gold badges84 silver badges97 bronze badges
asked Apr 29, 2016 at 21:27
TheJim01TheJim01
8,8961 gold badge31 silver badges57 bronze badges
1 Answer
Reset to default 14If you want to render overlapping transparent faces, but do not want the overlapping regions to be darker, you can implement a trick: render the faces first with
material.colorWrite = false;
and then render them a second time with
material.colorWrite = true;
You can use the Object3D.renderOrder
property to ensure rendering the faces with colorWrite = false
occurs prior to rendering with colorWrite = true
.
three.js r.144