Reputation: 1
I want to make a 3d room in three.js. I want the walls, that are in front of the camera view, to become transparent as I rotate the room.
Here is example what i need: http://jsfiddle.net/tp2f2oo4/
It seems that solution is adding THREE.BackSide to the material.
var material2 = new THREE.MeshPhongMaterial( {
color: 0xffffff,
transparent: false,
side: THREE.BackSide
} );
Indeed this works perfectly when a room acts as a single THREE.BoxGeometry, but in my case, every wall, ceiling, and floor are separate THREE.BoxGeometry objects. Is there any way to hide them or not rendering when are in front of the camera view?
Upvotes: 0
Views: 4388
Reputation: 104833
You want to hide a mesh based on some condition. This is a perfect use case for using the onBeforeRender()
and onAfterRender()
methods.
// callbacks
var onBeforeRender = function() {
var v = new THREE.Vector3();
return function onBeforeRender( renderer, scene, camera, geometry, material, group ) {
// this is one way. adapt to your use case.
if ( v.subVectors( camera.position, this.position ).dot( this.userData.normal ) < 0 ) {
geometry.setDrawRange( 0, 0 ); // it is too late to set visible = false, so do this, instead
}
};
}();
var onAfterRender = function( renderer, scene, camera, geometry, material, group ) {
geometry.setDrawRange( 0, Infinity );
};
// mesh
mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 0, 0, 10 );
mesh.rotation.set( 0, 0, 0 );
scene.add( mesh );
mesh.userData.normal = new THREE.Vector3( 0, 0, - 1 );
mesh.onBeforeRender = onBeforeRender;
mesh.onAfterRender = onAfterRender;
fiddle: http://jsfiddle.net/3qh815xa/
three.js r.114
Upvotes: 6
Reputation: 8886
You could use ray-casting to determine which walls to turn off. Basically, shoot a ray from your camera, and set the transparency of the wall it intersects.
For a simplified case, see updateWallTransparency_simple
in the snippet below. It fires a ray from the center of the camera, and sets the transparency of the intersected wall.
However, you'll probably encounter a literal corner case, where you transition from one wall into the next. You can compensate for this with a second raycast, but you need to offset the two rays from the center, so they won't intersect the same wall in the case of a corner. See updateWallTransparency_corners
in the snippet below for an example.
Now this is a highly simplified case. If your wall shapes are more complex, you'll need to perform further checks to see if certain walls should be hidden or not. Also, if you tip the camera too far up, you'll notice it will make the back walls hidden instead. There is a lot more to consider for this problem as a whole, but this should get you started.
var renderer, scene, camera, controls, stats;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000,
degrad = (Math.PI / 180),
walls = [];
var raycaster = null,
coords = null;
function updateWallTransparency_simple() {
// reset all walls. You can optimize this by storing which ones are transparent/not
walls.forEach(function(wall) {
wall.material.opacity = 1;
});
raycaster.setFromCamera(coords, camera);
var intersects = raycaster.intersectObjects(walls);
if (intersects && intersects.length > 0) {
intersects[0].object.material.opacity = 0.25;
}
}
function updateWallTransparency_corners() {
// reset all walls. You can optimize this by storing which ones are transparent/not
walls.forEach(function(wall) {
wall.material.opacity = 1;
});
coords.set(-0.1, 0);
raycaster.setFromCamera(coords, camera);
var intersects = raycaster.intersectObjects(walls);
if (intersects && intersects.length > 0) {
intersects[0].object.material.opacity = 0.25;
}
coords.set(0.1, 0);
raycaster.setFromCamera(coords, camera);
var intersects = raycaster.intersectObjects(walls);
if (intersects && intersects.length > 0) {
intersects[0].object.material.opacity = 0.25;
}
}
function populateScene() {
var geo = new THREE.BoxBufferGeometry(20, 20, 0.1),
mat = new THREE.MeshPhongMaterial({
color: "darkred",
transparent: true,
opacity: 1
});
var mesh = new THREE.Mesh(geo, mat);
mesh.position.set(0, 0, -10);
mesh.name = "backWall";
scene.add(mesh);
mesh.updateMatrixWorld(true);
walls.push(mesh);
mesh = new THREE.Mesh(geo, mat.clone());
mesh.name = "frontWall";
mesh.position.set(0, 0, 10);
scene.add(mesh);
mesh.updateMatrixWorld(true);
walls.push(mesh);
mesh = new THREE.Mesh(geo, mat.clone());
mesh.name = "leftWall";
mesh.position.set(-10, 0, 0);
mesh.rotation.set(0, (Math.PI / 180) * 90, 0);
scene.add(mesh);
mesh.updateMatrixWorld(true);
walls.push(mesh);
mesh = new THREE.Mesh(geo, mat.clone());
mesh.name = "rightWall";
mesh.position.set(10, 0, 0);
mesh.rotation.set(0, (Math.PI / 180) * 90, 0);
scene.add(mesh);
mesh.updateMatrixWorld(true);
walls.push(mesh);
geo = new THREE.BoxBufferGeometry(50, 0.1, 50),
mat = new THREE.MeshPhongMaterial({
color: "green"
});
mesh = new THREE.Mesh(geo, mat);
mesh.position.set(0, -10, 0);
mesh.name = "ground";
scene.add(mesh);
geo = new THREE.TorusKnotBufferGeometry(2, 0.5, 100, 16),
mat = new THREE.MeshPhongMaterial({
color: "blue"
});
mesh = new THREE.Mesh(geo, mat);
mesh.name = "insideObject";
scene.add(mesh);
}
function init() {
document.body.style.backgroundColor = "lightBlue";
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
scene.add(camera);
controls = new THREE.OrbitControls(camera);
controls.enableZoom = false;
controls.enablePan = false;
controls.maxPolarAngle = Math.PI / 2;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
light = new THREE.HemisphereLight(0xffffbb, 0x00ff00, 1);
scene.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
populateScene();
raycaster = new THREE.Raycaster();
coords = new THREE.Vector3();
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
//updateWallTransparency_simple();
updateWallTransparency_corners();
render();
stats.update();
}
function threeReady() {
init();
}
(function() {
function addScript(url, callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function() {
addScript("https://threejs.org/examples/js/controls/OrbitControls.js", function() {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
threeReady();
})
})
})
})();
Upvotes: 3