three.js实现3D汽车展厅效果展示

项目搭建

本案例还是借助框架书写three项目,借用vite构建工具搭建vue项目,搭建完成之后,用编辑器打开该项目,在终端执行 npm i 安装一下依赖,安装完成之后终端在安装 npm i three 即可。

因为我搭建的是vue3项目,为了便于代码的可读性,所以我将three.js代码单独抽离放在一个组件当中,在App根组件中进入引入该组件。具体如下:

<template><!-- 3D汽车展厅 --><CarShowroom></CarShowroom>
</template><script setup>
import CarShowroom from './components/CarShowroom.vue';
</script><style lang="less">*{margin: 0;padding: 0;}
</style>

初始化three.js代码

three.js开启必须用到的基础代码如下:

导入three库

import * as THREE from 'three'

初始化场景

const scene = new THREE.Scene()

初始化相机

// 创建相机
const camera = new THREE.PerspectiveCamera(40,window.innerWidth / window.innerHeight,0.1,1000)
camera.position.set(4.25,1.4,-4.5)

初始化渲染器

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)

监听屏幕大小的改变,修改渲染器的宽高和相机的比例

window.addEventListener("resize",()=>{ renderer.setSize(window.innerWidth,window.innerHeight)camera.aspect = window.innerWidth/window.innerHeightcamera.updateProjectionMatrix()
})

导入轨道控制器

// 添加轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 添加控制器
const controls = new OrbitControls(camera,renderer.domElement)
controls.enableDamping = true // 设置控制阻尼

设置渲染函数

// 设置渲染函数
const render = (time) =>{ controls.update()renderer.render(scene,camera)requestAnimationFrame(render)
}
render()

ok,写完基础代码之后,接下来开始具体的Demo实操。

加载汽车模型

通过使用模型加载器GLTFLoader,然后使用DRACOLoader加载Draco压缩过的模型可以显著减小模型文件体积,从而加快加载速度和提高用户体验。代码如下:

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";// 加载汽车模型
const loader = new GLTFLoader()
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath("/draco/")
loader.setDRACOLoader(dracoLoader)
loader.load("/public/model/Lamborghini.glb",(gltf)=>{scene.add(gltf.scene)
})
复制代码模型加载完成,画面如下:

因为没有灯光,所以我们需要给一个灯光让模型展现出来,这里设置一下环境光源:

// 设置环境光源
const ambientLight = new THREE.AmbientLight('#fff',0.5)
scene.add(ambientLight)

设置展厅效果

这里通过three库中自带的一些模型来实现展厅的效果,如下:

设置地板样式

// 设置地板样式
const floorGeometry = new THREE.PlaneGeometry(20,20)
const floormaterial = new THREE.MeshPhysicalMaterial({side: THREE.DoubleSide,color: 0x808080,metalness: 0, // 设置金属度roughness: 0.1, // 设置粗糙度wireframe: false // 关闭网格线
})
const mesh = new THREE.Mesh(floorGeometry,floormaterial)
mesh.rotation.x = Math.PI / 2
scene.add(mesh)

底部样式设置完,设置一个圆柱体将整个地板进行包裹:

// 设置圆柱体模拟展厅
const cylinder = new THREE.CylinderGeometry(12,12,20,32)
const cylindermaterial = new THREE.MeshPhysicalMaterial({color: 0x6c6c6c,side: THREE.DoubleSide
})
const cylinderMesh = new THREE.Mesh(cylinder,cylindermaterial)
scene.add(cylinderMesh)

接下来在圆柱体中设置一个聚光灯,让聚光灯偏垂直照射汽车模型,如下:

// 设置聚光灯(让汽车更具有立体金属感)
const spotLight = new THREE.SpotLight('#fff',2)
spotLight.angle = Math.PI / 8 // 散射角度,和水平线的夹角
spotLight.penumbra = 0.2 // 横向,聚光锥的半影衰减百分比
spotLight.decay = 2 // 纵向,沿着光照距离的衰减量
spotLight.distance = 30
spotLight.shadow.radius = 10
spotLight.shadow.mapSize.set(4096,4096)
spotLight.position.set(-5,10,1)
spotLight.target.position.set(0,0,0) // 光照射的方向
spotLight.castShadow = true
scene.add(spotLight)

为了不让展厅穿帮,这里将控制器的缩放以及旋转角度进行一个限制,让其只能在展厅中灵活查看而不能跑到展厅外面去:

controls.maxDistance = 10 // 最大缩放距离
controls.minDistance = 1 // 最小缩放距离
controls.minPolarAngle = 0 // 最小旋转角度
controls.maxPolarAngle = 85 / 360 * 2 * Math.PI // 最大旋转角度

设置GUI面板动态控制车身操作

这里我使用three.js库中自带的gui库,来动态的改变车身相关操作,因为我仅仅是控制车身材质和玻璃材质相关的数据操作,这里就线设置一下其相关的材质:

// 车身材质
let bodyMaterial = new THREE.MeshPhysicalMaterial({color: 'red',metalness: 1,roughness: 0.5,clearcoat: 1.0,clearcoatRoughness: 0.03
})
// 玻璃材质
let glassMaterial = new THREE.MeshPhysicalMaterial({color: '#793e3e',metalness: 0.25,roughness: 0,transmission: 1.0 // 透光性
})

在glb模型中,通过traverse函数遍历场景中的所有对象(包括Mesh、Group、Camera、Light等),并对这些对象进行相应操作或处理(这里的门操作后面会讲解到):

loader.load("/public/model/Lamborghini.glb",(gltf)=>{const carModel = gltf.scenecarModel.rotation.y = Math.PIcarModel.traverse((obj)=>{if(obj.name === 'Object_103' || obj.name === 'Object_64' || obj.name === 'Object_77'){// 车身obj.material = bodyMaterial}else if(obj.name === 'Object_90'){// 玻璃obj.material = glassMaterial}else if(obj.name === 'Empty001_16' || obj.name === 'Empty002_20'){// 门// doors.push(obj)}else{return true}})scene.add(gltf.scene)
})

最后得到的结果如下:

接下来通过控制面板来动态的监视汽车模型的车身和玻璃材质:

// 设置gui模板控制
// 修改默认面板名称
gui.domElement.parentNode.querySelector('.title').textContent = '3D汽车动态操作'const bodyChange = gui.addFolder("车身材质设置")
bodyChange.close() // 默认关闭状态
bodyChange.addColor(bodyMaterial,'color').name('车身颜色').onChange(value=>{bodyMaterial.color.set(value)
})
bodyChange.add(bodyMaterial,'metalness',0,1).name('金属度').onChange(value=>{bodyMaterial.metalness = value
})
bodyChange.add(bodyMaterial,'roughness',0,1).name('粗糙度').onChange(value=>{bodyMaterial.roughness = value
})
bodyChange.add(bodyMaterial,'clearcoat',0,1).name('清漆强度').onChange(value=>{bodyMaterial.clearcoat = value
})
bodyChange.add(bodyMaterial,'clearcoatRoughness',0,1).name('清漆层粗糙度').onChange(value=>{bodyMaterial.clearcoatRoughness = value
})
const glassChange = gui.addFolder("玻璃设置")
glassChange.close() // 默认关闭状态
glassChange.addColor(glassMaterial,'color').name('玻璃颜色').onChange(value=>{glassMaterial.color.set(value)
})
glassChange.add(glassMaterial,'metalness',0,1).name('金属度').onChange(value=>{glassMaterial.metalness = value
})
glassChange.add(glassMaterial,'roughness',0,1).name('粗糙度').onChange(value=>{glassMaterial.roughness = value
})
glassChange.add(glassMaterial,'transmission',0,1).name('透光性').onChange(value=>{glassMaterial.transmission = value
})

车门操作与车身视角展示

这里依然用GUI控制面板来动态实现开关车门以及车内车外视角动态切换的操作,如下:

var obj = { carRightOpen,carLeftOpen,carRightClose,carLeftClose,carIn,carOut }
// 设置车身动态操作
const doChange = gui.addFolder("车身动态操作设置")
doChange.close() // 默认关闭状态
doChange.add(obj, "carLeftOpen").name('打开左车门')
doChange.add(obj, "carRightOpen").name('打开右车门')
doChange.add(obj, "carLeftClose").name('关闭左车门')
doChange.add(obj, "carRightClose").name('关闭右车门')
doChange.add(obj, "carIn").name('车内视角')
doChange.add(obj, "carOut").name('车外视角')

每个操作都对应一个函数,如下:

// 打开左车门
const carLeftOpen = () => { setAnimationDoor({ x: 0 }, { x: Math.PI / 3 }, doors[1])
}
// 打开右车门
const carRightOpen = () => { setAnimationDoor({ x: 0 }, { x: Math.PI / 3 }, doors[0])
}
// 关闭左车门
const carLeftClose = () => { setAnimationDoor({ x: Math.PI / 3 }, { x: 0 }, doors[1])
}
// 关闭右车门
const carRightClose = () => { setAnimationDoor({ x: Math.PI / 3 }, { x: 0 }, doors[0])
}// 车内视角
const carIn = () => {setAnimationCamera({ cx: 4.25, cy: 1.4, cz: -4.5, ox: 0, oy: 0.5, oz: 0 }, { cx: -0.27, cy: 0.83, cz: 0.60, ox: 0, oy: 0.5, oz: -3 });
}
// 车外视角
const carOut = () => {setAnimationCamera({ cx: -0.27, cy: 0.83, cz: 0.6, ox: 0, oy: 0.5, oz: -3 }, { cx: 4.25, cy: 1.4, cz: -4.5, ox: 0, oy: 0.5, oz: 0 });
}

这里使用了补间动画tween.js,其github网址为 github.com/tweenjs/twe… ,终端安装其第三方插件之后,直接引入即可,如下(这里不再过多介绍该库的使用,想学习的可以自行寻找其官方文档学习):

接下来借助tween.js库实现补间动画,如下:

// 设置补间动画
const setAnimationDoor = (start, end, mesh) => {const tween = new TWEEN.Tween(start).to(end, 1000).easing(TWEEN.Easing.Quadratic.Out)tween.onUpdate((that) => {mesh.rotation.x = that.x})tween.start()
}
const setAnimationCamera = (start, end) => {const tween = new TWEEN.Tween(start).to(end, 3000).easing(TWEEN.Easing.Quadratic.Out)tween.onUpdate((that) => {//  camera.postition  和 controls.target 一起使用camera.position.set(that.cx, that.cy, that.cz)controls.target.set(that.ox, that.oy, that.oz)})tween.start()
}

最终实现的效果如下:

点击查看车内视角的话,画面如下:

设置手动点击打开关闭车门

通过设置监听点击事件函数来动态实现打开关闭车门:

// 设置点击打开车门的动画效果
window.addEventListener('click', onPointClick);
function onPointClick(event) {let pointer = {}pointer.x = (event.clientX / window.innerWidth) * 2 - 1;pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;var vector = new THREE.Vector2(pointer.x, pointer.y)var raycaster = new THREE.Raycaster()raycaster.setFromCamera(vector, camera)let intersects = raycaster.intersectObjects(scene.children);intersects.forEach((item) => {if (item.object.name === 'Object_64' || item.object.name === 'Object_77') {if (!carStatus || carStatus === 'close') {carLeftOpen()carRightOpen()} else {carLeftClose()carRightClose()}}})
}

然后给每个车门设置汽车状态,如下:

设置图片背景

为了让展厅更具有视觉效果,接下来设置一个画面背景让其更具有画面感,如下:

// 创建聚光灯函数
const createSpotlight = (color) => {const newObj = new THREE.SpotLight(color, 2);newObj.castShadow = true;newObj.angle = Math.PI / 6;;newObj.penumbra = 0.2;newObj.decay = 2;newObj.distance = 50;return newObj;
}// 设置图片背景
const spotLight1 = createSpotlight('#ffffff');
const texture = new THREE.TextureLoader().load('src/assets/imgs/奥特曼.jpg')
spotLight1.position.set(0, 3, 0);
spotLight1.target.position.set(-10, 3, 10)
spotLight1.map = texture
const lightHelper = new THREE.SpotLightHelper(spotLight1);
scene.add(spotLight1);

最终呈现的效果如下:

原文链接:
https://juejin.cn/post/7307146429004333094

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/581407.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python学习笔记(十)】串口被占用导致无法访问的解决办法

串口被占用导致无法访问的解决办法 前言正文1、封装串口打开函数2、解决过程3、实现效果 前言 在项目开发中需要用到串口进行通讯&#xff0c;但当有其他串口调试工具、串口助手等打开占用了某一端口&#xff0c;打开串口时会导致程序卡死&#xff0c;针对这一问题的出现及解决…

CentOS 8 安装指定版本ansible

背景&#xff1a;想要练习ansible使用&#xff0c;用于面试&#xff0c;结果使用centos 8 的yum安装失败&#xff0c;提示版本不兼容&#xff08;指的是python版本&#xff09;&#xff0c;故而使用python来安装指定版本的ansible&#xff0c;特此记录 环境&#xff1a;win11虚…

苹果手机打开Microsoft Outlook日历ics文件方法

作为一名经常需要处理各种日程安排的苹果用户&#xff0c;我深知ics文件的重要性。ics文件通常来自于我们日常使用的日历应用&#xff0c;比如Microsoft Outlook&#xff0c;是日程信息的标准格式。但很多时候&#xff0c;当我们尝试打开这些ics文件时&#xff0c;却会遇到种种…

gulimall-002 分布式基础概念

1、微服务概念 微服务是一种非常流行的架构风格。 拒绝大型单体应用&#xff0c;基于业务边界进行服务微化拆分&#xff0c;各个服务独立部署运行。 每个服务运行在自己的单个进程使用轻量级机制通信可以使用不同的编程语言编写以及不同的数据存储技术 2、集群&分布式&…

【VUE】Flask+vue-element-admin前后端分离项目发布到linux服务器操作指南

目录 一、Flask后端发布环境搭建1.1 python环境第一步&#xff1a;安装python环境第二步&#xff1a;配置python虚拟环境 1.2 uwsgi环境1.3 nginx配置1.4 测试 二、VUE前端发布环境搭建2.1 配置修改2.2 打包上传服务器2.3 nginx配置2.3 测试 三、联合调试 一、Flask后端发布环境…

MyBatis 中 #{}和 ${}的区别是什么?

MyBatis 中 #{}和 ${}的区别是什么&#xff1f; 在 MyBatis 中&#xff0c;#{} 和 ${} 是用于在 SQL 语句中插入参数值的两种方式&#xff0c;它们之间有重要的区别&#xff1a; #{} 的使用&#xff1a; #{} 主要用于预编译的 SQL 语句中&#xff0c;它会将参数值以安全的方式…

python 框架 写一个demo

首先&#xff0c;确保您已经安装了Python和Django。您可以使用以下命令来安装Django&#xff1a; pip install django接下来&#xff0c;创建一个新的Django项目。在命令行中&#xff0c;使用以下命令&#xff1a; django-admin startproject myproject这将创建一个名为mypro…

pyCharm 打印控制台中文乱码解决办法

解决方法 在 "File" -> "Settings" 中的控制台设置&#xff1a; 在 "File" -> "Settings" 中&#xff0c;你可以找到 "Editor" -> "General" -> "Console"。在这里&#xff0c;你可能会找到…

docker学习(十八、network介绍)

[TOC]添加链接描述 首先&#xff0c;我们要知道什么是 Docker 网络。简单来说&#xff0c;它就是 Docker 中用于实现容器间通信的一个东西。 network相关内容&#xff1a; docker学习&#xff08;十八、network介绍&#xff09; docker学习&#xff08;十九、network使用示例br…

【快速全面掌握 WAMPServer】03.玩转安装和升级

网管小贾 / sysadm.cc 大多数情况我们在了解和学习任何一款软件之前都会先去尝试一下软件的安装&#xff0c;毕竟只有安装好了软件&#xff0c;再通过使用它来进一步学习和掌握。 那么同样的道理&#xff0c;我们要学习和掌握如何动手搭建 PHP 的调试环境&#xff0c;那么作为…

启明智显开源项目分享|基于Model 3c芯片的86中控面板ZX3D95CM20S-V11项目软硬件全开源

前言&#xff1a; 本文为4寸 480*480 RGB接口IPS全面触屏的86中控面板&#xff08;RT-ThreadLVGL&#xff09;软硬件开源干货内容&#xff0c;该项目是综合性非常强的RTOS系列项目&#xff01;项目主控芯片使用 Model 3c&#xff0c;整体实现了简化版本的86中控面板的功能需求…

“2023年的技术发展与个人成长:回顾与展望“

文章目录 每日一句正能量前言工作生活未来展望后记 每日一句正能量 凡事顺其自然&#xff0c;遇事处于泰然&#xff0c;得意之时淡然&#xff0c;失意之时坦然&#xff0c;艰辛曲折必然&#xff0c;历尽沧桑悟然。 前言 在这快速发展的信息时代&#xff0c;技术的进步和创新不…

Python网络设备连接和配置工具

当您的Python程序需要运行外部依赖密码的程序&#xff0c;或访问远程服务器时&#xff0c;请使用Paramiko。 Paramiko 是一个实现 SSHv2 协议的 Python 模块。 Paramiko 不是 Python 标准库的一部分&#xff0c;尽管它被广泛使用。 本指南向您展示如何在 Python 脚本中使用 Par…

基于深度学习的召回算法

基于深度学习的召回算法在推荐系统中取得了显著的成功&#xff0c;它利用深度神经网络来学习用户和物品之间的复杂关系&#xff0c;能够更好地捕捉数据中的隐藏模式。以下是一个基于深度学习的召回算法的基本步骤&#xff1a; 数据准备&#xff1a; 收集用户行为数据&#xff…

深入理解C语言中冒泡排序(优化)

目录 引言&#xff1a; 冒泡排序概述&#xff1a; 优化前&#xff1a; 优化后(注意看注释)&#xff1a; 解析优化后&#xff1a; 原理&#xff08;先去了解qsort&#xff09;&#xff1a; 引言&#xff1a; 排序算法是计算机科学中的基础问题之一。在本篇博客中&#xff0c…

[MySQL] MySQL 高级(进阶) SQL 语句

一、高效查询方式 1.1 指定指字段进行查看 事先准备好两张表 select 字段1&#xff0c;字段2 from 表名; 1.2 对字段进行去重查看 SELECT DISTINCT "字段" FROM "表名"; 1.3 where条件查询 SELECT "字段" FROM 表名" WHERE "条件…

计算机组成原理-多处理器系统的基本概念(SISD SIMD MISD MIMD)

文章目录 总览先看这个再往下看 SISDSIMDMISDMIMD向量处理器共享内存多处理器和多核处理器 总览 先看这个 再往下看 SISD 并发就是&#xff1b;先执行一下该指令序列&#xff0c;再执行一下另外一个指令序列 并行就是&#xff1a;两个指令序列同时进行 在某个时间段内只能处理…

centos 编译安装 python 和 openssl

安装环境&#xff1a; centos 7.9 &#xff1a; python 3.10.5 和 openssl 3.0.12 centos 6.10 &#xff1a; python 3.10.5 和 openssl 1.1.1 两个环境都能安装成功&#xff0c;可以正常使用。 安装 openssl 下载地址 下载后解压&#xff0c;进入到解压目录 执行&#xf…

java设计模式学习之【状态模式】

文章目录 引言状态模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用状态示例代码地址 引言 设想你正在使用一个在线视频播放器观看电影。随着你的互动&#xff0c;播放器可能处于不同的状态&#xff1a;播放、暂停、缓冲或结束。每个状态下&#xff0c;播放…

ActiveMQ漏洞合集

目录 介绍CVE-2015-5254&#xff1a;Apache ActiveMQ任意代码执行漏洞漏洞介绍 & 环境准备漏洞发现Nuclei❌Vulmap✅漏洞验证漏洞利用 CVE-2016-3088&#xff1a;Apache ActiveMQ Fileserver远程代码执行漏洞漏洞发现Nuclei✅Vulmap✅MSF✅第三方工具1&#xff08;漏洞探测…