Increasing the shadow map width and height obviously solves the problem, but I would need to increase it to a massive number (which will obviously kill the performance)
I noticed that this issue arises when the body is very far from the spotlight. If I put the planet in a position which is closer to the spotlight, the shadows look much less boxy.
I would highly appreciate any help.
Code:
import * as THREE from "three";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// SUN
import sunMap from "../assets/textures/sun/sunmap.jpg";
// SATURN
import { getFresnelMatSAT } from "./saturnGlowMat.js";
import saturnmap from "../assets/textures/saturn/th_saturn.png";
import saturnbump from "../assets/textures/saturn/th_saturnbump.png";
import saturnclouds from "../assets/textures/saturn/th_saturnclouds.png";
import saturnlights from "../assets/textures/saturn/th_saturnnight.png";
import saturnrings from "../assets/textures/saturn/t00fri_gh_saturnrings.png";
const w = window.innerWidth;
const h = window.innerHeight;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75, // fov
w / h, // aspect ratio
0.1, // near
2000 // far
);
camera.position.y = 10;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap
renderer.setClearColor(0x000000)
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);
renderer.setPixelRatio( window.devicePixelRatio );
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
const controls = new OrbitControls(camera, renderer.domElement);
const detail = 12;
const loader = new THREE.TextureLoader();
// Create the cube geometry
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
// Create the cube material
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // Green color
// Create the cube mesh
const cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial);
// Add the cube to the scene
// scene.add(cubeMesh);
const smallcubeGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const smallcubeMaterial = new THREE.MeshBasicMaterial({color: 0xFF00FF});
const smallcubeMesh = new THREE.Mesh(smallcubeGeometry, smallcubeMaterial);
// scene.add(smallcubeMesh);
cubeMesh.position.set(0, 0, 0)
// MERCURY ORBIT
const semiMajorAxis = 0.387; // scaled value, use a realistic scale for your scene
const eccentricity = 0.2056;
const semiMinorAxis = semiMajorAxis * Math.sqrt(1 - Math.pow(eccentricity, 2));
// Create the orbit path
const orbitCurve = new THREE.EllipseCurve(
0, 0, // Center of the ellipse (the Sun)
semiMajorAxis, // xRadius (semi-major axis)
semiMinorAxis // yRadius (semi-minor axis)
);
// Generate points from the curve
const points = orbitCurve.getPoints(100); // 100 points for a smooth orbit
// Create a geometry from the points
const orbitGeometry = new THREE.BufferGeometry().setFromPoints(points);
const orbitMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
const orbit = new THREE.Line(orbitGeometry, orbitMaterial);
scene.add(orbit);
// Tilt the orbit to match Mercury's inclination
orbit.rotation.x = (90+7.004) * Math.PI / 180;
function createOrbit(semiMajorAxis, semiMinorAxis, inclination, orbitColor){
const orbitCurve = new THREE.EllipseCurve(
0, 0,
semiMajorAxis,
semiMinorAxis
);
const points = orbitCurve.getPoints(100) // CONST
const orbitGeometry = new THREE.BufferGeometry().setFromPoints(points);
const orbitMaterial = new THREE.LineBasicMaterial({
color: orbitColor,
transparent: true,
opacity: 0.3, // CONST
});
const orbit = new THREE.Line(orbitGeometry, orbitMaterial);
scene.add(orbit);
orbit.rotation.x = THREE.MathUtils.degToRad(90) + inclination;
}
// SUN
///////////////////////////// SUN ///////////////////////////
const sunGroup = new THREE.Group();
scene.add(sunGroup);
sunGroup.rotation.y = 7.25 * Math.PI / 180;
sunGroup.rotation.x = -7.25 * Math.PI / 180;
const sunGeometry = new THREE.IcosahedronGeometry(1, detail);
const sunMaterial = new THREE.MeshBasicMaterial({
map: loader.load(sunMap)
})
const sunMesh = new THREE.Mesh(sunGeometry, sunMaterial);
sunGroup.add(sunMesh);
/*
const sunfluidMaterial = new THREE.MeshBasicMaterial({
map: loader.load(sunFluid),
transparent: true,
opacity: 1,
})
const sunfluidMesh = new THREE.Mesh(sunGeometry, sunfluidMaterial);
sunGroup.add(sunfluidMesh);
sunfluidMesh.scale.setScalar(1.015);
*/
const sunlight = new THREE.PointLight( 0xffffff, 2.5, 500, 0, 0, 0);
sunGroup.add(sunlight);
sunlight.castShadow = true;
sunlight.shadow.mapSize.width = 1024; // improve shadow quality
sunlight.shadow.mapSize.height = 1024; // default is 512
sunGroup.position.set(0, 0, 0);
//
////////////////////////// SATURN //////////////////////////
const saturnGroup = new THREE.Group();
saturnGroup.rotation.y = 26.73 * Math.PI / 180;
saturnGroup.rotation.x = -26.73 * Math.PI / 180;
scene.add(saturnGroup);
const saturnGeometry = new THREE.IcosahedronGeometry(1, detail);
const saturnMaterial = new THREE.MeshPhongMaterial({
map: loader.load(saturnmap),
bumpMap: loader.load(saturnbump),
bumpScale: 3,
});
const saturnMesh = new THREE.Mesh(saturnGeometry, saturnMaterial);
saturnGroup.add(saturnMesh);
const saturnNightlightsMat = new THREE.MeshBasicMaterial({
map: loader.load(saturnlights),
blending: THREE.AdditiveBlending,
});
const saturnNightlightsMesh = new THREE.Mesh(saturnGeometry, saturnNightlightsMat);
saturnGroup.add(saturnNightlightsMesh);
const saturnCloudsMat = new THREE.MeshStandardMaterial({
map: loader.load(saturnclouds),
transparent: true,
opacity: 0.4,
blending: THREE.AdditiveBlending,
});
const saturnCloudsMesh = new THREE.Mesh(saturnGeometry, saturnCloudsMat);
saturnCloudsMesh.scale.setScalar(1.01);
saturnGroup.add(saturnCloudsMesh);
const fresnelMatSAT = getFresnelMatSAT();
const saturnGlowMesh = new THREE.Mesh(saturnGeometry, fresnelMatSAT);
saturnGlowMesh.scale.setScalar(1.011);
saturnGroup.add(saturnGlowMesh);
// RING
const saturnRingGeometry = new THREE.RingGeometry(1, 2.5, 64);
const saturnRingMaterial = new THREE.MeshStandardMaterial({
map: loader.load(saturnrings),
color: 0xffffff,
side: THREE.DoubleSide,
transparent: true,
opacity: 1,
});
const saturnRingMesh = new THREE.Mesh(saturnRingGeometry, saturnRingMaterial);
saturnRingMesh.rotation.x = Math.PI / 2;
saturnGroup.add(saturnRingMesh);
// saturnGroup.scale.set(0.084, 0.084, 0.084);
saturnMesh.castShadow = true; //default is false
saturnMesh.receiveShadow = false; //default
saturnRingMesh.receiveShadow = true;
//
let startangle = 1.15;
function updatePlanetPosition(semiMajorAxis, eccentricity, inclination, group, angle){
const DISTANCE_SCALE = 10 // CONST
semiMajorAxis *= 10;
inclination = THREE.MathUtils.degToRad(inclination);
const semiMinorAxis = semiMajorAxis * Math.sqrt(1 - Math.pow(eccentricity, 2));
const x = semiMajorAxis * Math.cos(angle);
const z = semiMinorAxis * Math.sin(angle);
const y = z * -Math.sin(inclination);
const correctedZ = z * Math.cos(inclination);
group.position.set(x, y, correctedZ);
}
updatePlanetPosition(9.573, 0.0520, 2.486, saturnGroup, startangle); // saturn
// startangle += 0.025
controls.target.copy(saturnGroup.position);
controls.update();
camera.position.set(saturnGroup.position.x ,saturnGroup.position.y + 5, saturnGroup.position.z);
camera.lookAt(saturnGroup.position);
sunlight.shadow.radius=5;
sunlight.shadow.blurSamples=10;
function render() {
requestAnimationFrame(render);
//
updatePlanetPosition(9.573, 0.0520, 2.486, saturnGroup, startangle); // saturn
// startangle += 0.025
renderer.render(scene, camera);
}
render();
function handleWindowResize () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}