I was just experimenting with some lightning in three.js and came across a problem which I seem to be the only on having.
The setup is simple, two PointLight, one PlaneGeometry and one BoxGeometry.
"use strict";
var scale = 0.8;
var w = parseInt('' + Math.floor(innerWidth * scale));
var h = parseInt('' + Math.floor(innerHeight * scale));
var camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 1000);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
var scene = new THREE.Scene();
// init
{
scene.background = new THREE.Color(0x404040);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);
}
// plane
{
let geometry = new THREE.PlaneGeometry(40, 40, 10, 10);
let material = new THREE.MeshLambertMaterial({
color: 0x70B009,
side: THREE.DoubleSide
});
var plane = new THREE.Mesh(geometry, material);
plane.lookAt(new THREE.Vector3());
plane.rotateX(90 / 180 * Math.PI);
plane.receiveShadow = true;
scene.add(plane);
}
// box
{
let geometry = new THREE.BoxGeometry(1, 1, 1);
let material = new THREE.MeshLambertMaterial({
color: 0xFF6C00
});
var orangeCube = new THREE.Mesh(geometry, material);
orangeCube.castShadow = true;
scene.add(orangeCube);
}
// pointlights
{
var mapSize = 2 << 10;
var pointLight1 = new THREE.PointLight(0xFFFFFF, 0.6, 100);
pointLight1.castShadow = true;
pointLight1.shadow.mapSize.set(mapSize, mapSize);
scene.add(pointLight1);
var pointLight2 = new THREE.PointLight(0xFFFFFF, 0.6, 100);
pointLight2.castShadow = true;
pointLight2.shadow.mapSize.set(mapSize, mapSize);
scene.add(pointLight2);
}
// position camera, lights and box
{
pointLight1.position.set(0, 15, -15);
pointLight2.position.set(0, 15, 15);
orangeCube.position.set(0, 5, 0);
camera.position.set(10, 10, 0);
camera.lookAt(new THREE.Vector3());
}
// render once
{
renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>
Which works quite well, but one problem. The lights do not eliminate the shadow projected by the other PointLight.
Does someone know how to fix this?
Thank you for your help.
As explained in this SO answer, shadows in MeshLambertMaterial are an approximation. Try MeshPhongMaterial, for example.
In MeshPhongMaterial and MeshStandardMaterial, shadows are the absence of light. If there is light from two light sources, shadow intensity can vary where the shadows overlap. See this three.js example.
three.js r.91
Related
I have been learning threeJS just recently and can't get passed a problem. I tried to animate a extruded triangle using the GSAP library. It is just a simple animation to have the triangle move to the right but it seems I did something wrong. Any help is much appreciated.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create Triangles
var material = new THREE.MeshPhongMaterial({
color: 0xf6c12a,
shininess: 70
});
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(2, 3);
shape.lineTo(4, 0);
shape.lineTo(0, 0);
var extrudeSettings = {
steps: 5,
depth: 1,
bevelEnabled: true,
bevelThickness: 0.3,
bevelSize: 0.5,
bevelOffset: 0,
bevelSegments: 1
};
var geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
// Sets the origin to the center of geometry for rotation
geometry.center();
var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = -5;
mesh.scale.set(1.5, 1.5, 1.5);
scene.add(mesh);
gsap.to(mesh, { duration: 2, x: 300 });
camera.position.z = 5;
// Background
var geometry = new THREE.PlaneGeometry(1000, 1000, 1);
var material = new THREE.MeshPhysicalMaterial({ color: 0x444444 });
var plane = new THREE.Mesh(geometry, material);
plane.position.z = -50;
scene.add(plane);
// Lighting
var ambientLight = new THREE.AmbientLight(0xffffff, 0.55);
scene.add(ambientLight);
var pointLight1 = new THREE.PointLight(0xf9eac8, 1, 100);
pointLight1.position.set(5, 10, 0);
pointLight1.castShadow = true;
pointLight1.shadow.camera.top = 20;
scene.add(pointLight1);
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
Here is the link to Codepen
Tried to put the gsap code into different position but maybe that's not the problem.
gsap.to(mesh.position, { duration: 2, x: 300 });
the value that you want to change is mesh.position.x not mesh.x
just add .position it will work
**I need to use the data from first picture to draw cylinder,put two cylinders point B is not coincide(like second picture) **
var geometry = new THREE.CylinderGeometry(10, 10,151.02648774304458, 20, 1, false);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(1,75.5,1);
scene.add(mesh);
var material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var geometry1 = new THREE.CylinderGeometry(10, 10,158.8741640418605, 20, 1, false);
var mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.position.set(-30,217,32.5);
mesh1.rotation.set(2,151,2);
scene.add(mesh1);
You have to add the red cylinder to a Group. Set the position in that way, that the bottom of the cylinder is at (0, 0, 0). Set the position of the group in that way, that it's origin is at the top of the black cylinder.
Finally you have to rotate the group:
let height = 151.02648774304458;
let height1 = 158.8741640418605;
var geometry = new THREE.CylinderGeometry(10, 10, height, 20, 1, false);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(1, 75.5, 1);
scene.add(mesh);
var material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var geometry1 = new THREE.CylinderGeometry(10, 10, height1, 20, 1, false);
var mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.position.set(0, height1/2, 0);
group = new THREE.Group();
group.position.set(mesh.position.x, mesh.position.y + height/2, mesh.position.z);
group.add(mesh1);
group.rotation.set(...);
scene.add(group);
(function onLoad() {
var container, camera, scene, renderer, orbitControls;
init();
animate();
function init() {
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({
canvas: my_canvas,
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
//container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 200, -400);
camera.lookAt( 0, 0, 0 );
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(camera);
window.onresize = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
orbitControls = new THREE.OrbitControls(camera, container);
createModel();
}
var group;
function createModel() {
var material = new THREE.MeshPhongMaterial({color:'#ff0000'});
var material1 = new THREE.MeshPhongMaterial({color:'#000000'});
let height = 151.02648774304458;
let height1 = 158.8741640418605;
var geometry = new THREE.CylinderGeometry(10, 10, height, 20, 1, false);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(1, 75.5, 1);
scene.add(mesh);
var material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var geometry1 = new THREE.CylinderGeometry(10, 10, height1, 20, 1, false);
var mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.position.set(0, height1/2, 0);
group = new THREE.Group();
group.position.set(mesh.position.x, mesh.position.y + height/2, mesh.position.z);
group.add(mesh1);
//group.rotation.set(2, 151, 2);
scene.add(group);
}
var rotate = 0.0;
function animate() {
group.rotation.set(0, 0, rotate);
rotate += 0.01;
requestAnimationFrame(animate);
orbitControls.update();
render();
}
function render() {
renderer.render(scene, camera);
}
})();
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/controls/OrbitControls.js"></script>
<div id="container"><canvas id="my_canvas"> </canvas></div>
To set a specific rotation by a specific vector, I recommend to set the rotation by a .setRotationFromQuaternion.
The Quaternion defines how to rotate from the upwards direction (0, 1, 0) to the target direction. The Target direction is the vector form the joint to the endpoint of the upper cylinder (-62-1, 283-151, 61-1):
For instance:
let upVector = new THREE.Vector3(0, 1, 0);
let targetVector = new THREE.Vector3(-62 - 1, 283 - height, 61 - 1);
let quaternion = new THREE.Quaternion().setFromUnitVectors(
upVector, targetVector.normalize());
group.setRotationFromQuaternion(quaternion)
I am having trouble figuring out how to set a PlaneGeometry to a good aspect ratio based on the image size.
var material = new THREE.MeshBasicMaterial({
map : THREE.ImageUtils.loadTexture('img/bunny.png')
});
var plane = new THREE.Mesh(new THREE.PlaneGeometry(20, 20*.75), material);
plane.position.set(0, 10, -60)
scene.add(plane);
What I've tried so far sort of works, but I realise I'm still setting a fixed width/height on the Plane.
I'd like the plane to set the size of the image, then I could scale it down.
var planeGeom = new THREE.PlaneGeometry(20, 20);
var imgSrc = "https://upload.wikimedia.org/wikipedia/commons/5/5f/BBB-Bunny.png"
var mesh;
var tex = new THREE.TextureLoader().load(imgSrc, (tex) => {
tex.needsUpdate = true;
mesh.scale.set(1.0, tex.image.height / tex.image.width, 1.0);
});
var material = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: tex
});
mesh = new THREE.Mesh(planeGeom, material);
scene.add(mesh);
//Working snippet is below...
var renderer = new THREE.WebGLRenderer();
var w = 300;
var h = 200;
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
45, // Field of view
w / h, // Aspect ratio
0.1, // Near
10000 // Far
);
camera.position.set(15, 10, 15);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var light = new THREE.PointLight(0xFFFFFF);
light.position.set(20, 20, 20);
scene.add(light);
var light1 = new THREE.AmbientLight(0x808080);
light1.position.set(20, 20, 20);
scene.add(light1);
var planeGeom = new THREE.PlaneGeometry(20, 20);
var imgSrc = "https://upload.wikimedia.org/wikipedia/commons/5/5f/BBB-Bunny.png"
var mesh;
var tex = new THREE.TextureLoader().load(imgSrc, (tex) => {
tex.needsUpdate = true;
mesh.scale.set(1.0, tex.image.height / tex.image.width, 1.0);
});
var material = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: tex
});
mesh = new THREE.Mesh(planeGeom, material);
scene.add(mesh);
renderer.setClearColor(0xdddddd, 1);
(function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
})();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
The accepted answer works but has to use the callback to set the width since the loader is asynchronous. Nowadays one can also use the loadAsync method, which IMO makes the code more readable:
const getImageRatioPlane = async () => {
const texture = await new TextureLoader().loadAsync(imgSrc);
const material = new MeshBasicMaterial({ map: texture });
const geometry = new PlaneGeometry(texture.image.width, texture.image.height);
const plane = new Mesh(geometry, material);
}
I'm having trouble controlling shadows in THREE.js. First off, the shadow in my scene is way too dark. From what I've read, there was a shadowDarkness property, that is know longer available in the current version of three.js. Does anyone know a work around?
Also, in the attached image: the "backface" geometry is not occluding light on the shadow of the seat - however, you can see the backface of the stool in the reflection of the sphere(cubeCamera). Does anyone know how to fix that?
On a side note: chrome gives me an error "Uncaught TypeError: Cannot set property 'visible' of undefined," regarding the
frameMesh.visible = false;
cubeCameraFrame.position.copy(frameMesh.position);
cubeCameraFrame.updateCubeMap(renderer, scene);
frameMesh.visible = true;
part of my code. Could that be effecting the shadows in some way? I can comment that part of the code and it will have little effect on the stoolframes "reflective" appearance. However it then no longer is reflects in the sphere. Any help is much appreciated.
///webGL - Locking down the Basics
/////////////////////////////////////////////////////////////Environment Settings///////////////////////////////////////////////////////////////////////
///Renderer
var scene = new THREE.Scene();
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapType = THREE.PCFSoftShadowMap;
renderer.shadowMapEnabled = true;
document.body.appendChild(renderer.domElement);
///Camera's
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
scene.add(camera);
camera.position.set(0, 16, 25);
camera.rotation.x += -0.32;
var cubeCameraSphere = new THREE.CubeCamera(1, 1000, 256); // parameters: near, far, resolution
cubeCameraSphere.renderTarget.texture.minFilter = THREE.LinearMipMapLinearFilter; // mipmap filter
scene.add(cubeCameraSphere);
var cubeCameraFrame = new THREE.CubeCamera(1, 1000, 256); // parameters: near, far, resolution
cubeCameraFrame.renderTarget.texture.minFilter = THREE.LinearMipMapLinearFilter; // mipmap filter
scene.add(cubeCameraFrame);
///Controls
///Lights
var lightSpot_Right = new THREE.SpotLight(0xffffff);
lightSpot_Right.position.set(50, 50, 0);
lightSpot_Right.intensity = 1.25;
lightSpot_Right.castShadow = true;
lightSpot_Right.shadowDarkness = 0.1;
lightSpot_Right.shadowMapWidth = 2048;
lightSpot_Right.shadowMapHeight = 2048;
lightSpot_Right.shadowCameraNear = 1;
lightSpot_Right.shadowCameraFar = 100;
lightSpot_Right.shadowCameraFov = 65;
scene.add(lightSpot_Right);
var lightDirect_Left = new THREE.DirectionalLight(0xffffff, 0.25);
lightDirect_Left.position.set(-1, 0, 0);
scene.add(lightDirect_Left);
///Loaders
var loadTexture = new THREE.TextureLoader();
var loader = new THREE.JSONLoader();
///skyBox
var imagePrefix = "textures/";
var directions = ["skyboxRight", "skyboxLeft", "skyboxTop", "skyboxBottom", "skyboxFront", "skyboxBack"];
var imageSuffix = ".jpg";
var skyMaterialArray = [];
for (var i = 0; i < 6; i++)
skyMaterialArray.push(new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load(imagePrefix + directions[i] + imageSuffix),
side: THREE.BackSide
}));
var skyMaterial = new THREE.MeshFaceMaterial(skyMaterialArray);
var skyGeometry = new THREE.CubeGeometry(1000, 1000, 1000);
var skyBox = new THREE.Mesh(skyGeometry, skyMaterial);
scene.add(skyBox);
////////////////////////////////////////////////////////Object Settings//////////////////////////////////////////////////////////////////
//Textures
var seatTexture = loadTexture.load("textures/Maharam_Mister_Notice_Diffuse.jpg");
seatTexture.wrapS = THREE.RepeatWrapping;
seatTexture.wrapT = THREE.RepeatWrapping;
seatTexture.repeat.set(3, 3);
var conceteDiffuse = loadTexture.load("textures/Contrete_Diffuse.jpg");
conceteDiffuse.wrapS = THREE.RepeatWrapping;
conceteDiffuse.wrapT = THREE.RepeatWrapping;
conceteDiffuse.repeat.set(3, 3);
var conceteNormal = loadTexture.load("textures/Contrete_Normal.jpg");
conceteNormal.wrapS = THREE.RepeatWrapping;
conceteNormal.wrapT = THREE.RepeatWrapping;
conceteNormal.repeat.set(3, 3);
var conceteSpecular = loadTexture.load("textures/Contrete_Specular.jpg");
conceteSpecular.wrapS = THREE.RepeatWrapping;
conceteSpecular.wrapT = THREE.RepeatWrapping;
conceteSpecular.repeat.set(3, 3);
///Materials
var seatMaterial = new THREE.MeshLambertMaterial({
map: seatTexture,
side: THREE.DoubleSide
});
var frameMaterial = new THREE.MeshPhongMaterial({
envMap: cubeCameraFrame.renderTarget,
color: 0xcccccc,
emissive: 0x404040,
shininess: 10,
reflectivity: .8
});
var frameHardwareMat = new THREE.MeshPhongMaterial({
color: 0x000000
});
var feetMat = new THREE.MeshPhongMaterial({
color: 0x050505,
shininess: 99
});
var sphereMat = new THREE.MeshPhongMaterial({
envMap: cubeCameraSphere.renderTarget
});
var groundMat = new THREE.MeshPhongMaterial({
map: conceteDiffuse,
specularMap: conceteSpecular,
normalMap: conceteNormal,
normalScale: new THREE.Vector2( 0.0, 0.6 ),
shininess: 50
});
///Geometry and Meshes
var barStool = new THREE.Object3D();
scene.add(barStool);
var seatMesh;
loader.load("models/stoolSeat.js", function (geometry, material) {
seatMesh = new THREE.Mesh(geometry, seatMaterial);
seatMesh.scale.set(.5, .5, .5);
seatMesh.castShadow = true;
seatMesh.receiveShadow = true;
barStool.add(seatMesh);
});
var frameMesh;
loader.load("models/stoolFrame.js", function (geometry, material) {
frameMesh = new THREE.Mesh(geometry, frameMaterial);
frameMesh.scale.set(.5, .5, .5);
frameMesh.castShadow = true;
barStool.add(frameMesh);
});
var frameFeetMesh;
loader.load("models/stoolFeet.js", function (geometry, material) {
frameFeetMesh = new THREE.Mesh(geometry, feetMat);
frameFeetMesh.scale.set(.5, .5, .5);
frameFeetMesh.castShadow = true;
barStool.add(frameFeetMesh);
});
var frameHardwareMesh;
loader.load("models/stoolHardware.js", function (geomtry, material) {
frameHardwareMesh = new THREE.Mesh(geomtry, frameHardwareMat);
frameHardwareMesh.scale.set(.5, .5, .5);
barStool.add(frameHardwareMesh);
});
var sphereGeo = new THREE.SphereGeometry(2.5, 50, 50);
var sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
scene.add(sphereMesh);
sphereMesh.position.set(-10, 5, 0);
var groundGeo = new THREE.PlaneGeometry(100, 50, 1);
var groundMesh = new THREE.Mesh(groundGeo, groundMat);
scene.add(groundMesh);
groundMesh.rotation.x = -90 * Math.PI / 180;
groundMesh.receiveShadow = true;
///Render Scene
var render = function () {
requestAnimationFrame(render);
barStool.rotation.y += 0.01;
skyBox.rotation.y -= 0.0002;
sphereMesh.visible = false;
cubeCameraSphere.position.copy(sphereMesh.position);
cubeCameraSphere.updateCubeMap(renderer, scene);
sphereMesh.visible = true;
//frameMesh.visible = false;
//cubeCameraFrame.position.copy(frameMesh.position);
//cubeCameraFrame.updateCubeMap(renderer, scene);
//frameMesh.visible = true;
renderer.render(scene, camera);
};
render();
Shadow darkness has been removed. The best work-around is to add ambient light to your scene.
scene.add( new THREE.AmbientLight( 0xffffff, 0.3 );
You may want to concurrently reduce the intensity of your SpotLight.
The shadow is actually correct given only back faces are casting shadows. It appears that the stool is hollow under the seat -- in other words, the seat is not a closed volume. Add a bottom to the underside of your seat.
Alternatively, you can leave your model as-is and experiment with
renderer.shadowMap.cullFace = THREE.CullFaceNone;
Finally, you are getting the error because you are accessing frameMesh in the animation loop before it is defined in the loader callback. The callback is asynchronous.
if ( frameMesh !== undefined ) {
// your code
}
three.js r.75
I'm trying to animate a Canvas-based texture that is mapped onto a plane, like a billboard. I've made a point of including material.needsUpdate & texture.needsUpdate, but I'm still unable to get the texture to come to life. I've also included a rotating cube just so I know the animation routine is functioning on some level.
Here is the code:
<body>
<script src="http://mrdoob.github.com/three.js/build/three.min.js"></script>
<script>
if (window.innerWidth === 0) {
window.innerWidth = parent.innerWidth;
window.innerHeight = parent.innerHeight;
}
var camera, scene, renderer;
var mesh, geometry, material;
var light, sign, animTex;
var canvas, context;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1200);
camera.position.z = 700;
scene = new THREE.Scene();
material = new THREE.MeshLambertMaterial(
{
color: 0x885522,
wireframe: false,
overdraw: false
});
geometry = new THREE.CubeGeometry(80, 120, 100, 1, 1, 1);
mesh = new THREE.Mesh(geometry, material);
sign = createSign();
light = new THREE.DirectionalLight(0xFFFFFF, 3.0);
light.position = new THREE.Vector3(5, 10, 7);
light.target = new THREE.Vector3(0, 0, 0);
scene.add(mesh);
scene.add(sign);
scene.add(light);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function createSign() {
canvas = document.createElement("canvas");
context = canvas.getContext("2d");
canvas.width = 200;
canvas.height = 200;
context = canvas.getContext("2d");
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial({ map : texture, overdraw: true });
material.needsUpdate = true;
var mesh = new THREE.Mesh(new THREE.PlaneGeometry(200, 200), material);
mesh.doubleSided = true;
return mesh;
}
function animate() {
var time = Date.now()*0.01;
var sinTime = Math.sin(time * 0.05) * 100;
var cosTime = Math.cos(time * 0.05) * 100;
mesh.rotation.y = sinTime*0.01;
requestAnimationFrame(animate);
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "white";
context.fillRect((canvas.width/2) + sinTime, (canvas.height/2) + cosTime, 20, 20)
renderer.render(scene, camera);
}
This runs, but I can't seem to get the Canvas texture material to update. What have I overlooked?
Place this right before your render() call:
sign.material.map.needsUpdate = true;
The needsUpdate flag is reset (to false) every time the texture is used (every render loop), so it needs to be set to true in the render loop (before the render call, or it'll be a frame off). So in your example, put sign.material.map.needsUpdate = true before renderer.render( scene, camera ). texture.needsUpdate = true and material.needsUpdate = true are not needed.
Also, you only need to set the needsUpdate flag on the texture, as the material properties are not changing.