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

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

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

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

在这里插入图片描述


创建场景和导入素材

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

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

scene.background = new THREE.Color("#ccc");

导入模型后:
在这里插入图片描述


设置光源和地面

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

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

// 添加灯光组
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轴旋转。

// 制作地面
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方法

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去用鼠标拖动改变相机位置。并且为了后续效果,我禁用了缩放功能

const controls = new OrbitControls(camera, render.domElement);  
controls.enableZoom = false;

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

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

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

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

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

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

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

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

在这里插入图片描述

代码:

// 定义角度
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。
![[t10.png]]
并且我的笔记本有GPU0和GPU1,是双显。去Nvidia中进行了设置,具体可参考:Win10笔记本双显卡怎么切换 在哪里设置独立显卡-百度经验 (baidu.com)

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

未完待续…

在这里插入图片描述

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐