ClassOutside / HDR_SkySphere

Demonstration project for creating a skyshphere with an HDR image using ThreeJS
1 stars 0 forks source link

Shiny materials appear black #1

Closed hkunz closed 2 months ago

hkunz commented 2 months ago

This is really amazing thank you so much for sharing. But there is a bug. This setup does not work for a cube with metalic properties. Metal will appear Black. I tried setting Principled BSDF to metallic in Blender and exported to GLB. When I import it back to blender works perfectly. But when I use it in this Three.js project, the cube will appear black.

This is the MdelLoader I am using:

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

export function loadModel(scene, modelPath) {
    const loader = new GLTFLoader();
    return new Promise((resolve, reject) => {
        loader.load(
            modelPath,
            (gltf) => {
                scene.add(gltf.scene);
                resolve(gltf.scene); // Return the loaded model
            },
            undefined,
            (error) => {
                console.error('An error occurred loading the GLTF model:', error);
                reject(error);
            }
        );
    });
}

Then loaded in mainView as const model = await loadModel(scene, modelPath);

ClassOutside commented 2 months ago

Hello and thank you for your compliment!

Metallic textures sometimes require more steps than standard color inputs for a shader. Metallic maps usually require both lighting and environment textures to be visible and function properly. This is because metal is meant to be reflective of its surroundings, and often in 3D software this is done through an image set as an environment texture.

I would suggest trying to load a .HDR or .HDRI file to the scene and set it as the scenes environment map, or the map for the mesh's material.

I have previously gotten HDR and HDRI files on polyhaven: https://polyhaven.com/

This discussion here seems to provide some explanations and examples related to setting up and applying HDR files as environment maps: https://discourse.threejs.org/t/how-to-apply-hdri-env/218/4

I hope this information is helpful towards your solution!

hkunz commented 2 months ago

Oh ok thank you so much for your response. I got a minimal example to work with metals. Let me know what you think:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test</title>
</head>
<body>
    <canvas></canvas>
    <script type="module">
        import * as THREE from 'three';
        import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
        import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

        function setupRenderer() {
            const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('canvas'), antialias: true });
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.toneMapping = THREE.ACESFilmicToneMapping;
            renderer.outputEncoding = THREE.sRGBEncoding;
            return renderer;
        }

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

        function setSkySphere(scene, imagePath) {
            new RGBELoader().load(imagePath, (hdrTexture) => {
                hdrTexture.mapping = THREE.EquirectangularReflectionMapping;
                scene.environment = hdrTexture;
                scene.background = hdrTexture;
            });
        }

        function loadModel(scene, modelPath) {
            const loader = new GLTFLoader();
            return new Promise((resolve, reject) => {
                loader.load(
                    modelPath,
                    (gltf) => {
                        gltf.scene.traverse((child) => {
                            if (child.isMesh) {
                                child.material.envMap = scene.environment;
                                child.material.envMapIntensity = 1.0;
                                child.material.needsUpdate = true;
                            }
                        });
                        scene.add(gltf.scene);
                        resolve(gltf.scene);
                    },
                    undefined,
                    (error) => {
                        console.error('An error occurred loading the GLTF model:', error);
                        reject(error);
                    }
                );
            });
        }

        async function setupScene() {
            const scene = new THREE.Scene();
            const renderer = setupRenderer();
            const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            updateCameraAspect(camera, renderer);
            camera.position.set(3, -1, 3);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
            setSkySphere(scene, '/dist/src/textures/kloofendal_48d_partly_cloudy_puresky_1k.hdr');
            const model = await loadModel(scene, '/dist/src/models/metal-cube.glb');
            function animate() {
                requestAnimationFrame(animate);
                if (model) {model.rotation.y += 0.01;}
                renderer.render(scene, camera);
            }
            animate();
        }
        setupScene();
    </script>
</body>
</html>