I am merging multiple cubes into different block shapes with three.js using THREE.GeometryUtils.merge. I want to make these block shapes transparent, but when it's transparent you can see that it's multiple cubes stuck together like this:
How do I make my merged cubes transparent without rendering the inside edges? (how do I remove the inner faces?)
My code that merges the cubes:
geometry = new THREE.CubeGeometry(STEP_SIZE, STEP_SIZE, STEP_SIZE);
for (i = 0; i < shape.length; i++) {
tmpGeometry = new THREE.Mesh(new THREE.CubeGeometry(STEP_SIZE, STEP_SIZE, STEP_SIZE));
tmpGeometry.position.x = STEP_SIZE * shape[i].x;
tmpGeometry.position.y = STEP_SIZE * shape[i].y;
THREE.GeometryUtils.merge(geometry, tmpGeometry);
}
block = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x0000ff, opacity: 0.5, transparent: true }));
After some experimenting I finally got what I wanted. Hope this will help other people.
I go through each face of the block and raycast the block itself using the face's centroid and inverted normal (so that the normal points inward). If it intersects with another face that's part of the block, we know that that face is an inner face and we can go ahead and remove it.
// block is a Three.Mesh object
function removeInternalFaces(block) {
var geometry = block.geometry;
var face;
var toDelete = [];
var blockRaycaster = new THREE.Raycaster();
// raycast itself from the center of each face (negated normal), and whichever face gets intersected
// is an inner face
for (i = 0; i < geometry.faces.length; i++) {
face = geometry.faces[i];
if (face) {
normal = face.normal.clone();
normal.negate();
blockRaycaster.set(face.centroid, normal);
intersects = blockRaycaster.intersectObject(block);
for (j = 0; j < intersects.length; j++) {
toDelete.push(intersects[j].faceIndex);
}
}
}
// actually delete them
for (i = 0; i < toDelete.length; i++) {
delete geometry.faces[toDelete[i]];
}
geometry.faces = geometry.faces.filter( function(v) { return v; });
geometry.elementsNeedUpdate = true; // update faces
}
You can actually initialize cube's each face material separately and individually set the opacity of the inner faces to 0. The Code will be something like this.
var materialArray = [];
materialArray.push(new THREE.MeshLambertMaterial({
opacity:0,
color:0x0000FF,
transparent: true
}));// assuming you want this face to be transparent
materialArray.push(new THREE.MeshLambertMaterial({
opacity:0,
color:0x0000FF,
transparent: true
}));// assuming you want this face to be transparent
materialArray.push(new THREE.MeshLambertMaterial({
opacity:0.5,
color:0x0000FF,
transparent: true
}));
materialArray.push(new THREE.MeshLambertMaterial({
opacity:0.5,
color:0x0000FF,
transparent: true
}));
materialArray.push(new THREE.MeshLambertMaterial({
opacity:0.5,
color:0x0000FF,
transparent: true
}));
materialArray.push(new THREE.MeshLambertMaterial({
opacity:0.5,
color:0x0000FF,
transparent: true
}));
var CubeMat = new THREE.MeshFaceMaterial(materialArray);
and pass this to your cube mesh something like this.
block = new THREE.Mesh(geometry,CubeMat )
check this JsFiddle for a demo
Related
I am using threejs and trying to create a star system by using the following code:
// create the particle variables
//var particleCount = 180000,
var particles = new THREE.Geometry();
var multiplyingFactor = 50;
// now create the individual particles
for (var p = 0; p < array.length; p++) {
//the star from the csv file
star = array[p];
// initialize the particle with the star's information.
particle = new THREE.Vector3(star.x * multiplyingFactor, star.y * multiplyingFactor, star.z * multiplyingFactor);
// add it to the geometry
particles.vertices.push(particle);
}
// var pMaterial = new THREE.PointsMaterial({
// color: 0xFFFFFF,
// size: 20,
// map: THREE.ImageUtils.loadTexture("http://localhost:8080/images/circle.png"),
// blending: THREE.AdditiveBlending,
// transparent: true
// });
// instantiate a loader
var loader = new THREE.TextureLoader();
//allow cross origin loading
loader.crossOrigin = '';
// load a resource
loader.load('http://localhost:8080/images/circle.png',
// Function when resource is loaded
function (texture) {
var pMaterial = new THREE.PointsMaterial({
color: 0xFFFFFF,
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true
});
// create the particle system
particleSystem = new THREE.Points(
particles,
pMaterial);
// also update the particle system to
// sort the particles |which enables
// the behaviour we want
particleSystem.sortParticles = true;
scene.add(particleSystem);
}
);
It works correctly, however the texture is loaded as a square even though the image I am using for it is a white circle (not visible here in stack overflow but you can click over it):
However, once it loads, every vector in the system has black edges visible when over imposed:
My question is:
How can I get rid of those black edges? is it possible?
You can use alphaTest to discard unwanted fragments like so:
var pMaterial = new THREE.PointsMaterial( {
color: 0xffffff,
size: 20,
map: texture,
alphaTest: 0.5,
transparent: false // only set to true if it is required for your texture
} );
Also, only set the material blending property if you specifically want to change it from the default.
three.js r.85
I am trying to use a single plane to show multiple materials. I am currently using the MultiMaterial with the materials I plan to use inside of (textured).
The issue I am having is that the materials I use seem to get split across the entire plane into little chunks for each face. However I would like to have a material cover a 1 / n amount of the plane/mesh.
My current code (shortened):
var splitX = 2;
var splitY = 2;
var geometry = new THREE.PlaneGeometry(800, 800, splitX, splitY);
var materials = [
new THREE.MeshBasicMaterial({
map: preloaded texture..., side: THREE.DoubleSide
}),
new THREE.MeshBasicMaterial({
color: 0xff0000, side: THREE.DoubleSide
})
];
// set a single square inside the plane to the desired textured material
geometry.faces[0].materialIndex = 0;
geometry.faces[1].materialIndex = 0;
// set the other squares inside the plane to use the coloured material
for(var i = 1; i < geometry.faces.length / 2; i++) {
geometry.faces[i * 2].materialIndex = 1;
geometry.faces[i * 2 + 1].materialIndex = 1;
}
var mesh = new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
scene.add(mesh);
The output: http://prnt.sc/e8un2a
I marked each corner of the texture to see whether it would show the entire texture in the 2 faces I specified and it did not. Any help would be appreciated to resolve this! :)
I have an array of Vector3s that define an arbitrary curved shape in 3D space. I can render an outline of the curve in Three.js by using a THREE.Geometry and a THREE.Line, but I'd like to fill it with color.
I tried using THREE.ShapeGeometry and THREE.Mesh, but it seems as though THREE.ShapeGeometry is for 2D planes only (the z-coordinates of my vertices are being ignored). I also tried to use THREE.Geometry and define the faces in addition to the vertices I wanted, but had no luck.
How should I go about doing this?
Code:
geom.vertices = curve.getPoints(100);
for (var i = 0; i < 97; i++) {
geom.faces.push(new THREE.Face3(i, i + 1, i + 2));
}
var material = new THREE.MeshNormalMaterial();
obj = new THREE.Mesh(geom, material);
scene.add(obj);
Fixed by changing the code in the question above to the excerpt below:
geom.vertices = curve.getPoints(100);
for (var i = 0; i < 98; i++) {
geom.faces.push(new THREE.Face3(0, i + 1, i + 2));
}
var materialObj = { color : 0xff0000, side: THREE.DoubleSide };
var material = new THREE.MeshBasicMaterial(materialObj);
obj = new THREE.Mesh(geom, material);
scene.add(obj);
I have a problem with shadows not being received on the faces in an Object3D group.
The shadows are being cast from the objects and received by the ground, but the shadows are not received by each other when they should.
I've searched around but I can't seem to find a similar problem which leads me to believe I'm setting something up incorrectly.
Would anyone be able to take a look? I've put a working example in the below JSfiddle. I think it might be an issue with the way I'm setting up the faces.
https://jsfiddle.net/shanemccster/848k1qxh/
var makeobject = function(width, height, depth){
logger('makeobject fired');
var geometry = new THREE.BoxGeometry( width, height, depth );
var materials = [
new THREE.MeshLambertMaterial({ color: 0xffffff }),
new THREE.MeshLambertMaterial({ color: 0xffcc00 }),
new THREE.MeshLambertMaterial({ color: 0xffffff }),
new THREE.MeshLambertMaterial({ color: 0xffcc00 }),
new THREE.MeshLambertMaterial({ color: 0xffffff }),
new THREE.MeshLambertMaterial({ color: 0xffcc00 })
];
var texture = new THREE.MeshFaceMaterial( materials );
texture.minFilter = THREE.LinearFilter;
var theObject = new THREE.Mesh(geometry,texture);
theObject.recieveShadow = true;
theObject.castShadow = true;
return theObject;
}
You need to set the receiveShadow flag on your meshes. Look at the documentation of Object3D which is the parent of Mesh.
https://jsfiddle.net/woa7kzz1/
I'm pretty new to 3d and to threejs and I can't figure out how I can get a PlaneGeometry to show individually illuminated polygons i.e. receive shadows or show reflection. What I basically do is taking a PlaneGeometry applying some noise to every z value of the vertices. Then I have a simple directional light in my scene which is supposed to make the emerging noise pattern on the plane visible. I tried different things like plane.castShadow = true or renderer.shadowMapEnabled = true without success. Am I just missing a simple option or is this way more complicated than I think?
Here's are the relevant pieces of my code
renderer.setSize(width, height);
renderer.setClearColor(0x111111, 1);
...
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.9);
directionalLight.position.set(10, 2, 20);
directionalLight.castShadow = true;
directionalLight.shadowCameraVisible = true;
scene.add( directionalLight );
var geometry = new THREE.PlaneGeometry(20, 20, segments, segments);
var index = 0;
for(var i=0; i < segments + 1; i++) {
for(var j=0; j < segments + 1; j++) {
zOffset = simplex.noise2D(i * xNoiseScale, j * yNoiseScale) * 5;
geometry.vertices[index].z = zOffset;
index++;
}
}
var material = new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
color: 0xf50066
});
var plane = new THREE.Mesh(geometry, material);
plane.rotation.x = -Math.PI / 2.35;
plane.castShadow = true;
plane.receiveShadow = true;
scene.add(plane);
This is the output I get. Obviously the plane is aware of the light because the bottom side is darker than the upper side but there is no sign of any individual polygons receiving individual lightening and no 3d structure is visible. Interestingly when I put in a different geometry like a BoxGeometry individual polygons are illuminated individually (see 2nd image). Any ideas?
Ok I figured it out thanks to this post. The trick is to use the THREE.FlatShading shader on the material. Important to note is that after every update of the vertices two things need to be done. Before rendering geometry.normalsNeedUpdate must be set to true so the renderer also incorporates the newly oriented vertices. Also geometry.computeFaceNormals() needs to be called before rendering because when you alter the vertices the normals are not the same anymore.