I set shadow for directional light with shadow map size 2048, shadow bias -0.0001 and shadow type is PCSoftshadow, but rendered shadow seem look like low quality, not smooth and not correct (image below).
How I can make shadow smoother ?
This is my code for render shadow
const dirLight = new THREE.DirectionalLight(0xffffff, 0.75);
dirLight.color.setHSL(0.1, 1, 0.95);
dirLight.position.set(-1, 1.75, 1);
dirLight.position.multiplyScalar(30);
this.scene.add(dirLight);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
const d = 80;
dirLight.shadow.camera.left = -d;
dirLight.shadow.camera.right = d;
dirLight.shadow.camera.top = d;
dirLight.shadow.camera.bottom = -d;
dirLight.shadow.camera.far = 1000;
dirLight.shadow.bias = -0.0001;
Related
I have a custom mesh geometry (three js) in mapbocx. I am trying to create a light for casting directional shadows but I always end up woth the light source in the base plane (which results in no casted shadows on my objects above the plane). Does anyone know how I can move the light source so it is above the plane? I added a helper to see the scope box and I would like to move it upwards along the z-vector in the image below.
//Create a WebGLRenderer and turn on shadows in the renderer
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
//Add Ambient light
const amblight = new THREE.AmbientLight(0xffffff, 0.8);
amblight.position.set(8, 10, 5); //default; light shining from top
scene.add(amblight);
//Create a DirectionalLight and turn on shadows for the light
const light = new THREE.DirectionalLight(0xffffff, 0.5);
//light.position.set(8, 10, 5); //default; light shining from top
light.position.y = 2000;
light.position.x = 10;
light.position.z = 5;
light.castShadow = true; // default false
scene.add(light);
//scene.add(light.target);
//Set up shadow properties for the light
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.shadow.camera.left = -100;
light.shadow.camera.right = 100;
light.shadow.camera.top = 100;
light.shadow.camera.bottom = -100;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 100; //Scope box depth
//Create a plane that receives shadows (but does not cast them)
const planeGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
const planeMaterial = new THREE.MeshPhongMaterial({
color: 0x808080,
opacity: 0.8,
transparent: true,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
scene.add(plane);
const meshString = result.mesh.meshString;
const mesh = meshToThreejs(rhino, meshString, THREE);
//scene.add(mesh);
//Add shadows
mesh.castShadow = true; //default is false
mesh.receiveShadow = true; //default
scene.add(mesh);
//ENd shadows
//Create a helper for the shadow camera (optional)
const helper = new THREE.CameraHelper(light.shadow.camera);
scene.add(helper);
"move the light source so it is above the plane" - It looks like you already know how to do this, just change the z number.
light.position.z = 20;
// or
light.position.set(0, 0, 20);
// Check note below - If y is up
light.position.y = 20;
// or
light.position.set(0, 20, 0);
Just a note, by default Y is up in Three.js unless you have already handled that in code not shown here. If you need to check this add the axesHelper to your scene. The X axis is red. The Y axis is green. The Z axis is blue. Make sure the camera is moved in the correct direction.
const axesHelper = new THREE.AxesHelper( 100 );
scene.add( axesHelper );
If you are still not getting shadows you could try to add a sphere like in the Three.js docs (https://threejs.org/docs/#api/en/lights/shadows/DirectionalLightShadow)
//Create a sphere that cast shadows (but does not receive them)
const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 );
const sphereMaterial = new THREE.MeshStandardMaterial( { color: 0xff0000 } );
const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.castShadow = true; //default is false
sphere.receiveShadow = false; //default
scene.add( sphere );
If that is casting a shadow correctly then perhaps there is an issue with your mesh, or the height of those buildings is so small that the shadows are really small
I am using Three.VideoTexture(video) to load video on canvas. It's work perfectly fine with landscape videos. But when i tried to load a portrait video, on canvas video gets stretched.
var wrap3D = $("#" + threeJsPreviewHTMLElement);//.find(".wrap3d");
videoTexture = new THREE.VideoTexture(video);
videoTexture.minFilter = THREE.LinearFilter;
videoTexture.magFilter = THREE.LinearFilter;
videoTexture.format = THREE.RGBFormat;
scene = new THREE.Scene();
var WIDTH = 520, HEIGHT = 520;
var domID = "videoContext";
if (!isDesktop) {
HEIGHT = $("#modalTumbler3d").height();
WIDTH = $("#modalTumbler3d").width();
domID = "videoContextDevice";
}
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(WIDTH, HEIGHT);
if (!wrap3D.find('#' + domID).length) {
wrap3D.append(renderer.domElement);
}
else {
wrap3D.find('#' + domID).replaceWith(renderer.domElement);
}
renderer.domElement.id = domID;
//Two CAMERA options - perspective is preferred
camera = new THREE.PerspectiveCamera(50, WIDTH / HEIGHT, .1, 20000);
camera.position.set(0, 0, 150);
scene.add(camera);
//Lights
var hemLight = new THREE.HemisphereLight(0x999999, 0xffffff, 1);
scene.add(hemLight);
var spotLight = new THREE.PointLight(0x555555);
spotLight.position.set(0, 15, -20);
scene.add(spotLight);
var spotLight = new THREE.PointLight(0x555555);
spotLight.position.set(0, -15, 20);
scene.add(spotLight);
//size and position of tumbler from camera
var Y = 0;
var A = 0;
var B = 0;
var scale = 1;
//cylinder for the movie preview
//var movieFrame = new THREE.CylinderGeometry(2, 1.8, 2.5, 50, 1, true, -1.59, Math.PI)
var movieFrame = new THREE.CylinderGeometry(1, 1, 2.5, 100, 3, true, -1.59, Math.PI)
//var movieFrame = new THREE.CylinderGeometry(2, 1.9, 2.5, 50, 1, true, -1.59, Math.PI)
var movieMaterial = new THREE.MeshBasicMaterial({ map: videoTexture, overdraw: true, side: THREE.DoubleSide });
var moviePlayer = new THREE.Mesh(movieFrame, movieMaterial);
moviePlayer.position.y = Y;
moviePlayer.rotation.y = A;
moviePlayer.renderOrder = 14;
moviePlayer.scale.set(scale, scale, scale);
scene.add(moviePlayer);
//control with the mouse or two finger pinch
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minPolarAngle = 0; // radians
controls.maxPolarAngle = 2.5; // radians
controls.minDistance = 5;
controls.maxDistance = 10;
controls.enabled = false;
This is what i have tried to play video. I have used CylinderGeometry to load video in Cylinder form.
In this case, the texture will simply cover the whole CylinderGeometry. So any distortion you see is result of the aspect-ratio of the video not matching the aspect-ratio of the cylinder-fragment..
Based on your code you have there a height of 2.5 and a width of Math.PI, so your aspect-ratio is ~1.25. Now for a portrait-video (aspect-ratio <1) you need to adjust the geometry. (so for a 3:4 potrait-video you'd need a thetaLength of 1.875/radius instead of Math.PI).
There's been a-lot of questions around this but none of those have fixed my problem. Any image that I upload onto the object becomes pixelated regardless of the minFilter or magFilter that I use - and I've used all of them:
THREE.NearestFilter
THREE.NearestMipMapNearestFilter
THREE.NearestMipMapLinearFilter
THREE.LinearFilter
THREE.LinearMipMapNearestFilter
THREE.LinearMipMapLinearFilter
Here's the object with a pixelated image:
And here's a snapshot of how I'm loading the image on:
// Build a canvas object and add the image to it
var imageCanvas = this.getCanvas(imageLayer.guid, 'image');
var imageLoader = new THREE.ImageLoader();
imageLoader.load(imageUrl, img => {
// this.drawImage(img, gr, imageCanvas.canvas, imageCanvas.ctx);
var canvas = imageCanvas.canvas;
var ctx = imageCanvas.ctx;
canvas.width = 1024;
canvas.height = 1024;
var imgAspectRatioAdjustedWidth, imgAspectRatioAdjustedHeight;
var pushDownValueOnDy = 0;
var grWidth = canvas.width / 1.618;
if(img.width > img.height) {
grWidth = canvas.width - grWidth;
}
var subtractFromDx = (canvas.width - grWidth) / 2;
var grHeight = canvas.height / 1.618;
if(img.height > img.height) {
grHeight = canvas.height - grHeight;
}
var subtractFromDy = (canvas.height - grHeight) / 2;
var dx = (canvas.width / 2);
dx -= subtractFromDx;
var dy = (canvas.height / 2);
dy -= (subtractFromDy + pushDownValueOnDy);
imgAspectRatioAdjustedWidth = (canvas.width - grWidth) + 50;
imgAspectRatioAdjustedHeight = (canvas.height - grHeight) + 50;
ctx.globalAlpha = 0.5;
ctx.fillStyle = 'blue;'
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1.0;
ctx.drawImage(img, dx, dy, imgAspectRatioAdjustedWidth, imgAspectRatioAdjustedHeight);
});
After this the canvas data is added to an array to be painted onto the object - it is at this point that the CanvasTexture gets the mapped canvas:
var canvasTexture = new THREE.CanvasTexture(mainCanvas.canvas);
canvasTexture.magFilter = THREE.LinearFilter;
canvasTexture.minFilter = THREE.LinearMipMapLinearFilter;
// Flip the canvas
if(this.currentSide === 'front' || this.currentSide === 'back'){
canvasTexture.wrapS = THREE.RepeatWrapping;
canvasTexture.repeat.x = -1;
}
canvasTexture.needsUpdate = true;
// { ...overdraw: true... } seems to allow the other sides to be transparent so we can see inside
var material = new THREE.MeshBasicMaterial({map: canvasTexture, side: THREE.FrontSide, transparent: false});
for(var i = 0; i < this.layers[this.currentSide].length; i++) {
mainCanvas.ctx.drawImage( this.layers[this.currentSide][i].canvas, 0, 0, this.canvasWidth, this.canvasHeight);
}
Thanks to #2pha for the help as his suggestions lead me to the correct answer and, it turns out, that the pixelated effect was caused by different dimensions of the canvases.
For example the main canvas itself was 1024x1024 whereas the text & image canvases were only 512x512 pixels meaning that it would have to be stretched to cover the size of the main canvas.
Lathe Geometry won't cast shadows but spheres and cubes do. Why is that ?
Image
My renderer:
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(0xAAAAAA, 1.0);
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
webGLRenderer.shadowMapEnabled = true;
Lathe:
var latheGeometry = new THREE.LatheGeometry(array, Math.ceil(segments), 0, 2 * Math.PI);
//var meshMaterial = new THREE.MeshBasicMaterial({color:0x00ff00, transparent:true, opacity:0.6});
var meshMaterial = new THREE.MeshLambertMaterial({color: 0xFFFFFF});
meshMaterial.side = THREE.DoubleSide;
var wireFrameMat = new THREE.MeshBasicMaterial();
wireFrameMat.wireframe = false;
latheMesh = THREE.SceneUtils.createMultiMaterialObject(latheGeometry,[meshMaterial, wireFrameMat]);
latheMesh.rotation.x = Math.PI * -0.5;
latheMesh.position.z = 0;
latheMesh.castShadow = true;
scene.add(latheMesh);
My lighting:
var pointColor = "#ffffff";
directionalLight = new THREE.DirectionalLight(pointColor);
directionalLight.position.set(-50,50,50);
directionalLight.castShadow = true;
directionalLight.receiveShadow = false;
directionalLight.shadowCameraVisible = true;
directionalLight.intensity = 1;
directionalLight.shadowCameraNear = 50;
directionalLight.shadowCameraFar = 200;
directionalLight.shadowCameraLeft = -50;
directionalLight.shadowCameraRight = 40;
directionalLight.shadowCameraTop = 50;
directionalLight.shadowCameraBottom = -50;
directionalLight.distance = 0;
directionalLight.shadowMapHeight = 1024;
directionalLight.shadowMapWidth = 1024;
//scene.add( new THREE.DirectionalLightHelper(directionalLight, 0.5) );
scene.add(directionalLight);
As you can see, only sphere can cast shadow and not latheGeometry.
THREE.SceneUtils.createMultiMaterialObject() creates a THREE.Group with a child Mesh for each material.
You need to specify the castShadow property not for the group, but for the child mesh you want to cast the shadow.
three.js r.76
I have a few CubeGeometry objects rendered on the CanvasRenderer. I am getting some clipping issues due to depth sorting as shown on the following image.
Now I have seen the responses on here that this is due to limitations in CanvasRenderer and I have tried applying a few things to get a better result but there is still the clipping issues. I have overdraw set to 1 and I have increased segments as much as I can without impacting performance too much.
The image looks correct in the WebGLRenderer but it is not widely supported yet so before I give up on the CanvasRenderer I would like to exhaust all options. Anyone have any other ideas to try?
code for reference:
function SolidWood( length, width, thickness )
{
this.ID = -1;
this.name = "Unknown";
this.dimensions = {};
this.dimensions.length = length;
this.dimensions.width = width;
this.dimensions.thickness = thickness;
this.properties = {};
this.properties.color = 0x00ff00;
this.properties.wireframe = false;
this.properties.shading = THREE.SmoothShading;
this.properties.overdraw = 1.0;
this.geometry = null;
this.material = null;
this.mesh = null;
}
SolidWood.prototype.BuildGeometry = function(){
var segs = (Detector.webgl ? 1 : 8);
this.geometry = new THREE.CubeGeometry(this.dimensions.width, this.dimensions.length, this.dimensions.thickness
, segs, segs, segs);
};
SolidWood.prototype.BuildMaterial = function(){
this.properties.ambient = this.properties.color;
this.material = new THREE.MeshLambertMaterial( this.properties );
};
SolidWood.prototype.BuildMesh = function(){
this.mesh = new THREE.Mesh(this.geometry, this.material)
};
SolidWood.prototype.BuildAll = function(){
this.BuildGeometry();
this.BuildMaterial();
this.BuildMesh();
};
var camera, scene, renderer, controls, light;
var panel = {};
var HEIGHT, WIDTH;
var $container = $('#content-gallery-content');
var $speed = $('#speed');
init();
animate();
function init() {
WIDTH = 400;
HEIGHT = 300;
camera = new THREE.PerspectiveCamera( 60, WIDTH / HEIGHT, .001, 1000 );
camera.position.z = 30;
camera.position.y = 5;
camera.lookAt(new THREE.Vector3(0,0,0));
scene = new THREE.Scene();
light = new THREE.PointLight(0xffffff, 0.75, 0.0);
light.position.set(0, -30, 0);
scene.add(light);
scene.add(new THREE.AmbientLight(0xb0b0b0));
//build our panel
panel.crossmembersCount = 2;
panel.membersPerFoot = 4;
panel.memberWidth = 2.5;
panel.memberWidthHalf = panel.memberWidth/2;
panel.degrees90 = ((90*Math.PI)/180);
panel.gapWidth = (12-(panel.memberWidth * panel.membersPerFoot)) / panel.membersPerFoot;
panel.members = {};
panel.crossmembers = {};
for(i=0; i<panel.crossmembersCount; ++i)
{
panel.crossmembers[i] = new SolidWood(1.25, 11.875, 0.5);
panel.crossmembers[i].properties.color = 0x000000;
panel.crossmembers[i].BuildAll();
panel.crossmembers[i].mesh.rotation.x = panel.degrees90;
panel.crossmembers[i].mesh.position.z = ((i*12)+5.5)-11.5;
panel.crossmembers[i].mesh.position.x = 0.0625;
panel.crossmembers[i].mesh.position.y = 0.25;
scene.add( panel.crossmembers[i].mesh );
}
for(i=0; i< panel.membersPerFoot; ++i)
{
panel.members[i] = new SolidWood(23.0, panel.memberWidth, 0.6875);
panel.members[i].properties.color = 0xccaa00;
panel.members[i].BuildAll();
panel.members[i].mesh.rotation.x = panel.degrees90;
panel.members[i].mesh.position.z = 0;
panel.members[i].mesh.position.x = panel.memberWidthHalf-6.125+((panel.memberWidth+panel.gapWidth)*i);
panel.members[i].mesh.position.y = (0.6875/2)*-1;
scene.add( panel.members[i].mesh );
}
renderer = (Detector.webgl ? new THREE.WebGLRenderer() : new THREE.CanvasRenderer());
renderer.setSize( WIDTH, HEIGHT );
renderer.setClearColor(0xD0D0D0, 1.0);
$container.append( renderer.domElement );
controls = new THREE.OrbitControls(camera, $container.get(0));
}
function animate() {
// note: three.js includes requestAnimationFrame shim
requestAnimationFrame( animate );
light.position = camera.position;
//var length = $('#speed').val() * 100;
renderer.render( scene, camera );
}
The depth buffer (or Z-buffer) is used to resolve the hidden surface problem. If an incoming fragment has a greater depth value than the value in the buffer, it's discarded. If its value is less, it replaces the current value and the fragment colour is written.
The image looks to me like a classic case of Z-fighting. Your near/far plane is so far apart compared to the Z distance between the surfaces that their Z values collide.
The quick answer, change the projection line to something like this...
camera = new THREE.PerspectiveCamera(60, WIDTH / HEIGHT, 0.01, 100);
If you really need such large differences in near/far plane, it gets a bit more complicated.
A quick google turned up these:
Depth Buffer Precision
Maximizing Depth Buffer Range and Precision