Three.js基础使用-案例

javascript 复制代码
<div ref="modelContainer" style="width:100%; height:600px;">

<script>
import * as THREE from 'three'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

export default {
  name: 'FbxModelPreview',
  data() {
    return {
      //fbx url
      modelUrl: '',

      // three实例
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      fbxModel: null,
      animateId: null
    }
  },
  created() {
        //初始化
        this.$nextTick(() => {
          this.initThree()
          this.loadFbxModel()
        })
   }
   
    methods: {
        // 3D模型
        initThree() {
            const container = this.$refs.modelContainer
            if (!container) return

            console.log('容器宽高', container.clientWidth, container.clientHeight)

            // 场景
            this.scene = new THREE.Scene()
            this.scene.background = new THREE.Color(0xffffff)

            // 相机
            this.camera = new THREE.PerspectiveCamera(60, container.clientWidth / container.clientHeight, 0.1, 2000)
            this.camera.position.set(10, 10, 15)

            // 渲染器
            this.renderer = new THREE.WebGLRenderer({ antialias: true })
            this.renderer.setSize(container.clientWidth, container.clientHeight)
            this.renderer.setPixelRatio(window.devicePixelRatio)
            container.appendChild(this.renderer.domElement)

            // 灯光
            const ambientLight = new THREE.AmbientLight(0xffffff, 0.6)
            this.scene.add(ambientLight)
            const dirLight = new THREE.DirectionalLight(0xffffff, 0.8)
            dirLight.position.set(20, 30, 20)
            this.scene.add(dirLight)

            // 轨道控制器 鼠标拖拽旋转缩放平移
            this.controls = new OrbitControls(this.camera, this.renderer.domElement)
            this.controls.enableDamping = true

            // 窗口自适应
            window.addEventListener('resize', this.onWindowResize)

            this.animate()
        },

        loadFbxModel() {
            const loader = new FBXLoader()
            loader.load(
                this.modelUrl,
                (object) => {
                    console.log('fbx加载成功', object)
                    this.fbxModel = object

                    // true:计算scale/旋转/位移后的真实包围盒
                    const box = new THREE.Box3().setFromObject(object, true)
                    const size = box.getSize(new THREE.Vector3())
                    console.log('模型原始size', size)

                    // 基于底座XZ缩放,忽略巨大Y
                    const maxXZ = Math.max(size.x, size.z)
                    const targetXZ = 8
                    const scale = targetXZ / maxXZ
                    object.scale.set(scale, scale, scale)

                    // ✅缩放后重新获取真实边界,必须带true
                    box.setFromObject(object, true)
                    const center = box.getCenter(new THREE.Vector3())

                    // 模型底部对齐y=0
                    object.position.x = -center.x
                    object.position.z = -center.z
                    object.position.y = -box.min.y

                    this.scene.add(object)

                    box.setFromObject(object, true)
                    box.getCenter(center)
                    this.controls.target.copy(center)

                    // ==========核心:相机自动适配模型,模型刚好完整显示在画布(图2效果)==========
                    const sphere = new THREE.Sphere()
                    box.getBoundingSphere(sphere)
                    const fov = this.camera.fov * (Math.PI / 180)
                    const cameraDistance = sphere.radius / Math.sin(fov / 2) * 1.3 //1.3留一点边距

                    // 相机斜45°观察高塔,初始视角就是正面斜视角,出来直接看到整根塔
                    this.camera.position.set(
                        center.x + cameraDistance * 0.7,
                        center.y + cameraDistance * 0.45,
                        center.z + cameraDistance * 0.7
                    )
                    this.camera.lookAt(center)

                    this.controls.minDistance = sphere.radius * 0.4
                    this.controls.maxDistance = cameraDistance * 3
                    this.controls.update()
                },
                (progress) => {
                    const percent = (progress.loaded / progress.total * 100).toFixed(1)
                    console.log('加载进度:' + percent + '%')
                },
                (err) => {
                    console.error('FBX加载失败', err)
                    this.$message.error('3D模型加载失败')
                }
            )
        },
        animate() {
            this.animateId = requestAnimationFrame(this.animate)
            if (!this.renderer || !this.scene || !this.camera || !this.controls) return
            this.controls.update()
            this.renderer.render(this.scene, this.camera)
        },

        onWindowResize() {
            const container = this.$refs.modelContainer
            if (!container || !this.camera || !this.renderer) return
            this.camera.aspect = container.clientWidth / container.clientHeight
            this.camera.updateProjectionMatrix()
            this.renderer.setSize(container.clientWidth, container.clientHeight)
        },

        // 关闭弹窗
        handleClose() {
            this.modelUrl= ''

            window.removeEventListener('resize', this.onWindowResize)
            if (this.animateId) cancelAnimationFrame(this.animateId)
            if (this.renderer) {
                this.renderer.dispose()
                this.$refs.modelContainer && (this.$refs.modelContainer.innerHTML = '')
            }
            this.scene = null
            this.camera = null
            this.renderer = null
            this.controls = null
            this.fbxModel = null
        },
    },
}
</script>

效果图(案例):

相关推荐
Suxing91 小时前
C语言基础分享:C语言文件操作:从“写日记”到“拍电影”的存盘指南
c语言·开发语言
右耳朵猫AI1 小时前
Node.js周刊2026W36 | NestJS 12发布、Remix 3 RC、pnpm 12 Rust重写、Node.js 26.8.0
开发语言·rust·node.js
程序员蜡笔熊1 小时前
Vite 8 换芯实测:Rolldown 替掉双引擎,构建快 3.19 倍
前端·javascript·vue
统计学小王子1 小时前
数学建模国赛倒计时 2 天 ——《异常值的处理(R语言)》
开发语言·数学建模·r语言
z落落4 小时前
C# WinForm Socket 转串口设备服务器+Modbus CRC16校验+Socket 客户端
服务器·开发语言·c#
小灰灰搞电子10 小时前
Rust+Slint 实现动态消息提示框源码分享
开发语言·后端·rust
传奇开心果编程12 小时前
【Rust入门知识点学与练】第21课:Trait 进阶 Advanced Traits
开发语言·学习·rust
新时代牛马12 小时前
字符设备驱动完整篇:从 cdev_add、file_operations 到chrdev_open 与排障
开发语言·python
swordbob12 小时前
ReentrantLock 与 AQS 完整学习手册
java·开发语言