threejs使用的一些分享

最近有朋友需要渲染模型,就帮忙写了个简单的案例,下面分享下代码


image.png

代码块分享

<template>
    <div class="threejs-container">
        <div class="title">
            <p>{{ findRes.title }}</p>
        </div>
        <div class="subTitle"><img
                src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAASCAYAAADVCrdsAAAAAXNSR0IArs4c6QAABWlJREFUSEudVWtsFFUUPufOY9+z0+5ud9stFMqjYIUKqdpUQcSEGIIRNMUafxgTgxITEwn+gBi7+E9/KJHEB2pEQzQWKwkIJiZGfDQBedSW0m1X+to+tq/tln3v7My9Zna7pTRgwfNr5s45537znfOdgzDPmM9HoEk/aGKIyCJtH8jh4EhDOsPKDRYLM9tsUyazdKq49pVgzjfn6mOIwObnuddn1AMYA4RDPkSfjxYSMMZ4//f79nSev7okNDic5nkCpatWiDWb6qNOq+sze/3L04zpWJHl4mft/wCaA6EHD51+wyuYLVWjoeniRCRqG+wJrhgfGM6KVnPOLx6OaGsf3mDbtGv7D/29g36HgEVWFw27Hn0vNgc+x+a9sZNL7tsC/N63Dz4xPDhaN9w7aklEk1psJk7T8aRCRBGBUgBCgFIKLq8bV95flboxGQaeZ7zDLSWtkjFmNHDdBhnaHXVHdEBz7NyJmTx7DODQIcyBGDqz79mRoam6/q5AIpPK0HRSgWxGQ+D5PICCEQKysxgANGK0WkEQjIwgcJqaInYbD3KxmHI5zJfjidSFtS+eDM+V6UQDga7qW/pGL33o7AFXLJmsxKGf9q+PhKPPd1+8ltI0Dacmoqhfm++62xghYBRFxhtFIECAF3mQ3E5QMwqkEjeIRWTGMq/tRpFb7iotdV84c+TwxO4ToC3MFPr9gIuj5kZFSf2BwbNv7v2no7csOjWlDvRNoN6Ai5leljxBFAjJ+0uyxIo9TkgrWZqOxgVJFnmX26463Y6As9QxFhlPxDmDkVJEPjYddVlsUr3dUfyXe+tbLdjT8nrTcCCI1/0DoJdhNudiOHI9wjSVahoQBOSYRlFRFWq2WUEUBZqMJ5mqqGAvshrMFiPHG8Ws0SQgME4o8ZbwTq/7z28+vHzKd+6cij0t+3x9nQHo8w/kGu+ujABQhTK7w2TwlhcpyLFoJq0lbDazlNUoRxkxKIrKUw252EwcOI5nUpGdys6ijE2WIqVLvb86Nh/sKowH7Dm5v2mg8zoGrgV0du/KVJWykhIbV1O3ZJg3iD9WNX4bAjjKA+yhiKix5gbRn7VUIGIZMC7OCwbRaLJo9pVLe21rXpsuzJaCcrD/53de7W3vKBvvH9YmxmZQb7RbFLEAFuF5UNMKq6n1io9tWfUR1n8yoqtA1xljzeTK56dXG018tcVisVOVIG8VEtk4myDx6dblLx1L5/5+wSzB+LWjDwQ6uhuv/taaSCcznJJWAUiBEp33m/To5bLKVl07rKzMYNhYX/Gd+8nj7eePvyCt8sjVwb6phxQVvIwRzWgy/m2XLDMagSyiiMgLU5VPvdt+O6pzc2Ly4seNHa3na3suXIpqWQ3zvZEfToQnWFBAXg8Alfctg4lgiKyscmQqVpSHkhnN3tcz6kYUWHlFSff62rWnYP2Bmfw0WtxyIC59ukfwrqvaORK4vqG3oxtSiXjuNrPFRGPxpBqfSeZVoxOjUpBLZCjxemA8GEIto4ixWEqRHRK3advGvkqH52t83Key5gbulgH1H6McZ+vJmpsbuK01OzzjbZdrVSVlUtKppEnkwqMjY3X+tqCdJ6hRQvQJypS0AmbZijabjUYjUQpIhW1PP6JYPMLR0s1HJvWaz1+Gi3Ext/0KG3F+AANA/1fP7W39pXtJZDqZ4DkkokkQlq32MEkyq6GhsBmAh3UPrlbtxVKLd/v7bYz5COLNbbwYAP37HIi8ZhnOHc2u9s4vdtWoWeWZ0ZGoUa+Hx+uIlXjl0xnGR0yCuBmIgSlZ7cqyHYf9t/uRewZxhwD0f7mzYk11mZzIijA5MjO2fPexsUIZCzEL3+/m8oLPv9MIlKW079FBAAAAAElFTkSuQmCC"
                alt="">
            <p class="subTitle-val">{{ findRes.subTitle }}</p> <img
                src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAASCAYAAADVCrdsAAAAAXNSR0IArs4c6QAABWNJREFUSEulVl1sFFUUvvfO7Ozs7Ox2t2W7bekf7UJLm7YgFYqCRJ+EFIMmbSJGYjARf1580PhkmD6ZGJ80JgZ/iSFqq8QEE1BKINRoIrQCpU2xFcqy3XbbnXZ3drs7O3PnXnOn3f4AsYgn2czezNxzvvOde75zIfifRimAoEuB4OhRCiGE6sAnO1LqzFPZTNZBsW45JWf/XFg923rkmEkphaCnE4GOHgIhoPnQ8GEw2IEBAF1dClQUhTDnEEI6O3i8OTw8dLD/7AU9ndCoIAqgtrleCjXX/lTz9Ht9bF8++Mr/MO/gQcBQRUGLGdtZsPWtrUaA44i7cvsj09cvXj7cd/J0IJfOYkESIcYYmCaGoaY6I7A+8HdxcWEmEPRGr98I39j18hcpth8qCrEzsmkCEKykKA8qnzVbs/fd3R1ce3l5iRrPthmYDyTn0r65uOZ0SG4NOXjP1Yv9iBACAPshZD/ZWvK6nT6fB/qDflQdqowBJ/051P7hdZuR0ZNv7dz43Ae/5zNbxchRheaBdXd0cDs7QINpgS1qwqibnc4iLQuAIIjYsixq5HKcqevYMDBIJzSA7qKWUkJEwQFcboG6vB5n047NUHTwJ6oOfDQMp851PY9NbCU17sfGTsW4uyzRU69IThesVicTu6cm4pXTMR1Z0KV7A4UgrSZARtMAQQgSjCk2DGjo97hYcMlIwQAIIg/8BW4i+T1C0/ZGdXwg8im8dV7xIT33ajymErdLvCQ6kE6gZVFqCZIkFExNxDYmYnPByWjCMjEy3QUeysoXj0SBYWBo070QAwCev4eBVUkhADAmQJJEECz1k7JQhWtDfdX39pm4c0Yp1Oe1Q+Gx27UJNTHPIYizqQw0DJNLpzLGfCqLXW4J8gLH6RkDmIYOnQ4REsgKDy2eR6z+NqA1DSHA2KoJlVB/WYlQt6Xu6lJ3RE8pEhFw22ws3jo3M+tKzSUdybkUdAg8kgtECxBKXBJnAEpNSqiR03M5p4uX02nTe+emyqpB2Dlc0xbL4iuUwfraclS9uXZmsTsW+p4dQkopF/vl3cpsOhM0TN2ysMEBEzNlGW946cvYYhAEwDEEgJ+MffdDSyyitV+5FOYsSumaOBACrHWLi320pLbKUd/aNLgkVgutaKuJrQHXTrxWI4liC4RUotSSRQFlpyenZsrXF/0Z2PdxjH2XF5yh4wf2jgzO7I5GkjlAFtr+34xVrbQiQDY0Nbpb9mzrWbVhyWn367LgkFqxrgOehwJCwKFOxasgB2sxNvTCgOcvj9v1x43Lw6NPKhfw0FfPvnDt0kQD75SNaDgKEc8v6ITdFYyb/FlhtQDA6xOp5CvgHtu/b16biX+2Jupl0aLw1pk3N02OTrZP3o4Fg6UFudJK/x2LmGBk4HZ5Im0Rr08G4yMRwAvLIFg3IEbxYpuykkuyi2vbv0+obqz/Vq578ep9QSwNpbs4ZRJLh94vGfj1yuELpy+zFnbwogAkWTYLA156c2gcYmIHtY3lHyz1Q4yJAxvYZqWotBiGmuuzxeXVvUXb32AiyeT6wY12d3Cws8ea7Xtn13D/2DO/9V7RikrWQY5DUEskOSuHCS/wcFGumXPhib1bx80sHeBlYSNCTioV+SZqdmwbhIWdyfzc+k8gVsKdPvd2x9jg+KNjIxHL63PCdes8WvhmTJqe1CxMsGVhSBu2lnlCm8r6Nh/65lR+WC2Xd3miPjQIel7hIyn1cadb3JDNzKteWeiLhaf3qDNa66yaEtwyD8srvJEskD9vOfT1/MIEVhbvEMtdaEvDgxdj9Zf3uwKwIdd2UKqu2OT3zMc0OjIaHm890ptceXe4X7x/AJTdu1DramFgAAAAAElFTkSuQmCC"
                alt="">
        </div>
        <div ref="canvasContainer"></div>
        <div v-if="isLoading" class="loading-overlay">
            <div class="loading-bar"
                :style="{ background: `conic-gradient(#4caf50 ${loadingProgress}%, #eee  ${loadingProgress}%)` }">
                <span class="loading-text">{{ loadingProgress.toFixed(0) }}%</span>
            </div>
        </div>
    </div>
</template>

<script>
import * as THREE from 'three';
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
export default {
    name: 'MyView',
    data() {
        return {
            findRes: {},
            isLoading: true,
            loadingProgress: 0
        }
    },
    methods: {
        initSence() {
            const scene = new THREE.Scene();
            const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            this.$refs.canvasContainer.appendChild(renderer.domElement);

            // 添加光源
            const lightPower = this.findRes.lightPower || 1
            const ambientLight = new THREE.AmbientLight(0xffffff, lightPower);
            scene.add(ambientLight);
            const directionalLight = new THREE.DirectionalLight(0xffffff, lightPower);
            directionalLight.position.set(1, 1, 1);
            scene.add(directionalLight);

            const pointLight = new THREE.PointLight(0xffffff, 2, 100);
            pointLight.position.set(5, 5, 5);
            scene.add(pointLight);

            const loadingManager = new THREE.LoadingManager();

            loadingManager.onStart = () => {
                this.isLoading = true;
            };

            loadingManager.onLoad = () => {
                //this.loadingProgress = 100;
                setTimeout(() => {
                   this.isLoading = false;
                }, 500)
            };

            loadingManager.onProgress = (url, itemsLoaded, itemsTotal) => {
                this.loadingProgress = Math.floor((itemsLoaded / itemsTotal) * 10) + 90; // 更新进度
            };


            const loader = this.findRes.modelPath.endsWith('.gltf') || this.findRes.modelPath.endsWith('.glb') ?
                new GLTFLoader(loadingManager) : new FBXLoader(loadingManager); // 根据文件类型选择加载器

            let object; // 声明一个变量来存储加载的模型
            let isMouseControlled = false; // 添加标志来跟踪鼠标控制状态

            loader.load(this.findRes.modelPath, (loadedObject) => {
                object = loadedObject.scene || loadedObject; // 将加载的模型赋值给变量
                scene.add(object);
            }, (xhr) => {
                // 使用 xhr 对象获取更精确的加载进度
                const percentComplete = Math.round((xhr.loaded / xhr.total) * 100);
                this.loadingProgress = percentComplete>90 ? 90 : percentComplete;
                //console.log('xhr',percentComplete);
            }, (error) => {
                console.error('加载模型时出错:', error);
            });
            const cameraRng = this.findRes.cameraRng ? this.findRes.cameraRng : [0, 20, 22]
            //console.log('cameraRng', cameraRng);
            camera.position.set(...cameraRng);
            const controls = new OrbitControls(camera, renderer.domElement);



            // 监听鼠标控制事件
            controls.addEventListener('start', () => {
                isMouseControlled = true; // 开始控制时设置标志
            });

            const animate = function () {
                requestAnimationFrame(animate);
                if (object && !isMouseControlled) {
                    object.rotation.y += 0.005; // 使模型自动旋转
                }
                renderer.render(scene, camera);
            };
            animate();
        }
    },
    mounted() {
        const routeParams = this.$route.query;
        //const findRes = window.config.lists.find(item => item.id == routeParams.id);
        //console.log('obj:', findRes);
        const findRes  = {
            id: 1,
            title: '解放战争时期粟裕大将使用的臂搁',
            subTitle: '新四军江南指挥部纪念馆',
            modelPath: "/model/1/model.fbx",
            cameraRng: [0, 3, 30], //相机配置
            lightPower:2 //光源
        }
        this.findRes = findRes || window.config.lists[0];
        this.initSence();
    }
};
</script>

<style scoped>
.loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9999;
    /* 确保在最上层 */
}

.loading-bar {
    width: 100px;
    /* 圆形的直径 */
    height: 100px;
    /* 圆形的直径 */
    border-radius: 50%;
    /* 使其成为圆形 */
    position: relative;
    /* 使文本能够绝对定位 */
    transition: background 0.5s ease;
    /* 添加过渡效果 */
    background: conic-gradient(gray 0%, transparent 0%);
    /* 默认颜色为灰色 */
}



.loading-text {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* 居中对齐 */
    color: #000;
    /* 文本颜色为黑色 */
    font-size: 16px;
    /* 文本大小 */
    font-weight: bold;
    /* 文本加粗 */
    background: #fff;
    /* 白色背景 */
    border-radius: 50%;
    /* 使其成为圆形 */
    width: 80px;
    /* 圆形的直径 */
    height: 80px;
    /* 圆形的直径 */
    display: flex;
    align-items: center;
    justify-content: center;
    /* 居中对齐文本 */
}


.threejs-container {
    width: 100vw;
    height: 100vh;
    position: relative;
    overflow: hidden;
    background: url('/pc_bg.png') 50% / cover no-repeat;
    z-index: 1;
}

.threejs-container .title {
    position: absolute;
    top: 20px;
    left: 50%;
    z-index: 10;
    -webkit-transform: translateX(-50%);
    transform: translateX(-50%);
    font-size: 26px;
    font-weight: 700;
    color: #ffeac0;
    text-align: center;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex
}

.threejs-container .subTitle {
    position: absolute;
    top: 80px;
    left: 50%;
    z-index: 10;
    -webkit-transform: translateX(-50%);
    transform: translateX(-50%);
    font-size: 22px;
    color: #ffeac0;
    opacity: .5;
    text-align: center;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;

}

.subTitle-val {
    margin: 0px 10px;
}

canvas {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
}
</style>
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容