I'm currently wrapping my brain around three.js and I've imported 3d model I made in C4D via the three.OBJMTLLoader successfully, but I can't get the object to cast a shadow. I've used object.castShadow = true but its not working but I can get geometry created in three.js to cast a shadow so I know the scene is setup ok.
The test scene is currently here: http://kirkd.co.uk/dev/ and has now been updated with the fix suggested below.
The code is below, if someone could kindly point out either what I'm doing wrong or even if imported objects can cast shadows then I'd be eternally grateful.
Ta.
<script>
var container;
var controls;
var camera, scene, renderer;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.z = 500;
camera.position.y = 500;
scene = new THREE.Scene();
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 500, 1000, 500 );
spotLight.castShadow = true;
spotLight.shadowMapWidth = 1024;
spotLight.shadowMapHeight = 1024;
scene.add( spotLight );
var companion = new THREE.OBJMTLLoader();
companion.load( 'companion2.obj', 'companion.mtl', function ( object ) {
object.position.set(0,-20,0);
object.scale.set( 0.8, 0.8, 0.8 );
object.castShadow = true;
scene.add( object );
});
var floorGeometry = new THREE.CubeGeometry(1000,4,1000);
var floorMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.x=0;
floor.position.y=-130;
floor.position.z=0;
floor.receiveShadow = true;
scene.add(floor);
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
mesh = new THREE.Mesh( geometry);
scene.add( mesh );
mesh.position.set(-100,200,10);
mesh.castShadow = true;
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xEEEEEE, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild( renderer.domElement );
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
spotLight.shadowCameraVisible = true;
var step=0;
render();
};
function render() {
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame( animate );
render();
}
</script>
Your object has child meshes, each of which needs to have castShadow set to true.
In your loader callback, add this:
object.traverse( function( node ) { if ( node instanceof THREE.Mesh ) { node.castShadow = true; } } );
three.js r.66
Related
I am new to three.js and 3D model rendering and have been playing around with this sample code that i found on the internet. My aim here is to be able to render my 3D model using the chrome browser.
The default code is :
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
var animate = function () {
requestAnimationFrame( animate );
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
};
animate();
When i go to my browser and type in http://localhost:8080/ i get a cube rotating
Now i try modifying the code to
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 200;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
var keyLight = new THREE.DirectionalLight(new THREE.Color('hsl(30, 100%, 75%)'), 1.0);
keyLight.position.set(-100, 0, 100);
var fillLight = new THREE.DirectionalLight(new THREE.Color('hsl(240, 100%, 75%)'), 0.75);
fillLight.position.set(100, 0, 100);
var backLight = new THREE.DirectionalLight(0xffffff, 1.0);
backLight.position.set(100, 0, -100).normalize();
scene.add(keyLight);
scene.add(fillLight);
scene.add(backLight);
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setTexturePath('/examples/3d-obj-loader/assets/');
mtlLoader.setPath('/examples/3d-obj-loader/assets/');
mtlLoader.load('r2-d2.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath('/examples/3d-obj-loader/assets/');
objLoader.load('r2-d2.obj', function (object) {
scene.add(object);
object.position.y -= 60;
});
});
var animate = function () {
requestAnimationFrame( animate );
controls.update();
renderer.render(scene, camera);
};
animate();
This should give me an image of r2-d2 model being rendered but i see a blank screen
I would really appreciate help in understanding what i am missing.
This is my source code.
I'm trying to make the TextGeometry always look to the camera?It's possible?
Code:
var stats;
var camera, controls, scene, renderer;
init();
render();
function init() {
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0xFFFFFF );
scene.add( ambient );
var container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({antialias: 1 });
renderer.shadowMap.enabled = true;
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight);
renderer.setClearColor(0x013A65);
container.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5000 );
camera.position.set(0,0,0);
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render );
controls.enableKeys = false;
controls.enableZoom = true;
controls.minDistance = 2000;
controls.maxDistance = 3500;
controls.maxPolarAngle = Math.PI/2;
// world
var onError = function ( xhr ) { };
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setPath( './assets/3d/' );
mtlLoader.load( 'A.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
mtlLoader.setPath( './assets/3d/' );
objLoader.load( './assets/3d/A.obj', function ( object ) {
object.position.x = 0;
object.position.y = 0;
object.position.z = 0;
scene.add( object );
}, onError );
});
//Text
var loader = new THREE.FontLoader();
loader.load( './fonts/Open_Sans_Regular.json', function ( font ) {
var textGeometry = new THREE.TextGeometry( "Test", {font: font, size: 22, height: 3, curveSegments: 1});var textMaterial = new THREE.MeshPhongMaterial({ color: 0xFFFFFF, specular: 0xFFFFFF });var mesh1 = new THREE.Mesh( textGeometry, textMaterial );mesh1.position.x = -200;mesh1.position.y = 250;mesh1.position.z = 725;scene.add( mesh1);});
// lights
light = new THREE.DirectionalLight( 0xFFFFFF );
light.position.set( 1, 1, 1 );
scene.add( light );
window.addEventListener( 'resize', onWindowResize, true );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
stats.update();
render();
}
function render() {
renderer.render( scene, camera );
}
I already tried mesh.lookAt(camera.position) inside the render function but no sucess.
Best Regards.
I believe your question is how to make the text appear regardless of where the camera is. Instead of using text geometry you can try to put your text in a div container and make the position absolute using CSS. https://threejs.org/docs/#manual/en/introduction/Creating-text
I want to add a texture to my plane that repeats horizontal and vertical. The thing is, when I try to apply the texture, it is always black. I don't get any errors, and i already tried to add some lights, but the problem is still there; I don't know how to solve it... Here is what I did:
window.onload = function init()
{
scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
var light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );
var spotlight = new THREE.SpotLight( 0xffffff);
spotlight.position.set( -50, 40, 0 );
scene.add( spotlight );
var axes = new THREE.AxisHelper( 20 ); scene.add(axes);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xEEEEEE);
renderer.setSize(window.innerWidth, window.innerHeight);
desenhaMapa();
document.body.appendChild( renderer.domElement );
renderer.render(scene, camera);
}
function desenhaMapa()
{
labirinto = new THREE.Object3D();
var texturaPlano = new THREE.TextureLoader().load("texturaPac.jpg");
geometryPlano = new THREE.PlaneGeometry(50,50);
materialPlano = new THREE.MeshPhongMaterial( {map: texturaPlano} );
var planoPacMan = new THREE.Mesh(geometryPlano,materialPlano);
planoPacMan.rotation.x = -0.5 * Math.PI;
scene.add(planoPacMan);
}
Any suggestions?
TextureLoader.load() is an asynchronous method. That is why it has an onload argument.
You are calling render() before the texture loads. One solution is to call render() in the loader callback.
var loader = new THREE.TextureLoader();
var texture = loader.load( 'myTexture.jpg', function ( texture ) {
renderer.render( scene, camera );
} );
Another solution is to have an animation loop. But that is not required for static scenes.
three.js r.78
I have created a javascript script to do something, and mostly it worked. There are two issues I had with the script, so I planned to put a working example here on SO. However, during the 'reduction' of the code to something less complex a third error occurred, which completely made the script stop working. Here is the javascript part of the script:
var container, stats;
var camera, scene, renderer;
var group1, group2;
var mouseX = 0, mouseY = 0;
var map_width = 512;
var map_height = 512;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function createMesh(filename) {
var geometry = new THREE.SphereGeometry( 70, 40, 40 );
var texture = new THREE.TextureLoader(filename);
texture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } );
var mesh = new THREE.Mesh( geometry, material );
return mesh;
}
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 500;
scene = new THREE.Scene();
group1 = new THREE.Group();
var mesh = createMesh("textures/tree1.jpg");
group1.add( mesh );
group1.position.x = 00;
scene.add( group1 );
group2 = new THREE.Group();
var mesh = createMesh("textures/tree2.jpg");
group2.add( mesh );
group2.position.x = 250;
scene.add( group2 );
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setClearColor( 0xffffff, 0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * 0.05;
camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
camera.lookAt( scene.position );
group1.rotation.y -= 0.005;
group2.rotation.y -= 0.015;
renderer.render( scene, camera );
}
The idea is to have several spheres with some texture on it; to try just use any image. However, when I call this code within a html I get the error
TypeError: n is undefined
all over again. The error seems to originate in THREE.js. How can I fix this error, so I see two spheres with a 'tree' texture on it...?
The body of the html code is as follows:
<body>
<div id="container"></div>
<script src="js/build/three.min.js"></script>
<script>
>> the code from above <<
</script>
</body>
This problem is caused by the following line:
var texture = new THREE.TextureLoader(filename);
The solution is to use a loader instead:
var loader = new THREE.TextureLoader();
var texture = loader.load( filename );
I have a scene with two collada objects and a directional light.
The first collada is pretty much a plane, and the second one is made of multiple boxes.
It appears that when the scene is rendered, some "side" shadows are really stripped, although the shadows casted on ground are pretty well rendered.
.
As I was searching for an answer, I figured out it might be a problem with my collada, so I added a basic cube to the scene (the big one above all), but it seems it has the same problem.
Does anyone have a tip or know this problem already?
I'm using the last three.js revision atm (r71), tested on Google Chrome and Mozilla Firefox (MacOS).
I already tried to tweak pretty much all the shadow* attributes of the directional light, except the ones related with shadowCascade (which I don't use).
I also tested to tweak shadow-related renderer's attributes.
Here's my light setup:
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 );
directionalLight.target.position = THREE.Vector3(0, 0, 0);
directionalLight.position.set( 250, 500, 250 );
directionalLight.castShadow = true;
directionalLight.shadowCameraNear = 100;
directionalLight.shadowCameraFar = 1000;
directionalLight.shadowMapWidth = 2048;
directionalLight.shadowMapHeight = 2048;
directionalLight.shadowBias = 0.0001;
directionalLight.shadowDarkness = 0.5;
directionalLight.shadowCameraLeft = -300;
directionalLight.shadowCameraRight = 300;
directionalLight.shadowCameraTop = 300;
directionalLight.shadowCameraBottom = -300;
directionalLight.shadowCameraVisible = true;
My collada objects are kind of big, so are my shadowCamera bounds.
My renderer setup:
renderer = new THREE.WebGLRenderer( {antialias: true} );
renderer.setClearColor(0x222222);
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
Here's another view of the scene
(mostly showing my light setup).
EDIT: Here's a snippet:
var container;
var camera, scene, renderer, controls;
var particleLight;
scene = new THREE.Scene();
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set( 300, 100, 0 );
// Controls
controls = new THREE.OrbitControls( camera );
// Cube
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
var material = new THREE.MeshLambertMaterial();
var cube = new THREE.Mesh( geometry, material );
cube.position.y = 50;
cube.rotation.y = 0.8;
cube.castShadow = true;
cube.receiveShadow = true;
scene.add( cube );
// Plane
var planeGeometry = new THREE.PlaneBufferGeometry( 300, 300, 300 );
var plane = new THREE.Mesh( planeGeometry, material );
plane.position.set( 0, 0, 0 );
plane.rotation.x = -1.6;
plane.castShadow = true;
plane.receiveShadow = true;
scene.add( plane );
// Light
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 );
directionalLight.target.position = THREE.Vector3(0, 0, 0);
directionalLight.position.set( 250, 500, 250 );
directionalLight.castShadow = true;
directionalLight.shadowCameraNear = 100;
directionalLight.shadowCameraFar = 1000;
directionalLight.shadowMapWidth = 2048;
directionalLight.shadowMapHeight = 2048;
directionalLight.shadowBias = 0.0001;
directionalLight.shadowDarkness = 0.5;
directionalLight.shadowCameraLeft = -300;
directionalLight.shadowCameraRight = 300;
directionalLight.shadowCameraTop = 300;
directionalLight.shadowCameraBottom = -300;
directionalLight.shadowCameraVisible = true;
scene.add( directionalLight );
scene.add( new THREE.AmbientLight( 0x555555 ) );
renderer = new THREE.WebGLRenderer( {antialias: true} );
renderer.setClearColor(0x222222);
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.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 );
render();
}
var clock = new THREE.Clock();
function render() {
var timer = Date.now() * 0.0005;
camera.lookAt(new THREE.Vector3(0, 0, 0));
renderer.render( scene, camera );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
Thanks in advance.
You are getting self-shadowing because the rays of the directional light are exactly parallel to some of the faces of your geometry. Move the directional light to a slightly different location. For example,
directionalLight.position.set( 250, 500, 200 );
Usually, light.shadow.bias is used to remedy self-shadowing problems, but it is not going to be effective when the light ray and face are parallel, as in your case.
Also, set shadow.bias to be negative to help alleviate artifacts on other faces.
directionalLight.shadow.bias = - 0.01;
This unfortunately will result, as it usually does, in another artifact: "Peter Panning".
These kinds of trade-offs are common. You are just going to have to find an acceptable compromise. (Maybe set the bias based on camera position.)
three.js r.75