I'm loading .stl files, applying MeshStandardMaterial without touching the flatShading property as by default it is false.
The result looks very flat to me. If I try setting flatShading: true the result is the same.
I've tried everything I could think of but have ran out of ideas - any suggestion would be welcome, thanks.
geometry.computeVertexNormals();
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
geometry.normalizeNormals();
The result looks very flat to me. If I try setting flatShading: true the result is the same.
STLLoader always returns a non-indexed buffer geometry (unconnected triangle soup). That means the geometry's faces share no vertices and thus using BufferGeometry.computeVertexNormals() can not produce normals required for smooth shading.
Also recomputing bounding volumes and the usage of BufferGeometry.normalizeNormals() are unrelated to this issue.
You can try to solve this issue by ensuring the asset comes with normals that allow smooth shading. Or you give BufferGeometryUtils.mergeVertices() a try which produces an indexed geometry by merging vertices.
Related
I have created a box geometry as below,
const hand1geo = new THREE.BoxGeometry(2, 0.01, 0.2);
const material_sidehand = new THREE.MeshBasicMaterial({ color: 0x3cc1b7 });
const sidehand = new THREE.Mesh(hand1geo, material_sidehand);
What I want to do is to extract vertices from this box and I use this,
this.sidehand.attributes.position.array
And what I got is as following,
The picture of result. I really don't understand why it just spanned 72 elements(24 vectors) with same value. Why there are 24 vectors here and where have them been defined? Because I wanna use raycaster to do the collision detection later on.
I tried to use this.sidehand.vertices but it doesn't work.
I tried to use this.sidehand.vertices but it doesn't work.
I don't know what references you used but Mesh never had a property called vertices. You probably refer to the former Geometry class which indeed had this property. However, this class has been deprecated and BufferGeometry is used now instead.
I really don't understand why it just spanned 72 elements(24 vectors) with same value.
The values are not identical. BoxGeometry defines all vertices of the box in local space in a flat array so the data can be directly used by the WebGL API (which is good for performance).
There are 24 vectors because the geometry defines for each side of the box four vertices. Each side is composed of two triangles. This is done so it's possible to generate proper normals and texture coordinates.
I suggest you reconsider to use raw geometry data for collision detection. You are going to achieve much better performance by working with bounding volumes instead.
I am trying to figure out how to properly use Three.js' built in ShaderChunks for lighting and fog and such, and I figured a good first step was to just copy one of the ShaderLib shaders' setup. So to start with I used:
customMaterial = new ShaderMaterial({
lights: true,
uniforms: UniformsUtils.merge( [
UniformsLib.common,
UniformsLib.specularmap,
UniformsLib.envmap,
UniformsLib.aomap,
UniformsLib.lightmap,
UniformsLib.emissivemap,
UniformsLib.fog,
UniformsLib.lights,
{
emissive: { value: new Color( 0x000000 ) },
diffuse: { value: new Color( 1,1,1 ) }
}
]),
vertexShader: document.getElementById("vertexShader").textContent,
fragmentShader: document.getElementById("fragmentShader").textContent
})
Where the shader code is just directly copied from meshlambert_vert.glsl and meshlambert_frag.glsl, and that section there is based on this entry in the ShaderLib
However, I am rendering my test scene from two different cameras/renderers at once, and I immediately noticed an issue. Changing one camera's perspective changes the second camera's lighting angle, for objects with this customMaterial applied.
I assume this is due to these UniformLib objects being referenced elsewhere?
I'm not sure what I should be passing here instead, nor why this doesn't work but the standard material does. I guess I'm skipping a step, but I don't understand what it might be.
Here is a codepen where I have isolated the problem as much as I can. Now it is almost a direct copy of the ShaderLib source. At this point I'm thinking this is a pass-by-reference where it should have been a copy, somewhere inside the WebGLRenderer. https://codepen.io/cl4ws0n/pen/dVewdZ
For whatever its worth, I also tried adding a second scene, and moving the objects between them. That didn't fix it, nor did separate objects in separate scenes sharing the material.
WebGLRenderer has some hardcoded logic for certain materials. In this case it's looking for a flag called isMeshLambertMaterial: https://github.com/mrdoob/three.js/blob/r87/src/renderers/WebGLRenderer.js#L1780
So try setting isMeshLambertMaterial: true, isMeshBasicMaterial: false in your material.
As Matjaz Drolc indicated with his comments, this is a bug in three r87. The needsUpdate flags are not functioning properly for ShaderMaterials. You can force an update before each render pass in my codepen link and it will render correctly. See the fork https://codepen.io/cl4ws0n/pen/qPYwzp
I have made an issue on the repo, if anyone would like to track it's progress it can be found here.
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.
I'm currently diving into Three.js and came across my first real issue when switching from native primitives to imported OBJ objects.
I have a simple model of a dodecahedron that I UV-mapped in Cinema4D r15 and exported as OBJ file. When using this model over a DodecahedronGeometry, the geometry is not lit by my directional light anymore, but it is if I use the primitive.
See this JSFiddle: https://jsfiddle.net/xm3ttmxw/1/
See desired result here (using a primitive): https://jsfiddle.net/84hbs7ed/1/
As you can see, I'm setting the receiveShadow property to true for all meshes in the OBJ. I activated shadow maps for the light and renderer. The directional light is following the camera and pointing towards the origin (center of the dodecahedron). The ambient light seems to work fine.
Any idea? Thanks in advance
After some more research, it appears that the problem comes from a lack of vertex normals (OBJLoader doesn't compute those). The solution is to compute the vertex normals on the fly like shown here: https://stackoverflow.com/a/28568522/3446439
object.traverse( function( child ) {
if ( child instanceof THREE.Mesh ) {
child.geometry.computeVertexNormals(); //add this
}
} );
Thanks Sebastian Baltes!
Is it possible to have an black outline on my 3d models with three.js?
I would have graphics which looks like Borderlands 2. (toon shading + black outlines)
I'm sure I came in late. Let's hope this would solve someone's question later.
Here's the deal, you don't need to render everything twice, the overhead actually is not substantial, all you need to do is duplicate the mesh and set the duplicate mesh's material side to "backside". No double passes. You will be rendering two meshes instead, with most of the outline's geometry culled by WebGL's "backface culling".
Here's an example:
var scene = new THREE.Scene();
//Create main object
var mesh_geo = new THREE.BoxGeometry(1, 1, 1);
var mesh_mat = new THREE.MeshBasicMaterial({color : 0xff0000});
var mesh = new THREE.Mesh(mesh_geo, mesh_mat);
scene.add(mesh);
//Create outline object
var outline_geo = new THREE.BoxGeometry(1, 1, 1);
//Notice the second parameter of the material
var outline_mat = new THREE.MeshBasicMaterial({color : 0x00ff00, side: THREE.BackSide});
var outline = new THREE.Mesh(outline_geo, outline_mat);
//Scale the object up to have an outline (as discussed in previous answer)
outline.scale.multiplyScalar(1.5);
scene.add(outline);
For more details on backface culling, check out: http://en.wikipedia.org/wiki/Back-face_culling
The above approach works well if you want to add an outline to objects, without adding a toon shader, and thus losing "realism".
Toon shading by itself supports edge detection. They've developed the 'cel' shader in Borderlands to achieve this effect.
In cel shading devs can either use the object duplication method (done at the [low] pipeline level), or can use image processing filters for edge detection. This is the point at which performance tradeoff is compared between the two techniques.
More info on cel: http://en.wikipedia.org/wiki/Cel_shading
Cheers!
Yes it is possible but not in a simple out-of-the-box way. For toon shading there are even shaders included in /examples/js/ShaderToon.js
For the outlines I think the most commonly suggested method is to render in two passes. First pass renders the models in black, and slightly larger scale. Second pass is normal scale and with the toon shaders. This way you'll see the larger black models as an outline. It's not perfect but I don't think there's an easy way out. You might have more success searching for "three.js hidden line rendering", as, while different look, somewhat similar method is used to achieve that.
Its a old question but here is what i did.
I created a Outlined Cel-shader for my CG course. Unfortunately it takes 3 rendering passes. Im currently trying to figure out how to remove one pass.
Here's the idea:
1) Render NormalDepth image to texture.
In vertex shader you do what you normally do, position to screen space and normal to screen space.
In fragment shader you calculate the depth of the pixel and then create the normal color with the depth as the alpha value
float ndcDepth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) / (gl_DepthRange.far - gl_DepthRange.near);
float clipDepth = ndcDepth / gl_FragCoord.w;
2) Render the scene on to a texture with cel-shading. I changed the scene override material.
3)Make quad and render both textures on the quad and have a orto camera look at it. Cel-shaded texture is just renderd on quad but the normaldepth shaded on that you use some edge detection and then with that you know when the pixel needs to be black(edge).