Three.js杂记(十四)———— 汽车展览·上

在学习了一些理论知识后,要做一下实战演练了,做一个简单的车辆展览来看看吧。

通过调整相机的位置,将导入的车辆模型分成三个视角展示。

  1. 车辆外部:可以观察车辆的整体外观以及轮廓结构
  2. 车辆内部:相机在汽车内部,可以改变相机焦点位置观察车辆内部情况

首先根据需求,先要有一辆汽车。我的素材是从爱给网上找到的,3D模型的类型是gltf格式,并且携带了3种动画效果。


创建场景和导入素材

场景的创建比较简单,具体可以参考上一篇文章 Three.js杂记(十三)------ 包围盒。里面对render渲染器、scene场景、camera相机等元素创建都有写过,并且对GLTFLoader3D模型导入也有过介绍,所以具体代码和流程就不再复述了。

为了界面更好区分,我将scene.background的颜色设置为了#ccc

js 复制代码
scene.background = new THREE.Color("#ccc");

导入模型后:


设置光源和地面

当前模型是黑色的,因为我并没有设置环境贴图或者光线。

接下来设置光源,我使用了点光源PointLight,从5个不同方向对车辆进行照射。因为5个点光源,所以使用Group组的形式放到了一起。

js 复制代码
// 添加灯光组
const lightGroup = new THREE.Group();
let lightDir = [
	[3, 0.5, 0],
	[-3, 0.5, 0],
	[0, 0.5, 3],
	[0, 0.5, -3],
	[0, 3, 0],
]
for(let i = 0; i < lightDir.length; i++) {
	let light1 = new THREE.PointLight(0xffffff, 10);
	light1.position.set(...lightDir[i]);
	lightGroup.add(light1);
}
scene.add(lightGroup);

当前拥有光源后效果:

为了更好的呈现,我在车辆下方添加了一块平面几何体PlaneGeometry,使用的材质是MeshStandardMaterial。对几何体进行绕x轴旋转。

ts 复制代码
// 制作地面
const floorGeometry = new THREE.PlaneGeometry(10, 10);
const floorMaterial = new THREE.MeshStandardMaterial({ color: '#fff' });
let floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.material.opacity = 0.5;
floor.rotateX(-Math.PI / 2);
floor.position.y = 0;
scene.add(floor);

当前车辆展示就看起来很有科技感了


内外切换面板

接下来准备一块面板,面板上添加外部和内部三种选项。

面板使用div元素制作,通过绝对定位放在界面右上角。

然后设置点击方法,对相机位置进行调整

这里我使用了Tween补间动画,然后在animtion中需要添加补间动画的update方法

ts 复制代码
import { update as TweenUpdate } from 'three/examples/jsm/libs/tween.module';
// ...
// 面板上数据
const cGUI = ref({
	cameraPos: [
		{ name: '车辆外部', pos: [-3, 3, 3] },
		{ name: '车辆内部', pos: [0, 0.8, 0] },
	]
})
// 切换相机视角方法
const changeCameraPos = (pos: number[]) => {
	// 创建补间对象
	const tween = new Tween(camera.position);
	tween.to({ 
		x: pos[0],
		y: pos[1],
		z: pos[2]
	}, 1000).start();
}
// ...
function animate() {
	TweenUpdate(); //更新动画
	controls.update(); //鼠标控制
	render.render(scene, camera);
	window.requestAnimationFrame(animate);
}

这样一来,点击切换相机位置改变时,就可以比较流畅的看见过程。 以下是效果:


相机位置和视角

现在场景内已经使用了OrbitControls去用鼠标拖动改变相机位置。并且为了后续效果,我禁用了缩放功能

js 复制代码
const controls = new OrbitControls(camera, render.domElement);  
controls.enableZoom = false;

但是,此功能在车辆内部就不适用了,改变相机位置会导致穿模。在车辆内部需要的是改变相机焦点lookAt ,保持相机的位置不动。

这里我是根据点击方法中相机位置,设置inOut属性判断内外。

ts 复制代码
// 切换相机视角方法
const changeCameraPos = (pos: number[]) => {
	if (Math.abs(pos[0]) < 1){
		inOut = true;  // 在车辆内部
		controls.enableRotate =false; // 禁止旋转
	} else {
		inOut = false;  // 在车辆外部
		controls.enableRotate = true; // 允许旋转
	}
	// ......
}

此时,在animation动画中,也需要进行判断处理:

js 复制代码
function animate() {
	// ...
	if (!inOut) {
		controls.update(); //鼠标控制
	}
    // ...
}

将这些设置完成后,车辆外部能正常观察汽车,但是内部移动不动,此时就需要绑定鼠标按下移动释放等事件了。

通过这些事件中鼠标的位置,去设置camera相机的焦点位置。此处我参考了:three.js笔记5--添加鼠标移动视角 | Here. There. (godbasin.github.io)

原理也很明了:相当于以相机固定不动位置作为顶点,然后用焦点画一个圆环

代码:

ts 复制代码
// 定义角度
var theta = 0;
// 初始化鼠标X方向移动值
var mouseX = 0;
var r = 1000 / (2* Math.PI); // 用于角度计算: 鼠标移动1000px时,角度改变2PI
var far = 100; // 用于照相机焦点设置(焦点距离,越大越精确)
var move = 0.1; // 用于步长(照相机移动距离)
var mousedownFlag = false; // 鼠标是否按下
var inOut = false;
// 添加鼠标移动时事件
document.addEventListener('mousemove', handleMousemove, false);
// 添加鼠标页面点击释放
document.addEventListener('mousedown', initMousePosition, false);
document.addEventListener('mouseup', ()=>{ mousedownFlag = false; }, false);
// 初始化鼠标移动值
function initMousePosition(e:any) {
	if (!inOut) return ;
	mousedownFlag = inOut;
	mouseX = getMousePos(e || window.event).x;
}
// 获取鼠标坐标,传入事件event
function getMousePos(event: any) {
	var e = event || window.event;
	var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
	var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
	var x = e.pageX || e.clientX + scrollX;
	var y = e.pageY || e.clientY + scrollY;
	return { 'x': x, 'y': y };
}

// 处理鼠标移动
function handleMousemove(e: any) {
	if (!mousedownFlag) return ;
	var e = e || window.event;
	// 获取鼠标x坐标
	var newMouseX = getMousePos(e).x;
	// 若值无效,更新坐标然后返回
	if (Number.isNaN((newMouseX - mouseX) / r)) { mouseX = newMouseX; return; }
	// 更新视角以及坐标位置
	theta += (newMouseX - mouseX) / r;
	mouseX = newMouseX;
	// 更新照相机焦点
	renderCameraLookat();
}
// 更新照相机焦点
function renderCameraLookat() {
	camera.lookAt(new THREE.Vector3(camera.position.x + far * Math.sin(theta), 1, camera.position.z + far * Math.cos(theta)));
}

因为上述代码已经在mousemove的过程中修改了相机的焦点位置,所以不需要在animation方法中再去添加。

当前切换到汽车内部,可以拖动旋转相机,对车内情况进行观察了


后续

PS: 这里对于ThreeJs的性能消耗还是要提一下的,主要是使用GPU进行处理图形,但是这次突然是我的CPU 爆炸,排查原因可能是浏览器设置导致,然后我通过Edgeedge://flags/设置GPU。

并且我的笔记本有GPU0和GPU1,是双显。去Nvidia 中进行了设置,具体可参考:Win10笔记本双显卡怎么切换 在哪里设置独立显卡-百度经验 (baidu.com)

后续此汽车模型还有3种动画效果,之后再进行播放和切换了。

未完待续......

相关推荐
落霞的思绪1 小时前
CSS复习
前端·css
咖啡の猫3 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲5 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5816 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路6 小时前
GeoTools 读取影像元数据
前端
ssshooter6 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry7 小时前
Jetpack Compose 中的状态
前端
dae bal8 小时前
关于RSA和AES加密
前端·vue.js
柳杉8 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
lynn8570_blog8 小时前
低端设备加载webp ANR
前端·算法