I don't understand why the lighting does not work in my code. I downloaded a simple OBJ. file to test the OBJLoader but the model isn't affected. Before I edited the lighting more, at least the Ambient Lighting would work. Maybe the OBJ. model needs a texture?
var container, stats;
var camera, scene, renderer, controls;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 2.5;
scene.add( camera );
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 2.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.0;
controls.noZoom = false;
controls.noPan = true;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
var ambient = new THREE.AmbientLight( 0x020202 );
scene.add( ambient );
directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 1, 1, 0.5 ).normalize();
scene.add( directionalLight );
pointLight = new THREE.PointLight( 0xffaa00 );
pointLight.position.set( 0, 0, 0 );
scene.add( pointLight );
sphere = new THREE.SphereGeometry( 100, 16, 8 );
lightMesh = new THREE.Mesh( sphere, new THREE.MeshBasicMaterial( { color: 0xffaa00 } ) );
lightMesh.scale.set( 0.05, 0.05, 0.05 );
lightMesh.position = pointLight.position;
scene.add( lightMesh );
var loader = new THREE.OBJLoader();
loader.load( "originalMeanModel.obj", function ( object ) {
scene.add( object );
} );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
MeshBasicMaterial in THREE.js is like a toon shader (good for silouhette, shadow drawing or wireframe) and and is not affected by lights.
Try MeshLambertMaterial or MeshPhongMaterial
I had similar problems when using the Three.js exporter for Blender, everything appeared dark even with diffuse colors set in the original blender model and an ambient light added to the scene in the Three.js code. It turns out the fix was to edit part of the converted model file, there was a line to the effect of:
"colorAmbient" : [0, 0, 0]
which I manually changed to
"colorAmbient" : [0.75, 0.75, 0.75]
everywhere it appeared, and that fixed the problem. I bring this up because my best guess is that you are experiencing a problem similar to this. Without seeing the *.obj file it is difficult to diagnose the problem exactly, but perhaps in your model settings you could try changing the ambient color value rather than, say, the diffuse color value, which is what we normally think of when assigning color to a model.
Maybe this will help you if you are experiencing the same problem like me a few days ago, if you have no normals in your obj that's definitely somewhere to look at.
You can try to start with a MeshBasicMaterial as well just to check the vertices/ faces are ok: new THREE.MeshBasicMaterial({ color: 0x999999, wireframe: true, transparent: true, opacity: 0.85 } )
Also, as mr doob said, please consider sharing the obj you're loading.
Related
In ThreeJS, I am trying to bring my camera closer to the rendered mesh so it's taking up more of the viewport. I have messed around with the camera.position.set values but these only seem to change the rotation, not actually bring it closer/further away from the object.
I'm using the OrbitControls module and think this may be overriding the default settings, but am a bit stumped as to how the distance can be changed?
Any help is much appreciated.
Full source
function init() {
// renderer
renderer = new THREE.WebGLRenderer( { canvas: artifactCanvas, antialias: true } );
renderer.setSize(800, 600);
// scene
scene = new THREE.Scene();
scene.add( new THREE.GridHelper( build_plate_size_mm, build_plate_grid_segments ) );
scene.background = new THREE.Color( 0xc5e5fc );
// camera
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 3000 );
camera.position.set( 1000, 500, 1000 );
camera.lookAt( 0, 200, 0 );
//controls
orbit = new THREE.OrbitControls( camera, renderer.domElement );
orbit.update();
orbit.addEventListener( 'change', render );
control = new THREE.TransformControls( camera, renderer.domElement );
control.showY = false;
control.addEventListener( 'change', render );
control.addEventListener( 'dragging-changed', function ( event ) {
orbit.enabled = ! event.value;
} );
Try setting the position of the camera via the THREE.Vector3 method.
camera.lookAt(new THREE.Vector3(0,0,0));
Refrence
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
I'm working on an app that should allow users to manipulate 3D objects in the scene and observe how their changes affect the ground shadow:
In this scene, the yellow cylinder casts a shadow on a white plane with the middle of the cylinder contained in the green cube. What I would like to happen is for the cube to remove the middle of the shadow, like so:
Obviosly, my first thought was to subtract the green cube volume from the yellow cylinder volume and after a bit of googling I found CSG.js. Unfortunately, CSG.js is too slow for the actual model that I'm going to use, which will going to have at least 15k vertices.
I started digging into the Three.js source and reading about shadow maps to understand how shadows are produced, but my shader-fu is not strong enough yet to fully grasp how I can tweak shadow rendering.
How can I achieve this "shadow subtraction" effect?
var camera, scene, renderer;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 500;
camera.position.y = 100;
camera.lookAt(scene.position);
var ambient = new THREE.AmbientLight(0x909090);
scene.add(ambient);
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.0 );
directionalLight.position.set( -300, 300, 0 );
directionalLight.castShadow = true;
directionalLight.shadow.camera.near = 10;
directionalLight.shadow.camera.far = 2000;
directionalLight.shadow.camera.right = 350;
directionalLight.shadow.camera.left = -350;
directionalLight.shadow.camera.top = 350;
directionalLight.shadow.camera.bottom = -350;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
scene.add( directionalLight );
//var lightHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
//scene.add(lightHelper);
var geometry = new THREE.CylinderGeometry( 50, 50, 400, 32 );
var material = new THREE.MeshPhongMaterial( {color: 0xffff00} );
var cylinder = new THREE.Mesh( geometry, material );
cylinder.castShadow = true;
scene.add( cylinder );
var geometry = new THREE.BoxGeometry( 110, 110, 110 );
var material = new THREE.MeshPhongMaterial( {color: 0x00ff00} );
var cube = new THREE.Mesh( geometry, material );
cube.castShadow = true;
scene.add( cube );
var geometry = new THREE.PlaneGeometry( 3000, 3000, 32 );
var material = new THREE.MeshPhongMaterial( {color: 0xffffff, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.lookAt(new THREE.Vector3(0, 1, 0));
plane.position.y = -200;
plane.receiveShadow = true;
scene.add( plane );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
jsFiddle
Update:
What about a more complicated scene? Is it possible for the shadow from the red cylinder to be unaffected (you can see it being cut in half with cube.customDepthMaterial = new THREE.MeshBasicMaterial({ depthTest: false}))?
Updated jsFiddle
You can subtract an object's shadow from the rest of scene by setting the object's .customDepthMaterial property like so:
var cube = new THREE.Mesh( geometry, material );
cube.castShadow = true;
cube.receiveShadow = false;
// The secret sauce
cube.customDepthMaterial =
new THREE.MeshBasicMaterial({ depthTest: false});
scene.add( cube );
jsFiddle
No shader-fu required.
Why This Works
When the shadow map is rendered, each object's depth material ( .customDepthMaterial or the default ) is used to render the scene from the light's perspective. The depth material's resulting render represents the object's depth from the camera packed as RGBA. Since THREE.MeshBasicMaterial defaults to { color: 0xffffff, opacity: 1 }, it will return the maximum depth which makes the object further than the shadow camera's far.
I disabled depthTest because in your desired result screenshot you clipped the area where the cube's given the cylinder wasn't there. Disabling depthTest means that parts of the cube which are blocked by the cylinder will still cut out the shadow, giving you your desired result.
Documentation
There unfortunately is no documentation on .customDepthMaterial yet but I did find an official example where it is used.
Updated Answer:
To allow an object's shadow to always show:
You can use the same trick as above just setting the material's color and opacity to 0
Make sure it's added to the scene after the 'subtractive shadow' object. This way the additive shadow will win out even though they both have depthTest disabled.
updated jsFiddle
If you have anything more complicated, it will be up to you to figure out a way to manage the order of the shadow rendering.
Tested in r77
This is my first question in StackOverflow, but I've been browsing it for some years now, so I kindly ask you to bear with me. :)
I've been experimenting with Three.js to create a 3D world, and everything looked fine until I needed to control the camera. Since I'm using this lib to avoid having to do matricial calculations myself I found and added TrackballControls to my code aswell. It worked fine but then my camera could pass through the 3D shapes, and also below terrain. Unfortunately, although the movement is exactly what I needed, it didn't serve the purpose of allowing camera to respect collision.
My scene is simply the ground (thin BoxGeometry) and a cube (normal-sized BoxGeometry), and a rotating sphere that shares directionalLight position for a "sun light" effect. Some people here suggested adding Physijs to the code and simulate() physics within the scene, and adding a BoxMesh to the camera to make the physics apply to it aswell, but it simply didn't work (scene turned blank).
My working code so far (without Physijs) is:
window.onload = function() {
var renderer, scene, camera, ground, box, sphere, ambient_light, sun_light, controls;
var angle = 0;
var clock = new THREE.Clock();
init();
render();
function init(){
// Create renderer and add it to the page
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xffffff );
renderer.shadowMapEnabled = true;
document.body.appendChild( renderer.domElement );
// Create a scene to hold our awesome 3D world
scene = new THREE.Scene();
/*** 3D WORLD ***/
// Objects
ground = new THREE.Mesh(
new THREE.BoxGeometry(50, 1, 50),
new THREE.MeshLambertMaterial({ color: 0x33CC33 }),
0 // mass
);
ground.receiveShadow = true;
scene.add( ground );
box = new THREE.Mesh(
new THREE.BoxGeometry( 10, 10, 10 ),
new THREE.MeshLambertMaterial({ color: 0xDD3344 })
);
box.position.y = 5;
box.castShadow = true;
scene.add( box );
sphere = new THREE.Mesh(
new THREE.SphereGeometry( 3, 32, 32 ),
new THREE.MeshBasicMaterial({ color: 0xFFBB00 })
);
sphere.position.set( 1, 15.5, 5 );
scene.add( sphere );
// Light
ambient_light = new THREE.AmbientLight( 0x333333 );
ambient_light.mass = 0;
scene.add( ambient_light );
sun_light = new THREE.DirectionalLight( 0xBBBBBB );
sun_light.position.set( 1, 15.5, 5 );
sun_light.castShadow = true;
sun_light.shadowCameraNear = 1;
sun_light.shadowCameraFar = 100;
sun_light.shadowCameraLeft = -50;
sun_light.shadowCameraRight = 50;
sun_light.shadowCameraTop = -50;
sun_light.shadowCameraBottom = 50;
sun_light.shadowBias = -.01;
scene.add( sun_light );
// Create a camera
camera = new THREE.PerspectiveCamera(
45, // FOV
window.innerWidth / window.innerHeight, // Aspect Ratio
1, // Near plane
1000 // Far plane
);
camera.position.set( 30, 30, 30 ); // Position camera
camera.lookAt( box.position ); // Look at the scene origin
scene.add(camera);
// After swapping THREE.Mesh to Physijs.BoxMesh, this is where I'd attach a BoxMesh to the camera
window.addEventListener( 'resize', onWindowResize, false );
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 4.0;
controls.panSpeed = 0.3;
controls.staticMoving = true; // No sliding after-effects
}
function render() {
// use requestAnimationFrame to create a render loop
angle += .007;
var oscillateZ = Math.sin(angle * (Math.PI*4));
var oscillateX = -Math.cos(angle * (Math.PI*4));
//console.log(oscillateZ);
sphere.position.setZ( sphere.position.z + oscillateZ );
sphere.position.setX( sphere.position.x + oscillateX );
sun_light.position.setZ( sun_light.position.z + oscillateZ );
sun_light.position.setX( sun_light.position.x + oscillateX );
requestAnimationFrame( render );
controls.update();
renderer.render( scene, camera );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
};
Can you guys enlighten me? Thank you for your time!
#Edit
Physijs attempt
I have a mesh, created in blender and exported to .obj. The mesh looks valid and has UV map applied and exported into the same .obj as well. For some reason, when I try to apply a texture material, or even basic material to the mesh, only half of the hexagon is actually painted.
This is a mesh
This is the code
var container;
var camera, scene, renderer;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.5, 3000000 );
camera.position.set( 2000, 750, 2000 );
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.userPan = false;
controls.userPanSpeed = 0.0;
controls.maxDistance = 5000.0;
controls.maxPolarAngle = Math.PI * 0.495;
controls.center.set( 0, 1, 0 );
var light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
light.position.set( - 1, 1, - 1 );
scene.add( light );
waterNormals = new THREE.ImageUtils.loadTexture( 'textures/waternormals.jpg' );
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;
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,
} );
var loader = new THREE.OBJMTLLoader();
loader.load( "models/world.obj", "models/world.mtl", function(object)
{
console.log(object.children[0].children[1].geometry);
var mesh = new THREE.Mesh(
object.children[0].children[1].geometry,
new THREE.MeshBasicMaterial
);
scene.add(mesh);
});
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
controls.update();
renderer.render( scene, camera );
}
And this is how it looks:
When I split the hexagons into 2 quads it works perfectly, thing is, I need faces to stay hexagons for picking, the faces I want to be selected are hexagons.