简体   繁体   中英

three.js SpotLight acute beam not working

I am new to three.js but I have past experience from Unity with 3d objects and rendering, I am having trouble with the SpotLight, I am trying to shed light from an imported obj, to make it seem like a working lamp. I can't set the light's angle to an acute one because it just doesn't illuminate when it is beyond a certain value.

this is the relevant code for this:

const objLight = new THREE.SpotLight('yellow', 5);
objLight.angle = Math.PI/2;
objLight.penumbra = 0.1;
objLight.decay = 1;
objLight.distance = 40;
objLight.position.set(-0.48, 0.7, 0);
objLight.target.position.set(-0.9, 0, 0);
objLight.castShadow = true;
scene.add(objLight);
scene.add(objLight.target);

this is how it looks like when the angle is set to

Math.PI/2 : https://prnt.sc/vzddfy . Math.PI/3 : https://prnt.sc/vzdeel . Math.PI/4 : https://prnt.sc/vzdeks .

Now I moved the camera closer and I saw that the light is illuminating on the object but that's it: https://prnt.sc/vzder0 .

I tried playing with values for an hour and couldn't find a solution, I even tried removing the obj and the box and it didn't do anything.

I did find however, that if I move the light away from x=-0.48 to x=-4.48 , I can make it acute, up to Math.PI/6: https://prnt.sc/vzdgls

And if I want it to be more acute I need to move it further away.

My theory is that it has something to do with the distance to it's target or something, I tried to move the target away at one point but it didn't do anything.

Anyone knows what could be the issue?

The rest of the code:

 let renderer, camera, scene;

// const THREE = require('three');

function init() {
    renderer = new THREE.WebGLRenderer({antialias: true});
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    document.body.appendChild(renderer.domElement);

scene = new THREE.Scene();
// scene.background = new THREE.Color('red');

camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.z = 5;

new THREE.OrbitControls(camera, renderer.domElement);

const light = new THREE.AmbientLight('white', 0.2);
scene.add(light);

const BoxGeo = new THREE.BoxGeometry(1, 1, 1);
const BoxMat = new THREE.MeshLambertMaterial({color: 'blue'});
const Box = new THREE.Mesh(BoxGeo, BoxMat);
Box.castShadow = true;
scene.add(Box);

const TopPlaneGeo = new THREE.PlaneGeometry(10, 10, 1);
const TopPlaneMat = new THREE.MeshLambertMaterial({color: 'white', side: THREE.DoubleSide});
const TopPlane = new THREE.Mesh(TopPlaneGeo, TopPlaneMat);
TopPlane.position.y = 5;
TopPlane.rotation.x = Math.PI/2;
TopPlane.receiveShadow = true;

scene.add(TopPlane);

const BotPlaneGeo = new THREE.PlaneGeometry(10, 10, 1);
const BotPlaneMat = new THREE.MeshLambertMaterial({color: 'white', side: THREE.DoubleSide});
const BotPlane = new THREE.Mesh(BotPlaneGeo, BotPlaneMat);
BotPlane.position.y = -1;
BotPlane.rotation.x = Math.PI/2;
BotPlane.receiveShadow = true;

scene.add(BotPlane);



const loader = new THREE.OBJLoader();

loader.load
(
    'objects/Lampe_FerBlanc.obj',
    function(obj) {
        obj.position.set(-0.40, 0.5, 0);
        obj.scale.set(0.01, 0.01, 0.01);
        scene.add(obj);
    }
)
    
const objLight = new THREE.SpotLight('yellow', 5);
objLight.angle = Math.PI/6;
objLight.penumbra = 0.1;
objLight.decay = 1;
objLight.distance = 40;
objLight.position.set(-4.48, 0.7, 0);
objLight.target.position.set(-0.9, 0, 0);
objLight.castShadow = true;
scene.add(objLight);
scene.add(objLight.target);

const helper = new THREE.SpotLightHelper(objLight);
helper.name = 'helper';
scene.add(helper);

window.addEventListener('resize', () => {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth/window.innerHeight;
    camera.updateProjectionMatrix();
})

}

function animate() {
    requestAnimationFrame(animate);

scene.getObjectByName('helper').update();

renderer.render(scene, camera);

}

init();
animate();

I think it's because you're using MeshLambertMaterial , which calculates lighting on each vertex, instead of calculating on each face pixel. When the light hits a vertex (wide angle) it illuminates. But when the light only hits the face and doesn't touch a vertex (on narrower angles), the plane face doesn't show the light. Also, shining a yellow light on a blue box won't show anything due to the nature of light: #ffff00 * #0000ff = #000000 .

Try changing to MeshPhongMaterial , and you'll see your lights and shadows being calculated more accurately. Run the code snippet below to see it in action.

 let renderer, camera, scene; let objLight; let objLightPos = 0; let objLightAngle = 1; // const THREE = require('three'); function init() { renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; document.body.appendChild(renderer.domElement); scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 100); camera.position.z = 5; new THREE.OrbitControls(camera, renderer.domElement); const light = new THREE.AmbientLight('white', 0.2); scene.add(light); const BoxGeo = new THREE.BoxGeometry(1, 1, 1); const BoxMat = new THREE.MeshPhongMaterial({color: 'white'}); const Box = new THREE.Mesh(BoxGeo, BoxMat); Box.castShadow = true; scene.add(Box); const TopPlaneGeo = new THREE.PlaneGeometry(10, 10, 1); const TopPlaneMat = new THREE.MeshPhongMaterial({color: 'white', side: THREE.DoubleSide}); const TopPlane = new THREE.Mesh(TopPlaneGeo, TopPlaneMat); TopPlane.position.y = 5; TopPlane.rotation.x = Math.PI/2; TopPlane.receiveShadow = true; scene.add(TopPlane); const BotPlaneGeo = new THREE.PlaneGeometry(10, 10, 1); const BotPlaneMat = new THREE.MeshPhongMaterial({color: 'white', side: THREE.DoubleSide}); const BotPlane = new THREE.Mesh(BotPlaneGeo, BotPlaneMat); BotPlane.position.y = -1; BotPlane.rotation.x = Math.PI/2; BotPlane.receiveShadow = true; scene.add(BotPlane); objLight = new THREE.SpotLight('yellow', 5); objLight.angle = Math.PI/6; objLight.penumbra = 0.1; objLight.decay = 1; objLight.distance = 40; objLight.position.set(-0.48, 0.7, 0); objLight.target.position.set(-0.9, 0, 0); objLight.castShadow = true; scene.add(objLight); scene.add(objLight.target); const helper = new THREE.SpotLightHelper(objLight); helper.name = 'helper'; scene.add(helper); window.addEventListener('resize', () => { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); }) } function animate() { objLightPos += 0.01; objLightAngle += 0.03; requestAnimationFrame(animate); objLight.position.set(Math.sin(objLightPos), 3, 0); objLight.angle = Math.sin(objLightAngle) * 0.5 + 0.5; scene.getObjectByName('helper').update(); renderer.render(scene, camera); } init(); animate();
 body { margin: 0; } canvas { width: 100vw; height: 100vh; display: block; }
 <script src="https://threejs.org/build/three.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.123/examples/js/controls/OrbitControls.js"></script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM