[js& threeJS] 3개로 시작하고 전체 코드가 포함된 파노라마식 집 보기 케이스 구현

전주곡:

먼저 공식 문서와 사례 라이브러리 주소를 첨부하세요.

three.js 문서

three.js 예시

파노라마 절단 도구: HDRI에서 CubeMap으로 

전제 지식:

Three.js는 3D 그래픽을 생성하고 렌더링하기 위한 JavaScript 라이브러리입니다. WebGL 기술을 기반으로 합니다.

three.js에는 three.module.min.js, three.module.js, three.min.js, three.js라는 네 가지 참조 리소스가 있습니다. 차이점은 다음과 같습니다.

  • three.module.min.js 프로덕션 환경을 위해 최소화되고 모듈화된 버전입니다.
  • three.module.js 개발 환경에서 사용하기 위한 모듈식이지만 압축되지 않은 버전입니다.
  • three.min.js 모듈화되지 않은 압축된 이전 버전으로, ES 모듈을 지원하지 않는 이전 프로젝트나 환경에 적합합니다.
  • three.js 이전 버전의 압축되지 않은 간단한 버전입니다.

먼저 3D 자동 회전 형상을 구현합니다.

  • BootCDN에서 three.module.js를 다운로드하세요.

  • 공식 문서에 따라 다음 코드를 작성합니다.
<html>

<head>
    <meta charset="utf-8">
    <title>My first three.js app</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <script type="module">
        // script标签中加入type="module" 浏览器将把该脚本作为 ES6 模块来处理
        // 通过es6的方式导入THREE
        import * as THREE from './three.module.js';

        // 创建场景
        const scene = new THREE.Scene();

        // 创建相机
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        // 相机的位置
        camera.position.z = 5;

        // 渲染器
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 添加立方体
        const geometry = new THREE.BoxGeometry(1, 1, 1);
        // 创建材质
        const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        //  创建物体
        const cube = new THREE.Mesh(geometry, material);
        // 将物体添加到场景中
        scene.add(cube);


        // 动画循环又叫渲染循环
        function animate() {
            requestAnimationFrame(animate);

            // 使立方体动起来
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;

            // 渲染场景和相机
            renderer.render(scene, camera);
        }

        animate();
    </script>
</body>

</html>

효과는 다음과 같습니다.

3D 자동 회전 형상을 마우스 회전 가능으로 변경

OrbitControl이 필요합니다.

  • 먼저 Three 리소스 라이브러리를 다운로드하세요.

  • 그런 다음 three/examples/jsm/controls/OrbitControls 경로에 따라 OrbitControls.js를 가져옵니다.
  • 코딩 수정 시작
<html>

<head>
    <meta charset="utf-8">
    <title>My first three.js app</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <!-- 在 HTML 中使用 JavaScript 模块的新特性,被称为 Import Maps(导入映射) -->
    <!-- 当浏览器加载该 three 文件时,它会根据导入映射的规则来解析 JavaScript 中的模块导入语句 -->
    <script type="importmap">
        {
            "imports": {
                "three": "./three.module.js"
            }
        }
    </script>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from './OrbitControls.js'

        const scene = new THREE.Scene();

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

        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const geometry = new THREE.BoxGeometry(1, 1, 1);

        const colors = [
            { color: 0xff0000 },
            { color: 0x00ff00 },
            { color: 0x0000ff },
            { color: 0xff00ff },
            { color: 0xffff00 },
            { color: 0x00ffff }
        ]
        const material = []
        // 将6个面涂上不同的颜色
        for (let i = 0, len = colors.length; i < len; i++) {
            material.push(new THREE.MeshBasicMaterial(colors[i]))
        }

        const cube = new THREE.Mesh(geometry, material);
        scene.add(cube);


        // renderer.domElement 表示 Three.js 渲染器(renderer)所输出的 HTML 元素,通常是一个 <canvas> 元素
        const container = renderer.domElement
        // 创建了一个 OrbitControls 对象  OrbitControls 类会基于鼠标和触摸手势来控制相机在场景中的移动、旋转和缩放
        const controls = new OrbitControls(camera, container)
        // 启用了阻尼效果,使得相机在停止操作之后会自动减速停止
        controls.enableDamping = true
        // 设置了相机可以向外拉近或向内推离目标物体的最大距离。超过这个距离,相机将无法再拉近或推离
        controls.maxDistance = 2

        // 渲染
        const render = () => {
            renderer.render(scene, camera)
            requestAnimationFrame(render)
        }
        render()
    </script>
</body>

</html>
  • 효과는 그림과 같습니다.

집의 전경

내가 이해한 바에 따르면, 현재 파노라마 집 보기 효과를 얻기 위해 일반적으로 사용되는 두 가지 방법, 즉 스카이박스  와  파노라마 사진 지도가 있습니다  .

스카이박스

 이 방법이 가장 이해하기 쉬우며, 우리가 있는 장면에서는 상하, 앞과 뒤, 왼쪽과 오른쪽의 6개 변밖에 없습니다. 이 6개 얼굴의 비전을 사진으로 처리하면 다음과 같이 서로 다른 방향의 6개의 비전 사진이 생성됩니다.

  • 먼저 큐브의 6개 면에 시각적 그림 6개를 붙여 공간을 만듭니다.

  • 그런 다음 비전을 큐브 중앙으로 이동하고 지도를 안쪽으로 뒤집어 집의 전경을 확인하세요. 

최종 렌더링:

전체 코드는 다음과 같습니다.

<html>

<head>
    <meta charset="utf-8">
    <title>My first three.js app</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <script type="importmap">
        {
            "imports": {
                "three": "./three.module.js"
            }
        }
    </script>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from './OrbitControls.js'

        const scene = new THREE.Scene();

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

        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);


        // 添加立方体  参数分别是立方体的宽度、高度和深度
        const geometry = new THREE.BoxGeometry(10, 10, 10)
        // 左右、上下、后前
        const urls = [
            'https://cdn.huodao.hk/upload_img/20220620/3e532822bd445485d27677ca55a79b10.jpg?proportion=1',
            'https://cdn.huodao.hk/upload_img/20220620/cebf6fbcafdf4f5c945e0881418e34ec.jpg?proportion=1',
            'https://cdn.huodao.hk/upload_img/20220620/273081d1896fc66866842543090916d3.jpg?proportion=1',
            'https://cdn.huodao.hk/upload_img/20220620/8747f61fd2215aa748dd2afb6dce3822.jpg?proportion=1',
            'https://cdn.huodao.hk/upload_img/20220620/c34262935511d61b2e9f456b689f5c1c.jpg?proportion=1',
            'https://cdn.huodao.hk/upload_img/20220620/722d2bf88f6087800ddf116511b51e73.jpg?proportion=1'
        ]
        const boxMaterial = []

        urls.forEach((item, index) => {
            // 纹理加载
            const texture = new THREE.TextureLoader().load(item)
            // 通过旋转修复天花板和地板
            if (index == 2 || index == 3) {
                texture.rotation = Math.PI
                texture.center = new THREE.Vector2(0.5, 0.5)
            }
            // 创建材质
            boxMaterial.push(new THREE.MeshBasicMaterial({ map: texture }))
        })
        //  创建一个三维物体
        const house = new THREE.Mesh(geometry, boxMaterial)

        house.geometry.scale(1, 1, -1)
        scene.add(house)


        const container = renderer.domElement
        const controls = new OrbitControls(camera, container)
        controls.enableDamping = true
        controls.maxDistance = 2

        // 渲染
        const render = () => {
            renderer.render(scene, camera)
            requestAnimationFrame(render)
        }
        render()
    </script>
</body>

</html>

파노라마 지도

저는 파노라마 매핑이 가장 간단하고 효과적인 방법이라고 생각합니다. 글을 쓰기 전에 파노라마 사진이 필요하며, SLR의 파노라마 모드를 사용하여 다음과 같이 사진을 찍을 수 있습니다.

  • 구를 추가합니다. 그리고 파노라마를 텍스처로 구에 붙여넣으면 다음과 같은 효과가 나타납니다.

  • 마찬가지로 비전을 공 안에 넣고 텍스처를 반전시킵니다. 

최종 효과는 위와 같습니다.

전체 코드:

<html>

<head>
    <meta charset="utf-8">
    <title>My first three.js app</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <script type="importmap">
        {
            "imports": {
                "three": "./three.module.js"
            }
        }
    </script>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from './OrbitControls.js'

        const scene = new THREE.Scene();

        const defaultMap = {
            x: 20,
            y: 20,
            z: 20,
        }
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const { x, y, z } = defaultMap
        camera.position.set(x, y, z)

        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);


        // 添加立方体  参数分别是立方体的宽度、高度和深度
        let geometry = new THREE.SphereGeometry(16, 50, 50)
        let texture = new THREE.TextureLoader().load("./assets/quanjing.webp");
        let sphereMaterial = new THREE.MeshBasicMaterial({ map: texture });
        const house = new THREE.Mesh(geometry, sphereMaterial);
        house.geometry.scale(16, 16, -16);
        scene.add(house)


        const container = renderer.domElement
        const controls = new OrbitControls(camera, container)
        controls.enableDamping = true
        controls.maxDistance = 1000

        // 渲染
        const render = () => {
            renderer.render(scene, camera)
            requestAnimationFrame(render)
        }
        render()
    </script>
</body>

</html>


확장된 기록

다음 코드는 장면 배경에 텍스처를 할당하고 여러 개의 개별 그림을 회전하고 조정하는 방법을 지정합니다.

        function sceneBackground() {
            scene = new THREE.Scene();
            var urls = [
                'https://cdn.huodao.hk/upload_img/20220620/3e532822bd445485d27677ca55a79b10.jpg?proportion=1',
                'https://cdn.huodao.hk/upload_img/20220620/cebf6fbcafdf4f5c945e0881418e34ec.jpg?proportion=1',
                'https://cdn.huodao.hk/upload_img/20220620/273081d1896fc66866842543090916d3.jpg?proportion=1',
                'https://cdn.huodao.hk/upload_img/20220620/8747f61fd2215aa748dd2afb6dce3822.jpg?proportion=1',
                'https://cdn.huodao.hk/upload_img/20220620/c34262935511d61b2e9f456b689f5c1c.jpg?proportion=1',
                'https://cdn.huodao.hk/upload_img/20220620/722d2bf88f6087800ddf116511b51e73.jpg?proportion=1'
            ];

            var cubeTextureLoader = new THREE.CubeTextureLoader();
            var textureCube = cubeTextureLoader.load(urls, function (texture) {
                // 确定要旋转的面索引,例如右侧面为第 0 个面(索引从 0 开始)
                var faceIndex = [2, 3];
                for (let i = 0; i < faceIndex.length; i++) {
                    // 获取指定面的纹理
                    var faceTexture = texture.image[faceIndex[i]];

                    // 创建一个 canvas 元素用于绘制纹理
                    var canvas = document.createElement("canvas");
                    canvas.width = faceTexture.width;
                    canvas.height = faceTexture.height;
                    var ctx = canvas.getContext("2d");

                    // 在 canvas 上进行旋转操作
                    ctx.translate(canvas.width / 2, canvas.height / 2);
                    ctx.rotate(Math.PI); // 旋转 90 度
                    ctx.drawImage(faceTexture, -canvas.width / 2, -canvas.height / 2);

                    // 将修改后的 canvas 赋值给纹理对象的指定面
                    texture.image[faceIndex[i]] = canvas;
                    texture.needsUpdate = true;
                }
                // 将纹理赋给场景背景
                scene.background = texture;
            });
            // scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000); //雾
        }

new THREE.TextureLoader().load()와 new THREE.CubeTextureLoader().load()의 기능 및 차이점

THREE.TextureLoader().load()지도, 사진, 기타 평면 이미지와 같은 일반적인 2D 텍스처 이미지를 로드하는 데 사용되는 방법입니다.

THREE.CubeTextureLoader().load()환경 맵이라고도 하는 큐브 맵을 로드하는 데 메서드가 사용됩니다. 큐브맵은 6개의 텍스처 이미지로 구성된 맵으로, 각 이미지는 3차원 공간의 면을 나타냅니다.

추천

출처blog.csdn.net/weixin_52479803/article/details/132163434