Search code examples
javascriptthree.jsgraphics3d

Threejs: Pointlight not lighting up my geometries


I'm trying to create a scene from a set of triangles using Threejs. To get the shape of all the triangles i used a BufferGeometry which seems to create the shape correctly. However, it does not respond to lighting. I have tried with several different materials including standard, phong and lambert. With on luck. I understood that one might need to compute normals to the mesh, so i tried adding computeVertexNormals to the code as well but no luck. Ive also tried using flatshading, but that did not seem to have any effect either.

I then figured it might be the geometry and not the material that was throwing me of, so I tried adding a spinning torus to my scence using phong material, but it does not get iluminated either.

The code I have so far is this:

import * as THREE from 'three';
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';

const canvas = document.querySelector('#c')
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000000);
const renderer = new THREE.WebGLRenderer({antialias: true,castShadow:true, canvas});
const controls = new OrbitControls(camera, canvas)

renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement)

const topoGeometry = new THREE.BufferGeometry();
// Fetching triangles from static file
await fetch('http://{localserver}/topography.json')
    .then((response) => response.json())
        .then((json) => {
            const vertices = new Float32Array(json.geometry.vertices.map((coord) => {
                return [coord[0], coord[1], coord[2]]
            }).flat())
            const indices = json.geometry.triangles.flat()
            topoGeometry.setIndex( indices )
            topoGeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
            topoGeometry.computeVertexNormals()
        });
const  topoMaterial = new THREE.MeshStandardMaterial({
    wireframe: false,
    color: 0x00ff00,
});
const topo = new THREE.Mesh( topoGeometry, topoMaterial )
scene.add(topo)

camera.position.z = 2000;
camera.position.x = 0;
camera.position.y = 0;

const torusGeometry = new THREE.TorusGeometry(50,50)
const torusMaterial = new THREE.MeshPhongMaterial()
const torus = new THREE.Mesh(boxGeometry, boxMaterial)
torus.position.setZ(400)
scene.add(torus)


//Adding pointlight to scene
const light = new THREE.PointLight( 0xffffff, 1, 1000 );
light.position.set( 0, 0, 600 );
light.castShadow = true;
scene.add( light );

const lighthelper = new THREE.PointLightHelper(light,30, 0xffffff)
const gridHelper = new THREE.GridHelper(3000,50)
gridHelper.rotateX(Math.PI / 2)
scene.add(lighthelper, gridHelper)

function animate(){
    requestAnimationFrame(animate)
    camera.updateProjectionMatrix()
    controls.update(0.01)
    box.rotateX(0.01)
    box.rotateY(0.01)
    renderer.render(scene, camera)
}
animate()

Heres a small gif from the resulting scene: Dark scene, pointlighthelper displayed as the white wireframe diamond


Solution

  • Point lights use a physically correct unit (candela) with latest three.js releases so an intensity value of 1 is way too low for your scene. Since it has a large scale, try it with a value around 100000 or even higher.

    Depending on what geometry you are showing, consider to scale down the scene to a physically correct scale. 1 world unit should correspond to 1 meter. If your geometry represents a terrain, consider to use a directional light since it better represents the sun/daylight.