I'm doing something wrong with SpotLights and shadows in Three.js - javascript

I have a really simple scene which has one .dae mesh in it, and a 7000*7000 plane underneath the mesh. I'd like it to be lit by a high SpotLight, so the mesh throws a shadow on the ground. But, something seems to be broken! No matter how high I put the SpotLight, it never lights up the plane! Also, it lights the mesh up only a little, while it is in a small square (perimeter).
You can see the situation here:
As soon as I move the mesh (a monster) around, it wont be lit anymore.
This is how I instantiate the light:
// create a spotlight
self.spotLight = new THREE.SpotLight();
// set its position
self.spotLight.position.y = 1000; //I recon it needs to be relatively high so it lights up everything
self.spotLight.position.x = 0; //(0, 0) are the coordinates where the mesh is spawned, and are the center of the plane
self.spotLight.position.z = 0;
self.spotLight.castShadow = true;
This is how the plane is made:
//The plane.
self.plane = new THREE.Mesh(new THREE.PlaneGeometry(self.groundSize, self.groundSize), new THREE.MeshLambertMaterial({color: 0x5C8A00}));
self.plane.receiveShadow = true;
self.plane.position.x = 0;
self.plane.position.y = -26;
self.plane.position.z = 0;
Also, here's another picture, this time, I've added a lot of PointLights:
You can see how the shadow still disappears!
Now, what am I doing wrong here? AFAIK, light should disperse equally in all directions! And also, there is another problem, I seem to be unable to add multiple SpotLights on the scene! Everything slows down completely if I do so - is this intended? Maybe it's because I enabled shadows on all of them...
#Neil, the same thing happens in your code as well!

I have created a jsfiddle showing a plane with Lambert material and a rotating cube that is casting a shadow, maybe you can see what is different to yours.
edit
Try playing about with some of the params, I can stop the clipping on my demo with:
spotLight.shadowCameraFov = 70;
update demo and moving demo

Related

Three.js Materials are reversed depending on direction of view

I am using Three.js to model a home. The idea is that I can show the home to an architect and discuss it further. The project is supposed to be walkable, so I put together it all on this site. If you visit the site to view the code, use arrow keys to move horizontally relative to the grass, use W/S to move up/down, and A/D to yaw view.
https://bsdillon.github.io/cs200_Spring2020/ThreeJs/solarhouse.html
I was able to make panels (cubes actually) and put textures on them. Everything looks great except that the walls look different depending on the direction of view. See the image below. I added a red line and yellow triangle to help make the geometry more obvious. The image on the left shows the external view of a panel with a clapboard exterior and an open doorway on the left. The image on the right shows the same panel viewed from inside the structure. You can see that from inside the door is still on the left (it should appear on the right) and the stick frame interior also appears to be reversed.
I never noticed this before, but it appears to be the standard for the way I set up these panels. Apparently I'm doing something wrong. The question is how can I show the texture so that the views will be consistent.
var materialArray = new Object();//I set up an object I set up to store materials
function makeMaterial2(filename, repeatX, repeatY)//a method for creating materials from an image file
{
var m = new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( filename ), transparent: true, opacity: 0.9, color: 0xffffff });
return m;
}
//two examples of the material creation I implemented
materialArray["rDoor"] = makeMaterial2("doorRight.png",false,false);
materialArray["crDoor"] = makeMaterial2("doorRightClapboard.png",false,false);
function drawPanel2(x,y,z,x2,y2,z2,str)//this funciton creates a cube with the material in question
{
var cubegeometry = new THREE.BoxGeometry(Math.abs(x-x2),Math.abs(y-y2),Math.abs(z-z2));
var cube = new THREE.Mesh(cubegeometry, materialArray[str]);
cube.position.set((x+x2)/2,(y+y2)/2,(z+z2)/2);
return cube;
}
//adding the panels to the scene with the materials
scene.add(drawPanel2(-10,level,front,0,level+height,front,"rDoor"));
scene.add(drawPanel2(-10,level,front+margin,0,level+height,front+margin,"crDoor"));
You are using a simple BoxGeometry that has standard UV texture coodinates. A texture mapped on this geometry will look the same from all sides (see box example). However, you want that the open space of the door is at the same position. You could do one of the following to achieve that:
Apply different materials and textures to different sides of the box. The 6 sides of BoxGeometry are already indexed for multi material usage.
a) You'll have to flip a texture in an image editing software and save it separately. Load these multiple textures.
b) Clone the texture and set texture.wrapS = THREE.RepeatWrapping; texture.repeat.x = - 1; to flip it (How to flip a Three.js texture horizontally).
Create an array of 6 materials each with its according texture and pass it to the Mesh.
Change the UV texture coordinates of BoxGeometry, such that one side will show the texture flipped.
Your boxes are flat. 4 sides aren't visible. Instead of a BoxGeometry, you could also create a PlaneGeometry. Set material.side: THREE.DoubleSide such that the plane will be visible from both sides. Following this approach, you'll have to rework your drawPanel2 method, because you can't just flatten one side of the geometry, but you have to rotate the plane according the intended orientation of the panel.

In Three.js Is there a way to produce a trail that slowly fades over time?

I am looking to produce an effect very similar to the following example:
https://threejs.org/examples/?q=trails#webgl_trails
However, I would like to make the old trail fade into the background over time -- rather than just persist into an increasingly messy screen.
It seems that the following code will allow you to draw over previous frames without clearing them:
renderer = new THREE.WebGLRenderer( { preserveDrawingBuffer: true } );
renderer.autoClearColor = false;
But I am unsure how to make each frame fade into the background as new frames a drawn on top. It seems like painting over the screen with a somewhat transparent color would work, but I'm not sure how to approach that.
There's now an example using the postprocessing EffectComposer and AfterImagePass.
https://threejs.org/examples/webgl_postprocessing_afterimage.html
You could easily take the example you showed above, and create a very faint black plane that sits directly in front of the camera:
// Make highly-transparent plane
var fadeMaterial = new THREE.MeshBasicMaterial({
color: 0x000000,
transparent: true,
opacity: 0.01
});
var fadePlane = new THREE.PlaneBufferGeometry(1, 1);
var fadeMesh = new THREE.Mesh(fadePlane, fadeMaterial);
// Create Object3D to hold camera and transparent plane
var camGroup = new THREE.Object3D();
var camera = new THREE.PerspectiveCamera();
camGroup.add(camera);
camGroup.add(fadeMesh);
// Put plane in front of camera
fadeMesh.position.z = -0.1;
// Make plane render before particles
fadeMesh.renderOrder = -1;
// Add camGroup to scene
scene.add(camGroup);
This will make sure that every time the scene is rendered, it'll slowly fade the previously-rendered particles to black. You'll need to play with the opacity of fadeMaterial and position of fadeMesh to get the desired effect.
renderOrder = -1; makes sure that you render the plane first (fading out previous particles), and then render the particles, to avoid covering the newest ones.

How do I set the position a camera in three.js without it rotating?

I have an interface I developed with three.js using the CSS3DObject rendering tool.
I have set the orbit to 0 to prevent rotating and limit my movement to panning and zooming.
Please note I'm also using Orbit Control.
I set the position of the camera to x=-2000 with the following code:
camera.position.x=-2000;
camera.position.z=4000;
When I do this, the camera moves positions but is still pointing to (0,0,0) resulting in a skewed look.
So I assume that I need to give it a vector
camera.up = new THREE.Vector3(0,1,0); //keeps the camera horizontal
camera.lookAt(new THREE.Vector3(2000,0,0)); //should point the camera straight forward
Please note that I'm still trying to find a good explanation of how setting up the lookAt works.
After a bit more research, it seems that the orbit control is overriding the camera.lookAt and as a result, doesn't do anything.
To achieve the panning I set the location of the camera x position equal to the value of the target.
I also removed the camera.up line.
var myCameraX = -2000;
var myCameraY = 500;
camera.position.x=myCameraX;
camera.position.y=myCamerYa;
control.target.set(myCameraX,myCameraY,0);
Hope that helps someone.

three.js SpotLight orientation (direction) issue

Well, here is the problem,
Actually what I try to achieve is to place, at some places, some spotlights in a basic three.js example.
Here is the way I try to set the spotlight target position :
var light = new THREE.SpotLight(0xFFFFFF);
light.position.set(0,130,0);
light.target.position.set(200,-130,400);
scene.add(light);
The spotlight (light) keeps lighting the point (0,0,0) even if, when I console.log the target.position.(x,y,z) it gives me the right values...
Here is a quick fiddle I did with my full example.
http://jsfiddle.net/1xfno37y/7/
You have to update your light.target after changing (eg. setting position):
light.target.updateMatrixWorld();
Or just add your light.target to the scene:
scene.add( light.target );
Three.js r.71
http://jsfiddle.net/1xfno37y/19/
Further reading: Critical bug with spotLight.target.position #5555

Three.js disable lighting

I want to disable lighting entirely in three.js, and render a 3D object. Currently, since lighting is active in some form, the object appears completely black. I have no calls to any sort of lighting currently in my program, and I have been unable to find a solution with searching.
Edit: Code
var white = 0xFFFFFF;
var facemat = new THREE.MeshBasicMaterial( { color: white, opacity: 1.0, shading: THREE.FlatShading } );
vrmlLoader.addEventListener('load', function ( event ) {
var object = event.content;
object.material = facemat;
scene.add(object);
});
vrmlLoader.load("ship.wrl");
My questions for this particular post have mostly been answered. If I am to ask more I will drive this post off topic.
Shading / lighting take care of drawing an object in such a way that you can perceive its depth. In your example picture, shading is disabled - there is no depth, the object is the same everywhere - you cannot differentiate parts of it.
Now, it should be pretty obvious that you can't draw stuff without colour - just like you can't draw on a piece of paper without pencils or any other tool. What you are seeing is simply your object drawn with black colour. What you want to see is your object drawn in white, which is almost the same but if you do that currently, you'll see nothing since your background is white!
Anyway, after those clarifications, here is a how-to:
Change the background colour of your renderer to white:
renderer.setClearColor(0, 1)
Change the colour of your object by changing its material:
object.material = new THREE.MeshBasicMaterial(0xFFFFFF)

Categories