Three.js - Faces missing after Blender import - three.js

I tried to import a JSON exported file of my 3D model and import it in Three.js but it seems some faces are missing.
I am not sure if it was an export problem because when I rotate it, the left face exists but the right face does not, vice versa.
Here is my original model in Blender:
var scene, camera, renderer;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.01;
function init() {
scene = new THREE.Scene();
initMesh();
initCamera();
initLights();
initRenderer();
document.body.appendChild(renderer.domElement);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
camera.position.set(0, 3.5, 5);
camera.lookAt(scene.position);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
}
function initLights() {
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 0, 1, 0 );
scene.add( directionalLight );
}
var mesh = null;
function initMesh() {
var loader = new THREE.JSONLoader();
loader.load('./model.json', function(geometry, materials) {
mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.75;
mesh.translation = THREE.GeometryUtils.center(geometry);
mesh.position.x = -5;
scene.add(mesh);
});
}
function rotateMesh() {
if (!mesh) {
return;
}
mesh.rotation.y -= SPEED;
}
function render() {
requestAnimationFrame(render);
rotateMesh();
renderer.render(scene, camera);
}
init();
render();
Hope you can help me with this problem.
Thanks in advance!

I would suspect your problem has to do with the face-normals pointing in the wrong direction. To check if this is the case you could try to set all materials to double-sided:
materials.forEach(function(mat) {
mat.side = THREE.DoubleSide;
});
With Double-Sided mode, the faces are drawn regardless of the normals direction, so you should see all faces if enabled.
Or you could use the THREE.FaceNormalsHelper to have a look at the normals yourself.
scene.add(new THREE.FaceNormalsHelper(mesh, 2, 0x00ff00, 1));
This will render arrows for all faces indicating the normal-direction.
If the normals are wrong you can fix this in blender by selecting all affected faces and using the command Mesh>Faces>Flip Normals from the menu or in the Tools-Panel on the right-hand side in the "Shading/UV"-Tab. Sometimes just selecting all faces and running "Recalculate Normals" from the Tools will work as well.
Blender also has a display-mode for face-normals in the right hand menu in the "Mesh Display"-Section.

Related

Three.js LegacyGLTFLoader.js shadows missing

I have a GLTF version 1.0 model that I am importing into Three.js using LegacyGLTFLoader.js. When I do so, everything looks good, except that the model does not receive shadows. I am guessing that this is because the imported model's material is THREE.RawShaderMaterial, which does not support receiving shadows (I think). How can I fix this so that my imported model can receive shadows?
Here is sample code:
// Construct scene.
var scene = new THREE.Scene();
// Get window dimensions.
var width = window.innerWidth;
var height = window.innerHeight;
// Construct camera.
var camera = new THREE.PerspectiveCamera(75, width/height);
camera.position.set(20, 20, 20);
camera.lookAt(scene.position);
// Construct renderer.
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.shadowMapEnabled = true;
document.body.appendChild(renderer.domElement);
// Construct cube.
var cubeGeometry = new THREE.BoxGeometry(10, 1, 10);
var cubeMaterial = new THREE.MeshPhongMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.translateY(15);
scene.add(cube);
// Construct floor.
var floorGeometry = new THREE.BoxGeometry(20, 1, 20);
var floorMaterial = new THREE.MeshPhongMaterial({color: 0x00ffff});
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.receiveShadow = true;
scene.add(floor);
// Construct light.
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 20, 0);
light.castShadow = true;
scene.add(light);
// Construct light helper.
var lightHelper = new THREE.DirectionalLightHelper(light);
scene.add(lightHelper);
// Construct orbit controls.
new THREE.OrbitControls(camera, renderer.domElement);
// Construct GLTF loader.
var loader = new THREE.LegacyGLTFLoader();
// Load GLTF model.
loader.load(
"https://dl.dropboxusercontent.com/s/5piiujui3sdiaj3/1.glb",
function(event) {
var model = event.scene.children[0];
var mesh = model.children[0];
mesh.receiveShadow = true;
scene.add(model);
},
null,
function(event) {
alert("Loading model failed.");
}
);
// Animates the scene.
var animate = function () {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
// Animate the scene.
animate();
Here are my resources:
https://dl.dropboxusercontent.com/s/y2r8bsrppv0oqp4/three.js
https://dl.dropboxusercontent.com/s/5wh92lnsxz2ge1e/LegacyGLTFLoader.js
https://dl.dropboxusercontent.com/s/1jygy1eavetnp0d/OrbitControls.js
https://dl.dropboxusercontent.com/s/5piiujui3sdiaj3/1.glb
Here is a JSFiddle:
https://jsfiddle.net/rmilbert/8tqc3yx4/26/
One way to fix the problem is to replace the instance of RawShaderMaterial with MeshStandardMaterial. To get the intended effect, you have to apply the existing texture to the new material like so:
var newMaterial = new THREE.MeshStandardMaterial( { roughness: 1, metalness: 0 } );
newMaterial.map = child.material.uniforms.u_tex.value;
You also have to compute normal data for the respective geometry so lighting can be computed correctly. If you need no shadows, the unlint MeshBasicMaterial is actually the better choice.
Updated fiddle: https://jsfiddle.net/e67hbj1q/2/

Centering pivot point in three.js with OrbitControls autorotate

I'm loading a .glb model into three.js, and while I have it rotating automatically using OrbitControls, I'm not able to see how to change the pivot point so the rotating model is centered.
I've seen a lot of questions on setting boxes or pivot points with rotation, but not with OrbitControls and autorotate. Is there a way for me to center the imported model using autorotate as per my code below?
var scene = new THREE.Scene();
// Load Camera Perspective
var camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 1, 8000 );
camera.position.set( 200, 100, 0 );
// Load a Renderer
var renderer = new THREE.WebGLRenderer({ alpha: false });
renderer.setClearColor( 0xC5C5C3 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Load the Orbitcontroller
var controls = new THREE.OrbitControls( camera, renderer.domElement );
camera.position.set( 60, 20, 100 );
controls.update();
controls.autoRotate = true;
controls.minDistance = 700;
controls.maxDistance = 2000;
//controls.update();
// Load Light
var ambientLight = new THREE.AmbientLight( 0xcccccc );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 0, 1, 1 ).normalize();
scene.add( directionalLight );
// glTf 2.0 Loader
var loader = new THREE.GLTFLoader();
loader.load( 'BTR.glb', function ( gltf ) {
var object = gltf.scene;
gltf.scene.scale.set( 1, 1, 1 );
gltf.scene.position.x = 0; //Position (x = right+ left-)
gltf.scene.position.y = 0; //Position (y = up+, down-)
gltf.scene.position.z = 0; //Position (z = front +, back-)
scene.add( gltf.scene );
});
function animate() {
// required if controls.enableDamping or controls.autoRotate are set to true
controls.update();
render();
requestAnimationFrame( animate );
}
function render() {
renderer.render( scene, camera );
}
render();
animate();
I think this issue can be solved by setting Controls.target (the focus point) to the center point of your glTF asset. You should be able to do this like so:
var aabb = new THREE.Box3().setFromObject( gltf.scene );
aabb.getCenter( controls.target );
controls.update();
three.js R107
Correct way to set target.
var aabb = new THREE.Box3().setFromObject( gltf.scene );
controls.target.set(aabb.getCenter());
controls.update();
it should take (aab.getCenter()), as it returns a vector3 with 3 axis values. But I found this didn't work for me, so I used the following
let aabb = new THREE.Box3().setFromObject( gltf.scene );
let aabbc = aabb.getCenter()
controls.target.set(aabbc.x, aabbc.y, aabbc.z);
controls.update();
just separating into 3 values, if you ever get stuck just console.log(whateveryourstuckwith) and read through the methods and variables and stuff, really helped me understand Three.js more

THREE DirectionalLight falling off with distance

According to the docs for THREE.DirectionalLight:
This light will behave as though it is infinitely far away and the rays produced from it are all parallel.
But, I'm finding that as a move an object with THREE.MeshStandardMaterial farther away from the light (but kept at the same relative angle), the intensity of the light decreases. This doesn't seem right to me.
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
var dpr = window.devicePixelRatio;
var renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true});
renderer.setPixelRatio(dpr);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.z = 700;
var planeGeom = new THREE.PlaneGeometry(200, 200);
var plane = new THREE.Mesh(planeGeom, new THREE.MeshStandardMaterial({color: 0xff00ff, metalness: 1}));
plane.position.z = -10;
scene.add(plane);
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(-50, 50, 100);
scene.add(light);
var helper = new THREE.DirectionalLightHelper(light, 10);
scene.add(helper);
function update(time) {
plane.position.x = 200 * Math.sin(time);
plane.position.y = 200 * Math.cos(time/2);
}
function render() {
requestAnimationFrame(render);
update(performance.now() / 1000);
renderer.render(scene, camera);
}
render();
body {
margin: 0;
padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
I expect the plane to be lit the same no matter what position it's in. What am I doing wrong?
You are using MeshStandardMaterial and have set metalness to 1.
Metals reflect primarily specularly; the diffuse component of the reflection is minimal.
So what you are seeing is the "hot spot" of the reflection. Set the metalness to zero, for example, and you will see primarily a diffuse reflection.
Also, when using MeshStandardMaterial, you should include an environment map (material.envMap) so there is something to reflect. Materials -- especially metals -- will look much better that way.
three.js r.87

Loading External Shaders Using THREE.JS

I am trying to make a THREE.JS scene with GLSL shaders applied, but am having trouble figuring out how to make it load my shaders. The scene appears to work, but the shaders don't do what they're supposed to. I'm using a shader loader function I found that uses pure THREE.JS instead of AJAX or Jquery.
Here is the main javascript for my scene.
var container,
renderer,
scene,
camera,
light,
mesh,
controls,
start = Date.now(),
fov = 30;
window.addEventListener( 'load', function() {
container = document.getElementById( "container" );
if(!Detector.webgl) {
Detector.addGetWebGLMessage(container);
return;
}
//get the width and height
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
//sphere params
var radius = 20,
segments = 4,
rotation = 6;
scene = new THREE.Scene();
// ASPECT RATIO
camera = new THREE.PerspectiveCamera(fov, WIDTH / HEIGHT, 1, 10000);
camera.position.z = 100;
scene.add(new THREE.AmbientLight(0x333333));
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 3, 5);
scene.add(light);
;
ShaderLoader('./shaders/vertex.vert', './shaders/fragment.frag')
material = new THREE.ShaderMaterial( {
uniforms: {
tExplosion: {
type: "t",
value: THREE.ImageUtils.loadTexture( 'images/explosion.png' )
},
time: { //float initialized to 0
type: "f",
value: 0.0
}
},
vertexShader: ShaderLoader.vertex_text,
fragmentShader: ShaderLoader.fragment_text
} );
mesh = new THREE.Mesh(
new THREE.IcosahedronGeometry( radius, segments ),
material
);
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setSize( WIDTH, HEIGHT );
renderer.setPixelRatio( window.devicePixelRatio );
//update renderer size, aspect ratio, and projectionmatrix, on resize
window.addEventListener('resize', function() {
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
});
controls = new THREE.TrackballControls( camera );
container.appendChild( renderer.domElement );
render();
} );
function render() {
material.uniforms[ 'time' ].value = .00025 * ( Date.now() - start );
controls.update();
requestAnimationFrame( render );
renderer.render(scene, camera);
}
// This is a basic asyncronous shader loader for THREE.js.
function ShaderLoader(vertex_url, fragment_url, onLoad, onProgress, onError) {
var vertex_loader = new THREE.XHRLoader(THREE.DefaultLoadingManager);
vertex_loader.setResponseType('text');
vertex_loader.load(vertex_url, function (vertex_text) {
var fragment_loader = new THREE.XHRLoader(THREE.DefaultLoadingManager);
fragment_loader.setResponseType('text');
fragment_loader.load(fragment_url, function (fragment_text) {
onLoad(vertex_text, fragment_text);
});
}, onProgress, onError);
}
But when my scene loads, it is just a red sphere with no lighting or applied shaders... What am I doing wrong? I'm new to all of this so it is probably something easily noticeable for someone more experienced, but I have searched and searched and been experimenting and can't figure it out.
Thanks!
Try to add material.needsUpdate = true after you load your shader.
The snippet show error of the WebGL detector.
Are you loading that library?
https://github.com/mrdoob/three.js/blob/master/examples/js/Detector.js

EdgesHelper loses its mesh when scene is rotated

I've drawn a mesh with a edgeshelper.
When I rotate the scene using the mouse, the edges seems to react twice as fast the mesh.
The result is that the edges don't "fit" around the mesh anymore when the scene is rotated.
What am I doing wrong ?
<html>
<body onmousemove="bmousemove(event);">
<script src="three.min.js"></script>
<script>
var prevX = 0, prevY = 0;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight, 1,10000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
geometry = new THREE.BoxGeometry( 10, 20, 40, 1, 1, 1 );
material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
object = new THREE.Mesh( geometry, material );
edges = new THREE.EdgesHelper( object, 0xffffff );
camera.position.z = 100;
scene.add( object );
scene.add( edges );
render();
function render()
{
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function bmousemove(e)
{
if (prevX == 0)
{
prevX = e.clientX;
prevY = e.clientY;
}
scene.rotation.y += (e.clientX - prevX) / 100;
scene.rotation.x += (e.clientY - prevY) / 100;
prevX = e.clientX;
prevY = e.clientY;
}
</script>
</body>
</html>
I'm using version r71 under Windows 7
Yes, this is a consequence of how EdgesHelper(and some of the other helpers) have been implemented.
If you want to use EdgesHelper, you have to adhere to two rules:
EdgesHelper must be added to the scene, directly.
The scene can't have a transform applied (for example, be rotated).
Consequently, you will have to rotate the mesh, instead.
three.js r.71

Resources