I've made this table mesh which i would like to cast shadows on the pane(floor) mesh. I've been trying playing around with the ShadowMap for quite some while now but i cant seem to get it to work.
This is the current look:
camera & scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(35, (window.innerWidth / window.innerHeight), 0.1, 1000);
camera.position.z = 8;
camera.position.y = 2;
camera.lookAt( scene.position );
The table mesh
const boxWidth = 1; const boxHeight = 0.1; const boxDepth = 1;
const tableBoardGeometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const textureLoader = new THREE.TextureLoader();
const customPicture = textureLoader.load('https://threejsfundamentals.org/threejs/lessons/resources/images/compressed-but-large-wood-texture.jpg')
const tableBoardMaterial = new THREE.MeshLambertMaterial({map: customPicture, wireframe: false})
const tableBoard = new THREE.Mesh(tableBoardGeometry, tableBoardMaterial)
tableBoard.position.set(0, 0, 0)
tableBoard.castShadow = true;
The lightning & shadow
const lightAndShadow = () => {
const ambientLight = new THREE.AmbientLight(0xffffff, 0.75);
const lightIntensity = 1.3; const lightDistance = 18;
const light = new THREE.PointLight(0xffffff, lightIntensity, lightDistance);
light.position.set(1, 1, 1)
light.castShadow = true
light.target = tableBoard;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
light.shadow = new THREE.LightShadow(new THREE.PerspectiveCamera(100, 1, 500, 1000))
light.shadow.bias = 0.0001
light.shadow.mapSize.width = 2048*2
light.shadow.mapSize.height = 2048*2
scene.add(light, ambientLight);
}
The floor mesh
const floorWidh = 20; const floorHeight = 20; const floorWidthSegments = 50; const floorHeightSegments = 50;
const floorGeometry = new THREE.PlaneGeometry( floorWidh, floorHeight, floorWidthSegments, floorHeightSegments );
floorGeometry.rotateX( - Math.PI / 2 );
const floorTexture = new THREE.TextureLoader().load('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQOOcWSD0K2mPwokAFfZIhq5Xl49bh8B17RlU6NqCGa4UOKydgX');
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set(20, 20);
const floorMaterial = new THREE.MeshBasicMaterial({color: 0xFFFFFF, wireframe: false}),
floor = new THREE.Mesh( floorGeometry, floorMaterial );
floor.position.set(0, -tableLegHeightPosition, 0)
floor.receiveShadow = true;
Any suggestions on how i can get the shadows to work would be much appreciated.
light.shadow = new THREE.LightShadow(new THREE.PerspectiveCamera(100, 1, 500, 1000))
You are overriding the default camera parameters of LightShadow and defining the Near Clipping plane to 500 which is making the tableBoard shadows to disappear.
You can reduce this number to the default of 0.5 or something near to it. Having the near clipping plane as 500 is a very big number for your case.
From the Docs:
.shadow : LightShadow
A LightShadow used to calculate shadows for this light.
The lightShadow's camera is set to a PerspectiveCamera with fov of 90, aspect of 1, near clipping plane at 0.5 and far clipping plane at 500.
Refer Three.js complete doc for Point light shadows here
Related
import * as THREE from "three";
// // Canvas
const canvas = document.querySelector("canvas.webgl");
// // Scene
const scene = new THREE.Scene();
// Renderer
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
});
renderer.setSize(1200, 800);
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius,
widthSegments,
heightSegments
);
const solarSystem = new THREE.Object3D();
scene.add(solarSystem);
objects.push(solarSystem);
const sunMaterial = new THREE.MeshPhongMaterial({ emissive: "red" });
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(5, 5, 5);
solarSystem.add(sunMesh);
objects.push(sunMesh);
const EarthSystem = new THREE.Object3D();
solarSystem.add(EarthSystem);
objects.push(EarthSystem);
const earthMaterial = new THREE.MeshPhongMaterial({ emissive: "blue" });
const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
earthMesh.position.x = 20;
earthMesh.scale.set(2, 2, 2);
EarthSystem.add(earthMesh);
objects.push(earthMesh);
const MoonMaterial = new THREE.MeshPhongMaterial({ emissive: "gray" });
const moonMesh = new THREE.Mesh(sphereGeometry, MoonMaterial);
EarthSystem.add(moonMesh);
moonMesh.position.x = 25;
objects.push(moonMesh);
/**
* Sizes
*/
const sizes = {
width: 1200,
height: 800,
};
/**
* Camera
*/
const camera = new THREE.PerspectiveCamera(
60,
sizes.width / sizes.height,
0.1,
500
);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
scene.add(camera);
// /**
// * Renderer
// */
function animate() {
requestAnimationFrame(animate);
objects.forEach((obj) => {
obj.rotation.y += 0.007;
});
renderer.render(scene, camera);
}
animate();
I am making solar system. In center I make Sun add in SolarSystem first. And then I make EarthSystem and add in SolarSystem. Earth in EarthSystem and normally orbit around Sun. But I have problem in Moon. I make Moon and add in EarthSystem but not orbit around earth only around Sun with earth. How can I solve this problem?
Hello friends https://github.com/IceCreamYou/THREE.Terrain This is a great Library for procedural I want to add the procedural terrain generation in my code
Code goes here
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight,0.01, 100000);
camera.position.set(50, 10, 50)
camera.position.z = 10
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.setClearColor(0xcccccc, 1.0);
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls( camera,renderer.domElement );
controls.screenSpacePanning = true;
const sceneMeshes = [];
const axesHelper = new THREE.AxesHelper( 1000 );
scene.add( axesHelper );
axesHelper.position.y = 0
const shadowLight = new THREE.DirectionalLight(0xffffff, 0.6);
// Set the direction of the light
shadowLight.position.set(150, 350, 350);
// Allow shadow casting
shadowLight.castShadow = true
// define the visible area of the projected shadow
shadowLight.shadow.camera.left = -400;
shadowLight.shadow.camera.right = 400;
shadowLight.shadow.camera.top = 400;
shadowLight.shadow.camera.bottom = -400;
shadowLight.shadow.camera.near = 1;
shadowLight.shadow.camera.far = 10000;
// define the resolution of the shadow; the higher the better,
// but also the more expensive and less performant
shadowLight.shadow.mapSize.width = 2048;
shadowLight.shadow.mapSize.height = 2048;
// to activate the lights, just add them to the scene
scene.add(shadowLight);
const animate = function (){
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
Normal Scene
please help me for my project
Here is a live example based on your code using the default approach of THREE.Terrain for generating a terrain mesh:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 100000);
camera.position.set(0, 250, 1000)
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.setClearColor(0xcccccc, 1.0);
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.screenSpacePanning = true;
const sceneMeshes = [];
const axesHelper = new THREE.AxesHelper(1000);
scene.add(axesHelper);
axesHelper.position.y = 0
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
scene.add(hemiLight);
const shadowLight = new THREE.DirectionalLight(0xffffff, 0.8);
// Set the direction of the light
shadowLight.position.set(0, 1000, 0);
// Allow shadow casting
shadowLight.castShadow = true
// define the visible area of the projected shadow
shadowLight.shadow.camera.left = -400;
shadowLight.shadow.camera.right = 400;
shadowLight.shadow.camera.top = 400;
shadowLight.shadow.camera.bottom = -400;
shadowLight.shadow.camera.near = 1;
shadowLight.shadow.camera.far = 10000;
// define the resolution of the shadow; the higher the better,
// but also the more expensive and less performant
shadowLight.shadow.mapSize.width = 2048;
shadowLight.shadow.mapSize.height = 2048;
// to activate the lights, just add them to the scene
scene.add(shadowLight);
//
// Generate a terrain
var xS = 63,
yS = 63;
terrainScene = THREE.Terrain({
easing: THREE.Terrain.Linear,
frequency: 2.5,
heightmap: THREE.Terrain.DiamondSquare,
material: new THREE.MeshLambertMaterial({
color: 0x654321
}),
maxHeight: 100,
minHeight: -100,
steps: 1,
xSegments: xS,
xSize: 1024,
ySegments: yS,
ySize: 1024,
});
// Assuming you already have your global scene, add the terrain to it
scene.add(terrainScene);
const animate = function() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.131.3/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.131.3/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three.terrain.js#2.0.0/build/THREE.Terrain.min.js"></script>
Im trying to understand an issue i am facing with ThreeJS. I have a simple cube on a page and i have a PNG of a white question mark, with the rest of the image transparent. When i add the texture to the cube;
cubeGeometry = new THREE.BoxGeometry( 5, 5, 5 );
cubeMaterial = new THREE.MeshStandardMaterial( { color: this.cubeColor, flatShading: true, map:new THREE.TextureLoader().load( require("./question-mark.png") ) } );
cubeMesh = new THREE.Mesh( cubeGeometry, cubeMaterial );
The transparent pixels in the PNG appear black no matter what i do. Whats even stranger is the white pixels of the PNG appear the color of this.cubeColor - which is set to orange.
I would expect the cube color to be orange with the transparent pixels of the PNG taking that color and the white pixels in the PNG to remain white.
Anyone shed some light ?
what I see
the texture
You can apply 2 or more materials like this
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur.com/iFom4eT.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
Example, note it uses BoxBufferGeometry not BoxGeometry
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
scene.background = new THREE.Color('purple');
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur.com/hrzoDMj.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);
function render(time) {
time *= 0.001; // convert time to seconds
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.min.js"></script>
<canvas id="c"></canvas>
note that if you put a color and a texture (map) in a single material the 2 colors, (1) the color you specified and (2) the color from the texture, are multiplied with each other. So, wherever the texture has zeros, RGBA = 0,0,0,0 then the result is 0,0,0,0 as anything multiplied by zero is zero. This is why you got the results you got originally where outside the question mark is transparent and inside is orange.
Knowing this you can use your white question mark and change its color by setting the color on the material that is using the texture.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
scene.background = new THREE.Color('purple');
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur.com/hrzoDMj.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);
function render(time) {
time *= 0.001; // convert time to seconds
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
material1.color.setHSL(time, 1, 0.5);
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.min.js"></script>
<canvas id="c"></canvas>
QUESTION:
So I came across this:
https://skin-tracker.com/pubg/outfit?no=000000000000000000000000000&set=1&char=1
I have the central ground area which is textured already in my code. But I have no idea how to make it extend up to the horizon like seen in the link.
It seems the person who coded what you see in the link hit some kind of limitation and had to use a single color for what extends beyond the central floor area.
How can I make my floor extend to the horizon/ create a skyline ?
CODE:
var floorTexture = new THREE.ImageUtils.loadTexture( '../../public/assets/grassTile.jpg' );
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set( 10, 10 );
var floorMaterial = new THREE.MeshBasicMaterial({ map: floorTexture, side: THREE.DoubleSide });
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
var mesh = new THREE.Mesh(floorGeometry, floorMaterial);
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
scene.add( mesh );
You can simply create an enormous textured plane. I found no limitations with the example below. If you implement something like this and encounter errors/problems, update your question with the errors you're seeing.
// prepare the renderer
let WIDTH
let HEIGHT
let aspectRatio = function() {
return WIDTH / HEIGHT
}
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
})
document.body.appendChild(renderer.domElement)
const camera = new THREE.PerspectiveCamera(32, aspectRatio(), 1, 1000)
camera.position.set(0, 10, 50)
function resize() {
WIDTH = window.innerWidth
HEIGHT = window.innerHeight
renderer.setSize(WIDTH, HEIGHT)
camera.aspect = aspectRatio()
camera.updateProjectionMatrix()
}
resize()
window.addEventListener("resize", resize)
const scene = new THREE.Scene()
const light = new THREE.DirectionalLight(0xffffff, 1, Infinity)
light.position.set(0, 0, 1)
camera.add(light)
scene.add(camera)
const sun = new THREE.DirectionalLight(0xffffcc)
sun.position.set(0, 1, 0)
scene.add(sun)
// populate the scene
let geo = new THREE.BoxBufferGeometry(10, 10, 10)
let mat = new THREE.MeshLambertMaterial({
color: "red"
})
let mesh = new THREE.Mesh(geo, mat)
scene.add(mesh)
let tex = new THREE.TextureLoader().load("https://upload.wikimedia.org/wikipedia/commons/4/4c/Grass_Texture.png")
tex.anisotropy = 32
tex.repeat.set(100, 100)
tex.wrapT = THREE.RepeatWrapping
tex.wrapS = THREE.RepeatWrapping
geo = new THREE.PlaneBufferGeometry(10000, 10000)
mat = new THREE.MeshLambertMaterial({
map: tex
})
mesh = new THREE.Mesh(geo, mat)
mesh.position.set(0, -5, 0)
mesh.rotation.set(Math.PI / -2, 0, 0)
scene.add(mesh)
let axis = new THREE.Vector3(0, 1, 0)
function updateCamera() {
camera.position.applyAxisAngle(axis, 0.01)
}
// rendering functions
function render() {
renderer.render(scene, camera)
camera.lookAt(scene.position)
}
let animationLoopId = null
function animationLoop() {
animationLoopId = requestAnimationFrame(animationLoop)
updateCamera()
render()
}
animationLoop()
html,
body {
padding: 0;
margin: 0;
overflow: hidden;
background: skyBLue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.js"></script>
See this JS fiddle for my code. As you can see, there is a gap between the object and the shadow. This works no problem with spot lights. Anyone have any idea how I can fix this?
Key snippets:
//MATERIAL
var material = new THREE.MeshLambertMaterial();
var terrainMaterial = new THREE.MeshStandardMaterial();
//GEOMETRY
var geometry = new THREE.BoxGeometry(100, 100, 100, 10, 10, 10);
var terrainGeometry = new THREE.PlaneGeometry(10000, 10000, 100, 100);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.z = -500;
mesh.position.x = -100;
mesh.position.y = -50;
scene.add(mesh);
var terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
terrain.rotation.x = -90 * (Math.PI / 180);
terrain.position.y = -100;
scene.add(terrain);
// pointlight
var light = new THREE.PointLight(0xffffff, 2.0, 1200);
scene.add(light);
var pointLightHelper = new THREE.PointLightHelper(light);
scene.add(pointLightHelper);
light.position.y = 100;
light.target = mesh;
light.castShadow = true;
light.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 100, 1, 500, 1000 ) );
light.shadow.bias = 0.0001;
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
scene.add(light);
mesh.castShadow = true;
terrain.receiveShadow = true;
The "shadow camera" is actually 6 perspective cameras, each with a 90-degree field-of-view (fov). So if you want to modify the shadow camera, you must not change the fov.
light.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 500, 1000 ) );
three.js r.92
Try using a smaller bias value.. try moving the light closer or away and see how that changes things.. try cranking shadow res to 1024 or 2048...