Threejs PlaneGeometry doesn't receive shadows or reflections - javascript

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.

Related

three.js clone() property changes for all my meshes

I would like to create 100 clones of a simple cube and decrease gradually the opacity of each cube. Here's the loop I have :
var geometry = new THREE.BoxGeometry(0.15,0.15,0.15);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh( geometry, material );
cube.material.transparent = true;
scene.add( cube );
for(let i = 0; i < 100; i++){
window['cube'+i] = cube.clone();
window['cube'+i].position.x = i;
window['cube'+i].material.opacity = 1 - (0.01*i);
scene.add(window['cube'+i]);
}
Unfortunately, all my meshes end with the last opacity established.
I don't understand why all my meshes have the same opacity while the x position increase normally.
Does anyone have an idea on how to separate each opacity property ? Thank you
Cloning a mesh does not clone its geometry and material by default for performance reasons. If you want to control the opacity per mesh, it's best to clone the material for each instance.

Three.js Multiple Materials in Plane Geometry

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! :)

How would I create a terrain like this in Three.js

I am trying to create a terrain from a heightmap with a "closed" bottom see the example here:
http://demos.playfuljs.com/terrain/
My terrain generation function is as so:
var img = document.getElementById("landscape-image");
var numSegments = img.width - 1; // We have one less vertex than pixel
var geometry = new THREE.PlaneGeometry(2400, 2400, numSegments, numSegments);
var material = new THREE.MeshLambertMaterial({
color: 0xccccff,
wireframe: false
});
plane = new THREE.Mesh(geometry, material);
plane.name = 'Terrain';
// set height of vertices
for (var i = 0; i < plane.geometry.vertices.length; i++) {
plane.geometry.vertices[i].z = (terrain[i]/255) * height;
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
plane.position.x = 0;
plane.rotation.x = 0;
plane.position.y = -10;
The problem I am having is how do I create that connected bottom part of the terrain with a THREE.PlaneGeometry. I can't extrude as:
The bottom must be flat if I extrude it will be bumpy like the
terrain.
Extrude takes a Shape object, not a Geometry object.
Really scratching my head here anyone done this before.
Edit: Maybe I could use two planes and merge them but how would I merge the side faces into a single piece of Geometery ?
P.S. The example draws straight to the canvas
create a plane for each side which has your amount of Segments in width and 1 in height. them set the top Vertices according to your heightmap
something like this
var geometry_l = new THREE.PlaneGeometry(2400, 0, numSegments, 1);
plane_l = new THREE.Mesh(geometry_l, material);
for (var i = 0; i < numSegments; i++) {
plane_l.geometry_l.vertices[i].z = (Terrain[0][i]/255) * height;
}
//Offset to the edge of your main plane
you might want to Change your Terrain Array to be two-dimensional for this. so you can always read the row you want.

THREE.MeshBasicMaterial renders, but THREE.MeshLambertMaterial does not

I'm working on a project that makes a sort of randomized sheet. It stores arrays of x, y, and z coordinates and draws triangles between the points. You can see this working pretty well in this screenshot.
I used MeshBasicMaterial to make that sheet, but wanted to switch to MeshLambertMaterial to take advantage of lighting. When I try this, I get a sheet that looks like this.
This is the working Basic Mesh code on green tiles:
for(j = 0; j < h-1; j++) { //h is the number of tiles vertically
for(i = 0; i < w-1; i++) { //w is the number of tiles horizontally
o = ((j%2==1)?1:0); //checks if the row is odd
var geom = new THREE.Geometry();
var a = new THREE.Vector3(x[i][j], y[i][j] ,z[i][j]);
var b = new THREE.Vector3(x[i+1][j], y[i+1][j] ,z[i+1][j]);
var c = new THREE.Vector3(x[i+o][j+1], y[i+o][j+1] ,z[i+o][j+1]);
geom.vertices.push(a);
geom.vertices.push(b);
geom.vertices.push(c);
geom.faces.push(new THREE.Face3(0,1,2));
tile1[i][j] = new THREE.Mesh(
geom,
new THREE.MeshBasicMaterial({color: 'green'})
);
scene.add(tile1[i][j]);
}
}
And this is the failing Lambert Mesh code on red tiles (note that I only changed 'Basic' to 'Lambert'):
for(j = 0; j < h-1; j++) { //h is the number of tiles vertically
for(i = 0; i < w-1; i++) { //w is the number of tiles horizontally
o = ((j%2==1)?0:1); //checks if the row is even
var geom = new THREE.Geometry();
var a = new THREE.Vector3(x[i+o][j], y[i+o][j] ,z[i+o][j]);
var b = new THREE.Vector3(x[i+1][j+1], y[i+1][j+1] ,z[i+1][j+1]);
var c = new THREE.Vector3(x[i][j+1], y[i][j+1] ,z[i][j+1]);
geom.vertices.push(a);
geom.vertices.push(b);
geom.vertices.push(c);
geom.faces.push(new THREE.Face3(0,1,2));
tile2[i][j] = new THREE.Mesh(
geom,
new THREE.MeshLambertMaterial({color: 'red'})
);
scene.add(tile2[i][j]);
}
}
A cube created with the following Lambert Mesh works perfectly and catches light properly.
scene.add(new THREE.Mesh(new THREE.BoxGeometry(10,1000,5),new THREE.MeshLambertMaterial({color:'red'})));
Why does the Lambert Mesh not work on a geometry that Basic Mesh works on?
EDIT: I placed a colored box under the sheet to test how the box would react to lighting and found that the tiles above it weren't failing to render, but were just black. They are opaque, but don't use the color or pick up light the way the box does.
You should have lights in your scene to profit from THREE.LambertMaterial. Did you setup your scene lighting correctly?
EDIT:
I found out where your problem is. You should add a normal to your faces, otherwise the WebGL renderer does not know how to render the light bouncing of the THREE.LambertMaterial on the surfaces. So change your code like this:
face = new THREE.Face3( 0, 1, 2 );
face.normal = new THREE.Vector3().crossVectors(
new THREE.Vector3().subVectors( b, a ),
new THREE.Vector3().subVectors( c, b )
).normalize();
geom.faces.push( face );
Now your faces should render.
Instead of doing this manually you can also use the geometry methods for calculating them:
geometry.computeFaceNormals();
geometry.computeVertexNormals();

Threejs, making glass shatter effect

I have an idea to create, but since my knowledge about Three.js and 3D programming in general is limited, I am stuck...
Idea: User is doing some things. At some point - whole front screen crashes, and reveals something different behind.
At the beginning, my idea was - make screenshot, of what is happening right now, put that image in front of everything (still, having difficulties making that plane to take 100% size - idea - when it shows, user cannot tell the difference, between old 3d renderings, and new 2d picture), and then - shatter it. So - by looking around the Web for different examples, I made some... thing - Created screenshot, made plane and applied screenshot as a texture to it. To create shattering effect, I used TessellateModifier to divide that plane, and ExplodeModifier to create each face as separated face.
Here will be the code I made so far.
function drawSS()
{
var img = new Image;
img.onload = function() { // When screenshot is ready
var canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var context = canvas.getContext('2d');
// CANVAS DRAWINGS
// Draws screenshot
// END
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var multiMaterial = [
new THREE.MeshBasicMaterial({map: texture, side: THREE.FrontSide}), // canvas drawings
new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: true, transparent: true}) // for displaying wireframe
];
var geometry = new THREE.PlaneGeometry(canvas.width, canvas.height); // create plane
for (var i = 0, len = geometry.faces.length; i < len; i++) { // Snippet from some stackoverflow post, that works.
var face = geometry.faces[i].clone();
face.materialIndex = 1;
geometry.faces.push(face);
geometry.faceVertexUvs[0].push(geometry.faceVertexUvs[0][i].slice(0));
}
geometry.dynamic = true;
THREE.GeometryUtils.center( geometry ); // ?
var tessellateModifier = new THREE.TessellateModifier( 10 );
for ( var i = 0; i < 5; i ++ )
tessellateModifier.modify( geometry );
new THREE.ExplodeModifier().modify( geometry );
geometry.vertices[0].x -=300;
geometry.vertices[1].x -=300;
geometry.vertices[2].x -=300;
var mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(multiMaterial) );
scene.add( mesh );
};
// THEN, set the src
img.src = THREEx.Screenshot.toDataURL(renderer);
}
For now - I moved 1 face, by changing coordinates of 3 vertices. I'm asking - if this approach is the way to go? Result looks like this. (white lines - wireframe, black lines - (drawings in canvas) my desired wireframes - problem for later). Note - by moving this way - texture goes along, I don't know, if I make new triangles using these vertices, how would I set texture.

Categories