Stripped shadows on collada objects - javascript

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

Related

Need help in figuring out why my 3d model is not being rendered in the browser

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.

Plane always black

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

Three.js not rendering texture on external model properly

I am trying to create a very realistic scene using Three.js. So far I have implemented mouse controls, loading model from Maya and I came to applying textures. The code works however, as it can be seen on the images below the textures don't fill the box as one would expect. I assume the problem is because each face of the model is filled separately, which occurred to me when I displayed my model as in wireframe mode.
My UV values look like this :
[[0.375,0,0.625,0,0.375,0.25,0.625,0.25,0.375,0.5,0.625,0.5,0.375,0.75,0.625,0.75,0.375,1,0.625,1,0.875,0,0.875,0.25,0.125,0,0.125,0.25]]
function init() {
// renderer
renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
renderer.setSize(window.innerWidth, window.innerHeight);
container = document.getElementById('container');
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 10000 );
camera.position.z = 5;
cameraControls = new THREE.TrackballControls(camera, renderer.domElement);
cameraControls.target.set(0, 0, 0);
scene = new THREE.Scene();
light = new THREE.AmbientLight( 0xffffff );
scene.add( light );
material = new THREE.MeshPhongMaterial( {
map: THREE.ImageUtils.loadTexture('images/box_texture.jpg')
} );
group = new THREE.Object3D();
var loader = new THREE.JSONLoader();
loader.load('models/cube_1.js', modelLoadedCallback);
window.addEventListener( 'resize', onWindowResize, false );
}
function modelLoadedCallback(geometry) {
mesh = new THREE.Mesh( geometry, material );
group.add(mesh);
// scene.add (new THREE.Mesh (geometry,
// new THREE.MeshBasicMaterial ({ color: 0x000000, wireframe: true })));
scene.add (new THREE.Mesh (geometry, material));
scene.add( group );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
render();
}
function animate() {
var delta = clock.getDelta();
requestAnimationFrame(animate);
cameraControls.update(delta);
renderer.render(scene, camera);
}
It can be helpful
var texture = THREE.ImageUtils.loadTexture('images/box_texture.jpg') ;
and add:
texture.flipY = false;

Strange overlapping rendering in THREE.js (and some shadow issues)

This is my second foray into the world of THREE.js, the first being a short one. I just set up this scene to try some things out. It's a flat box mesh with three box meshes sitting on it. For some reason, the boxes on top are being rendered behind certain polygons of the ground mesh. Anyone have any idea why this is? Does it have something to do with the scale of the scene, or some setting I'm missing? Thanks.
While I have you, does anyone know why the meshes aren't casting directional light shadows onto the ground mesh?
Fiddle: http://jsfiddle.net/k8E43/1/
var scene, camera, renderer;
var material;
var player, ground, mesh1, mesh2;
var directionalLight, spotLight, ambientLight;
init();
animate();
function init() {
renderer = new THREE.CanvasRenderer();
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
renderer.setSize( window.innerWidth, window.innerHeight );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.x = 0;
camera.position.y = 450;
camera.position.z = 750;
camera.lookAt( new THREE.Vector3( 0, 150, 0 ) );
material = new THREE.MeshLambertMaterial( { color: 0xffffff } );
// Meshes
player = new THREE.Mesh( new THREE.BoxGeometry( 70, 160, 70 ), material );
player.position.y = 80;
player.castShadow = true;
player.receiveShadow = true;
scene.add( player );
ground = new THREE.Mesh( new THREE.BoxGeometry( 1000, 10, 600 ), material );
ground.position.y = -5;
ground.receiveShadow = true;
scene.add( ground );
mesh1 = new THREE.Mesh( new THREE.BoxGeometry( 100, 40, 100 ), material );
mesh1.position.x = -150;
mesh1.position.y = 20;
mesh1.position.z = 100;
mesh1.castShadow = true;
mesh1.receiveShadow = true;
scene.add( mesh1 );
mesh2 = new THREE.Mesh( new THREE.BoxGeometry( 100, 200, 100 ), material );
mesh2.position.x = 150;
mesh2.position.y = 100;
mesh2.position.z = -100;
mesh2.castShadow = true;
mesh2.receiveShadow = true;
scene.add( mesh2 );
// Lights
directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.x = -1000;
directionalLight.position.y = 500;
directionalLight.position.z = -1000;
directionalLight.target.position.x = 1000;
directionalLight.target.position.y = -500;
directionalLight.target.position.z = 1000;
directionalLight.castShadow = true;
scene.add( directionalLight );
ambientLight = new THREE.AmbientLight( 0x404040 );
scene.add( ambientLight );
document.body.appendChild( renderer.domElement );
}
var lightIncreasing = false;
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}

imported 3D objects are not casting shadows with three.js

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

Categories

Resources