Search code examples
javascriptthree.jsrenderingsmoothingnormals

Three.js: Smoothing normals on non indexed BufferGeometry


I'm trying to smooth the normals of a mesh starting from a non indexed BufferGeometry. This question has been answered before however the Three.js api has changed substantially since and I can't get it to work on r130

From what I've understood, I need to first merge the vertices to get an indexed BufferGeometry then re-compute the normals, but it doesn't seem to work.

Here is a minimal example using the defaulf cube :

    // Scene
    const scene = new THREE.Scene();
    
    // Geometry
    const boxGeometry = new THREE.BoxGeometry(.7,.7,.7);
    
    // Materials
    const shadedMaterial = new THREE.MeshStandardMaterial();
    shadedMaterial.metalness = 0.4;
    shadedMaterial.roughness = 0.4;
    shadedMaterial.color = new THREE.Color(0xffffff);
    
    // Mesh
    const smoothBoxGeometry=BufferGeometryUtils
    .mergeVertices(new THREE.BufferGeometry().copy(boxGeometry))
    smoothBoxGeometry.computeVertexNormals();
    smoothBoxGeometry.computeBoundingBox();
    smoothBoxGeometry.normalizeNormals();
    
    const box = new THREE.Mesh(smoothBoxGeometry, shadedMaterial);
    scene.add(box);

flat shaded cube instead of expected smooth shaded cube

What am I missing ?


Solution

  • Try it like so:

    let camera, scene, renderer;
    
    let mesh;
    
    init();
    animate();
    
    function init() {
    
      camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
      camera.position.z = 4;
    
      scene = new THREE.Scene();
    
      const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
      hemiLight.position.set(0, 20, 0);
      scene.add(hemiLight);
    
      const dirLight = new THREE.DirectionalLight(0xffffff);
      dirLight.position.set(-3, 10, -10);
      scene.add(dirLight);
    
      let geometry = new THREE.BoxGeometry();
      geometry.deleteAttribute('normal');
      geometry.deleteAttribute('uv');
      geometry = THREE.BufferGeometryUtils.mergeVertices(geometry);
      geometry.computeVertexNormals();
      const material = new THREE.MeshStandardMaterial();
    
      mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);
    
      renderer = new THREE.WebGLRenderer({
        antialias: true
      });
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);
    
    }
    
    function animate() {
    
      requestAnimationFrame(animate);
    
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.02;
    
      renderer.render(scene, camera);
    
    }
    body {
          margin: 0;
    }
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/utils/BufferGeometryUtils.js"></script>

    BufferGeometryUtils.mergeVertices() can only perform the merge if vertex data are identical. To ensure this, it is necessary to remove the existing normal and uv attribute.