Calculating light for custom geometry in Three.js - javascript

I've created a custom geometry and I've used geometry.computeFaceNormals() to get the lighting right. So far so good.
The problem comes when I animate the geometry. In the animation loop I call again geometry.computeFaceNormals() but the faces' lighting doesn't change.
Here is a fiddle with the example:

You are updating the vertices of your geometry, so typically, your normals must also be updated for the shading to be correct.
However, since you want flat shading, there is another solution.
MeshPhongMaterial generates flat shading using the OES_standard_derivatives extension. This means that geometry normals do not have to be specified or updated when vertices change.
All you have to do is use the following pattern, and flat shading will "just work" -- provided the extension is supported.
var material = new THREE.MeshPhongMaterial( {
color: 0xFFFFFF,
shading: THREE.FlatShading
} );
three.js r.80

Related

What are the properties of three.js emissive materials

I'm working on a simple demonstration in three.js and am confused by the behaviour of THREE.MeshPhongMaterial coming from a background in the Unity Game Engine.
create_ring() {
// creates a ring mesh per inputed class data
const material = new THREE.MeshPhongMaterial({
color: this.color,
emissive: this.color,
emissiveIntensity: 1.6
});
const ring_geo = new THREE.TorusGeometry(this.radius, this.thickness, 16, 100);
// Translate in space
ring_geo.translate(5, 5, 0)
// add texture to mesh and output
const ring_mesh = new THREE.Mesh(ring_geo, material);
ring_mesh.receiveShadow = true;
ring_mesh.castShadow = true;
ring_mesh.name = "ring";
return ring_mesh
}
I was under the impression the materials would create a nice gentle pool of light on the floor geometry but now having researched the problem either I need some advice on how to implement this as a shader feature? Or I'm not understanding the limits and behaviour of materials in three.js? Below is an example of what is possible with a material's emissive option in Unity.
There's more than just an emissive material shown in the Unity screenshot above — the objects around the light probably were probably also marked as static, which Unity uses to "bake" the glow effect onto them, while compiling the application. There could also be a "bloom" post-processing effect to create the dynamic glow seen by the camera around the object.
Because three.js runs on the web and does not have an offline compilation step, these additional effects have to be configured manually. You can see the three.js bloom example for some help adding the bloom effect to a scene. Baking the light onto surrounding objects would generally be done in Blender, and then loaded into three.js with the base color texture or a lightmap.

Three.js - how to create custom shapes

I´m using Three.js and trying to create some custom shapes, similar to one that appears in a project from one of agencies using threejs:
three.js featured project esample
How did they generated these boxes with holes inside? (on that examples
boxes basically have only borders around and are empty inside).
As I saw in the code (I was trying to figure out myself) they use BoxGeometry but I have no idea how to accomplish that. Does anyone know or can give me any directions? It would be really helpfull as i´m stuck with this and have no idea on how to create them.
So in THREE.js Meshes represent any kind of 3D object. They combine Geometries and Shaders. Generally to create a mesh you call
var mesh = new THREE.Mesh( geometry, shader );
If you use any of the builtin shaders (also known as Materials [ MeshBasicMaterial, MeshLambertMaterial, etc]) they have a wireFrame boolean attribute that allows this functionality.
var geometry = new THREE.BoxGeometry( x, y, z ),
material = new THREE.MeshBasicMaterial( {
wireFrame: true, // This makes the object appear wireframe
color: 0xffffff // You can alter other properties
});
var box = new THREE.Mesh( geometry, material );
// You can also change it later
box.material.wireFrame = false;

How to improve merging by computing new faces in ThreeJS

I'm learning ThreeJS for 4 months, applying it into a personal project.
Yesterday, I achieved building a stronghold using most of ThreeJS geometries and some CSG tricks. The result looks fine, but I like precision and my geometry is kind of a mess (mostly after CSG subtractions).
[Question] I wonder if there's a known way to merge two geometries and replacing its old faces by new computed faces ? There is a JSFiddle to illustrate my question.
[Edit : Updated the fiddle with a fourth and a fifth mesh]
// FIGURE 1 : Basic merged geometry
var figure1 = new THREE.Geometry();
figure1.merge(box1Geometry);
figure1.merge(box2Geometry);
figure1.merge(box3Geometry);
figure1.computeFaceNormals();
figure1.computeVertexNormals();
var mesh = new THREE.Mesh(figure1, material);
scene.add(mesh);
// FIGURE 2 : Merged geometry with merged vertices
var figure2 = figure1.clone();
figure2.mergeVertices();
figure2.computeFaceNormals();
figure2.computeVertexNormals();
mesh = new THREE.Mesh(figure2, material);
// FIGURE 3 : Expected merged geometry (less faces)
var figure3 = new THREE.Geometry();
figure3`.vertices.push(
// manually create vertices here
);
figure3.faces.push(
// manually create the faces here
);
figure3.computeBoundingSphere();
figure3.computeFaceNormals();
figure3.computeVertexNormals();
mesh = new THREE.Mesh(figure3, material);
scene.add(mesh);
Three ways to get the same mesh
The first mesh on the left is a basic merged geometry composed of three boxGeometry.
The second mesh in the middle is exactly the same mesh, after calling the mergeVertices() function. It results saving 4 vertices. But faces inside the mesh are still there. It results not only in looking bad (for me), but also in issues for texturing or lighting these parts (face normals aren't where they should be).
The last mesh on the right is the mesh I would expect after merging. Look at the faces below the middle box, they only fit what they should.
The fact that it leads to texture and lighting issues (look at the JSFiddle, it lights the inner parts of the mesh) makes me think that it must be a simple and well-known way to solve this but I'm just feeling like a big noob.
This issue is directly linked with another question I'll ask if I don't find (or understand) any answer on SO (and maybe it'll help you to understand why I want to do that): Is there a way to apply a texture on this merged geometry without creating an unique material for each face of each geometry (because of the different UV mapping and mesh sizes) ? I can't imagine to do it manually for each face of my huge stronghold...
[EDIT] Writing my question, I just realized that ThreeCSG and its union() function do the trick. But I don't like the mess of vertices it creates. Even for basic geometry like these boxes, ThreeCSG will create strange vertices and faces on parts of the geometry where everything was already fine.
I updated the JSFiddle with a fourth mesh (CSG). In this simple usecase, we can see that there are 2 vertices and 2 faces more than expected. It seems that it kept the old faces (look at the wireframe !).
Is ThreeCSG union the best option for now ?
[EDIT 2] Fiddle updated with native CSG geometry. It gives the result I expected with only 20 vertices and 32 faces. Thanks to Wilt for this idea. The issue is that hard coding the polygons takes too long (take a look at the code for only three boxes). I have no JSON file to load and generate the polygons, I only have ThreeJS geometries. So I'll look at the conversion between ThreeJS and ThreeCSG geometries and I hope to understand why when there is a conversion, it gives a bad result.

How do I change the basic parameters of a Geometry in Three.js (for example, radius, number of vertices, etc.)

I create a tetrahedron of radius 3
// create a tetrahedron
var tetGeometry = new THREE.TetrahedronGeometry(3);
var tetMaterial = new THREE.MeshLambertMaterial(
{color: 0x20f020, transparent:true, opacity:0.6});
tet = new THREE.Mesh(tetGeometry, tetMaterial);
tet.name='tet';
tet.castShadow = true;
Later, I want the tetrahedron to grow:
// change hedron
scene.getObjectByName('tet').radius = control.hedronRadius;
That doesn't work.
// change vertices
scene.getObjectByName('tet').detail = control.hedronVertices;
That doesn't work either.
scene.getObjectByName('tet').verticesNeedUpdate;
And this doesn't help.
So how do I change the radius of a tetrahedron (or any Geometry) and how do I change the vertices.
In the documentation I see references to:
Geometry
.dynamic
.morph
.verticesNeedUpdate
.scale
And also references to bones and skeletons and skinned meshes used to animate geometries.
How do I change these aspects of Geometries in general?
What's the most reasonable, suggested way then to grow the radius of a Tetrahedron, or change the number of vertices show it becomes a different number polyhedron?
To change geometry you need to use:
morphTargets: true
I've prepared an example using a tetrahedron as you mention in jsfiddle.
Use sliders to change geometry.
To make some custom vertices and "fill" them by faces, you need to understand a lot of things from math, like; point, vector, etc.
I've done 2 simple flat objects, triangle and square in jsfiddle.
I hope that you'll easy understand how it works in general.

Water/Mirrored surface in WebGL using ThreeJS

I am trying to make a water surface in WebGL using Three.js. I think I will start with just a mirror as I think I know how to add displacement to make basic ripple effects.
This is what I know: Reflection is usually made by rendering a vertically (y-axis) flipped scene on a FBO using the water plane as a culling plane. Then this FBO is used as a texture for the water plane. Using a displacement map (or a noise texture) the image can be displaced and a water effect achieved.
The problems: First off, I can't find a way to flip the scene in ThreeJS. In OpenGL you can just use glScale and put -1 for Y, but I don't think this is possible in WebGL (or GLES on which it is based). At least I found no such thing in ThreeJS. There is a scale parameter for geometry, but there is none for scene. One solution could be changing the .matrixWorldInverse in Camera, but I am not sure how I could do that. Any ideas?
The second hurdle is clipping/culling plane. Again, the old way was using glClipPlane, but its not supported even in the newest OpenGL standard as far as I know, so its also not in WebGL. I read somewhere that you can do that in vertex shader, but in ThreeJS I only know how to add shaders as materials and I need this during the render to FBO.
And third, rendering the FBO to water plane with correct texture coordinates, so I think basically projecting from the camera position.
I can't find any more information on this on the internet. There are very few WebGL reflection examples and the only thing close was here, and it used some "Oblique View Frustum" method for culling. Is this really the best way to do it nowadays? Instead of one function we now must code this ourselves in software (to be ran on CPU not GPU)? Also cube reflections provided in ThreeJS of course is not applicable for a plane, so yes, I tried those.
If someone can make as easy as possible example on how to do this I would greatly appreciate it.
Check this three.js example out.
Out of the box and ready to use, straight from the source:
water = new THREE.Water( renderer, camera, scene, {
textureWidth: 512,
textureHeight: 512,
waterNormals: waterNormals,
alpha: 1.0,
sunDirection: light.position.clone().normalize(),
sunColor: 0xffffff,
waterColor: 0x001e0f,
distortionScale: 50.0,
} );
mirrorMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry( parameters.width * 500, parameters.height * 500 ),
water.material
);
mirrorMesh.add( water );
mirrorMesh.rotation.x = - Math.PI * 0.5;
scene.add( mirrorMesh );
Seems to look like an ocean to me :)
You can see this presentation http://29a.ch/slides/2012/webglwater/
and this fiddle may be useful for you jsfiddle.net/ahmedadel/44tjE
This only addresses the scaling part of your question. The matrix that is attached to the Object3D has a makeScale method.

Categories

Resources