動態

詳情 返回 返回

threejs-夜景 - 動態 詳情

記錄threejs實現夜景光照、投影、陰影、反射、材質顏色編碼的效果

主要是光源設置了shadow、encoding

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Three.js Scene</title>
    <style>
        body {
            margin: 0;
        }

        canvas {
            width: 100%;
            height: 100%
        }
    </style>
</head>

<body>
    <script type="importmap">
        {
            "imports": {
                "three": "./three172/build/three.module.js",
                "three/addons/": "./three172/examples/jsm/"
            }
        }
    </script>

    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js'

        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
        import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

        import { GUI } from 'https://cdn.skypack.dev/dat.gui';

        // 場景
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x000000);

        // 相機
        const camera = new THREE.PerspectiveCamera(
            50, window.innerWidth / window.innerHeight, 0.1, 100
        );
        camera.position.set(5, 5, 5);
        camera.lookAt(0, 0, 0);

        // 渲染器
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        // renderer.outputEncoding = THREE.sRGBEncoding;
        document.body.appendChild(renderer.domElement);

        // 控制器
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;

        // 點光源
        const pointLight = new THREE.PointLight(0xffffff, 1, 100);
        pointLight.position.set(2, 4, 2);
        pointLight.castShadow = true;
        pointLight.shadow.mapSize.set(1024, 1024);
        pointLight.shadow.bias = -0.0005;
        pointLight.shadow.camera.near = 0.1;
        pointLight.shadow.camera.far = 100;
        scene.add(pointLight);

        // 小球可視化光源
        const lightSphere = new THREE.Mesh(
            new THREE.SphereGeometry(0.05, 16, 16),
            new THREE.MeshBasicMaterial({ color: 0xffff00 })
        );
        lightSphere.position.copy(pointLight.position);
        scene.add(lightSphere);

        // 地板
        const plane = new THREE.Mesh(
            new THREE.PlaneGeometry(500, 500),
            new THREE.MeshStandardMaterial({ color: 0xffffff })
        );
        plane.rotation.x = -Math.PI / 2;
        plane.receiveShadow = true;
        scene.add(plane);

        // 正方體
        const cube = new THREE.Mesh(
            new THREE.BoxGeometry(1, 1, 1),
            new THREE.MeshStandardMaterial({ color: 0x00aaff })
        );
        cube.position.y = 0.5;
        cube.castShadow = true;
        scene.add(cube);

        // 座標軸輔助線
        const axesHelper = new THREE.AxesHelper(3);
        scene.add(axesHelper);

        // GUI 控制面板
        const gui = new GUI();
        const lightFolder = gui.addFolder('點光源');

        const lightParams = {
            x: pointLight.position.x,
            y: pointLight.position.y,
            z: pointLight.position.z,
            intensity: pointLight.intensity,
            castShadow: pointLight.castShadow
        };

        lightFolder.add(lightParams, 'x', -100, 100).onChange(v => {
            pointLight.position.x = v;
            lightSphere.position.x = v;
        });

        lightFolder.add(lightParams, 'y', -10, 10).onChange(v => {
            pointLight.position.y = v;
            lightSphere.position.y = v;
        });

        lightFolder.add(lightParams, 'z', -100, 100).onChange(v => {
            pointLight.position.z = v;
            lightSphere.position.z = v;
        });

        lightFolder.add(lightParams, 'intensity', 0, 100).onChange(v => {
            pointLight.intensity = v;
        });

        lightFolder.add(lightParams, 'castShadow').onChange(v => {
            pointLight.castShadow = v;
        });

        const sceneFolder = gui.addFolder('場景設置');
        const sceneParams = {
            showAxes: true
        };

        sceneFolder.add(sceneParams, 'showAxes').onChange(show => {
            axesHelper.visible = show;
        });

        lightFolder.open();
        sceneFolder.open();


        function addModels(path) {
            const loader = new GLTFLoader();
            const dracoLoader = new DRACOLoader();
            dracoLoader.setDecoderPath('./three172/examples/jsm/libs/draco/');
            loader.setDRACOLoader(dracoLoader);

            loader.load(path, (gltf) => {
                const model = gltf.scene;
                model.position.set(0, 0, 0);

                // 遍歷所有子對象,確保陰影和材質設置正確
                model.traverse((child) => {
                    if (child.isMesh) {
                        // 開啓陰影
                        child.castShadow = true;
                        child.receiveShadow = true;

                        // 修復材質顏色編碼問題
                        const material = child.material;
                        // material.normalMap = null;
                        if (material.map) {
                            material.map.encoding = THREE.sRGBEncoding;
                        }
                        material.needsUpdate = true;
                    }
                });
                
                model.children[0].position.set(0, 0, 0);
                scene.add(model);
                console.log('模型加載成功:', model);
            });
        }
        addModels('./assets/jifang.glb');

        // 渲染循環
        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            renderer.render(scene, camera);
        }

        animate();

        // 自適應窗口
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
    </script>
</body>

</html>

推薦一個開源三維可視化開發平台
github跳轉
國內git跳轉

user avatar leexiaohui1997 頭像 inslog 頭像 zero_dev 頭像 dunizb 頭像 weidewei 頭像 youyoufei 頭像 licin 頭像 huangmingji 頭像 geeklab 頭像 Poetwithapistol 頭像 weishiledanhe 頭像 beckyyyy 頭像
點贊 73 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.