汽车的3D展示是一个比较常见的应用,今天我就通过three.js来与大家分享如何实现一个非常简单的3D汽车展示场景。
前言
虽然例子很简单,但也需要您掌握基本的three.js
知识点,例如场景、模型等基础。
感兴趣的可以先访问在线例子看看。
开始
废话不多说,现在就来开始进行代码的实践。
汽车创建
首先,我们需要使用GLTF/GLB
模型加载器来将模型加载到场景中,比如最基础的代码示例:
ts
new GLTFLoader().loade(...)
你可以从我的代码仓库里面获取模型,也可以自己去找一个喜欢能用的模型资源。
灯光创建
因为材质需要灯光的原因,所以我们还得加入灯光,这里我使用聚光灯效果来实现,添加灯光也非常的简单:
ts
const ambientLight = new AmbientLight(0xffffff, 0.1);
const spotLight1 = new SpotLight(0xffffff, 200, 20, Math.PI * 0.3);
const spotLight2 = new SpotLight(0xffffff, 200, 20, Math.PI * 0.3);
spotLight1.position.set(3, 5, 0);
spotLight2.position.set(-3, 5, 0);
scene.add(ambientLight, spotLight1, spotLight2);
完成这几个简单的步骤后,我们就可以在场景中看见这辆酷炫的赛车了:
颜色更换
现在来实现动态修改赛车颜色的功能。
更换材质
因为我们需要自己来控制汽车车身等位置的颜色,所以需要我们自定义材质来覆盖之前的材质,代码示例:
ts
const bodyMaterial = new MeshPhysicalMaterial({
color: this.debugParams.bodyColor, metalness: 0.6, roughness: 0.5, clearcoat: 0.725, clearcoatRoughness: 0.03
});
const tireMaterial = new MeshStandardMaterial({
color: this.debugParams.tireColor, metalness: 1.0, roughness: 0.5
});
const glassMaterial = new MeshPhysicalMaterial({
color: this.debugParams.glassColor, metalness: 0.7, roughness: 0, transmission: 0.8, clearcoatRoughness: 0.03
});
const lightMaterial = glassMaterial.clone();
const obj = this.model.scene.children[0];
// 车身
const bodyObj = obj.getObjectByName('Object_10') as Mesh;
bodyObj.material = bodyMaterial;
// 玻璃
const glassObj = obj.getObjectByName('Object_11') as Mesh;
glassObj.material = glassMaterial;
// 轮胎
const tire1 = obj.getObjectByName('Circle008_5') as Object3D;
const tire2 = obj.getObjectByName('Circle009_6') as Object3D;
const tire3 = obj.getObjectByName('Circle010_7') as Object3D;
const tire4 = obj.getObjectByName('Circle011_8') as Object3D;
[tire1, tire2, tire3, tire4].forEach(tireObj => {
tireObj.children.forEach(item => {
(item as Mesh).material = tireMaterial;
});
});
因为每个模型对应的位置名称可能不同,所以通过getObjectByName
方法获取对应对象时,应该先明确需要覆盖材质部位的名称,可以在Blender
或其他建模软件中对照。
接下来,我再介绍材质对应的属性:
- color:表示材质的颜色,也是我们需要更改的属性;
- mataless:金属度,模拟金属光泽的;
- roughness:粗糙度,越粗糙反光越差;
- transmission:透明度,表示光线能穿透的程度;
- clearcoat:清漆,可以表示额外的光泽效果;
- clearcoatRoughness:清漆粗糙度,理解为控制清漆的效果强度;
调试器
当我们修改材质的时候,会不断手动修改代码来看效果,这种效率是很低的,所以需要通过一个调试器来解决这个问题,同时调试器也让我们有了动态修改汽车颜色的功能。
通过npm
安装dat.gui
插件:
shell
npm i dat.gui
安装完成以后,通过下面所示代码来添加调试功能:
ts
this.debugParams = {
bodyColor: 0x49519e,
tireColor: 0x8c8c8c,
glassColor: 0xffffff,
lightColor: 0x567bff,
}
this.addColorDebug('bodyColor', bodyMaterial, '车身颜色');
this.addColorDebug('tireColor', tireMaterial, '轮胎颜色');
this.addColorDebug('glassColor', glassMaterial, '玻璃颜色');
this.addColorDebug('lightColor', lightMaterial, '车前盖大灯颜色');
addColorDebug(key: string, material: MeshPhysicalMaterial | MeshStandardMaterial, name: string) {
this.gui.addColor(this.debugParams, key).onFinishChange(() => {
const color = new Color(this.debugParams[key]);
material.color = color;
}).name(name);
}
当然各人写法不同,但是思路都是一致的,通过dat.gui
来动态修改颜色,然后将新的颜色赋值给对应的材质。 完成上面步骤后,我们来看看效果图:
可以看到,切换颜色的功能已经实现了。但是,切换颜色时效果比较突兀,这里我们通过使用gsap
库来实现过渡的效果,实现很简单,只需要将上面的这行代码改为如下:
ts
// material.color = color;
gsap.to(material.color, { r: color.r, g: color.g, b: color.b, duration: 1 });
现在再来看看修改之后的效果图:
其他效果
当然,我们还可以实现一些其他效果,例如在车的某个部位放一个标签,通过点击标签将摄像头移动过去。
标签
在汽车模型上放置标签我不知道怎么去实现比较好,就暂时通过一个CircleGeometry
来放在一个合适的位置,如下图:
鼠标事件
通过Raycaster
对象,我们可以实现当鼠标点击标签时的交互,代码如下:
ts
window.addEventListener('mousedown', evt => {
const x = evt.clientX;
const y = evt.clientY;
mouse.x = (x / this.game.width) * 2 - 1;
mouse.y = -(y / this.game.height) * 2 + 1;
raycaster.setFromCamera(mouse, this.game.gameCamera.camera);
// 检测是否点击到了标签
const objects = raycaster.intersectObject(this.point.mesh);
if (objects.length) {
gsap.to(this.game.gameControls.controls.target, {
x: x,
y: y,
z: z,
duration: 1
});
gsap.to(this.game.gameControls.controls.object.position, {
x: x,
y: y,
z: z,
duration: 1,
onComplete: () => {
// do sth
}
});
}
});
通过Raycaster
的setFromCamera
方法就可以实现鼠标点击物体的射线检测了,并且当触发事件后我们就可以做一些交互操作了,来看看实现后的效果图:
结语
这个例子的实现比较简单,没有什么比较复杂的点,所以很容易就能实现。
当然,也可以在车辆上做一些其他交互操作丰富场景,这里就不再继续说明了。