加载外部三维模型
1、在新手实战一的代码基础上修改,以下是基础代码:
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls";
//1、场景
const scene = new THREE.Scene();
//2、摄像机
//四个参数,摄像机锥体角度,摄像机锥体看到的宽高比(设置成宽口的宽高比就可以了),摄像机锥体看到物体的最近和最远的距离
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
1000
);
//3、渲染器
const render = new THREE.WebGLRenderer({
//抗锯齿
antialias: true,
});
//设置渲染的画面宽高
render.setSize(window.innerWidth, window.innerHeight);
document.getElementById("app").appendChild(render.domElement);
//设置camera的距离,否则摄像机在原点(0,0,0)的位置。
camera.position.z = 10;
camera.position.x = 4;
camera.position.y = 10;
//添加轨道控制器,控制摄像机的位置,不影响在页面上的位置。
const controls = new OrbitControls(camera, render.domElement);
//是否开起控制器阻尼系数(理解为对旋转的阻力,系数越小阻力越小)
//请注意,如果该值被启用,必须在动画循环中调用.update()
controls.enableDamping = true;
//添加辅助坐标系和辅助网格
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
//辅助网格,一个参数是格子的大小,第二个参数是一行有几个格子
const gridHelper = new THREE.GridHelper(20, 10);
gridHelper.material.opacity = 0.2;
gridHelper.material.transparent = true;
scene.add(gridHelper);
function init(time) {
controls.update();
//在页面渲染场景和相机
render.render(scene, camera);
//requestAnimationFrame浏览器的方法
requestAnimationFrame(init);
}
init();
//设置画面自适应
window.addEventListener("resize", () => {
//更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
//更新摄像头的投影矩阵
camera.updateProjectionMatrix();
//更新渲染器
render.setSize(window.innerWidth, window.innerHeight);
//设置渲染器的像素比
render.setPixelRatio(window.devicePixelRatio);
});
2、引入gltf模型加载库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
3、GLTF加载器
const loader = new GLTFLoader();
4、加载
模型库:www.shvr.work/
注:glb文件放到public文件夹中
loader.load('/car.glb', function ( gltf ) {
const model = gltf.scene;
console.log('gltf对象场景属性',model);
//将模型在y轴方向旋转45度
model.rotation.y = 45;
// 返回的场景对象gltf.scene插入到threejs场景中
scene.add(model);
})
5、添加地板
const geometry = new THREE.PlaneGeometry(60, 60); //默认在XOY平面上
const material = new THREE.MeshPhysicalMaterial({
color: "yellow",
metalness: 0.9, //车外壳金属度
roughness: 0.5, //车外壳粗糙度
envMapIntensity: 2.5, //环境贴图对Mesh表面影响程度
});
const mesh = new THREE.Mesh(geometry, material);
//旋转地板角度
mesh.rotation.x = 30;
scene.add(mesh)
6、添加聚光灯
const spotLight = new THREE.SpotLight("#fff", 2000.0);
spotLight.angle = Math.PI / 6; //散射角度
spotLight.penumbra = 0.2;
spotLight.decay = 2;
spotLight.distance = 30;
spotLight.shadow.radius = 30;
spotLight.shadow.mapSize.set(4096, 4096);
//灯的位置
spotLight.position.set(0, 20, -10);
//目标的位置
spotLight.target.position.set(0, 0, 0);
// 设置产生投影的网格模型
spotLight.castShadow = true;
scene.add(spotLight); //光源添加到场景中
7、设置圆柱型的展厅
// CylinderGeometry:圆柱
const geometry = new THREE.CylinderGeometry(50, 50, 100);
const material = new THREE.MeshBasicMaterial({
color: 0xffff00,
side: THREE.DoubleSide,
});
const cylinder = new THREE.Mesh(geometry, material);
scene.add(cylinder);
8、旋转会看到车底,缩小会看到外面的圆柱体。我们通过设置限制来解决这个问题
//最大缩放,最小缩放
controls.maxDistance = 6;
controls.minDistance = 1;
//最小旋转角度
controls.minPolarAngle = 0;
//最小旋转角度
controls.maxPolarAngle = (80 / 360) * 2 * Math.PI;
9、修改车身
three.js编辑器:three.js editor (threejs.org)
9.1 通过编辑器导入.glb模型文件,增加灯光
9.2 点击车身,在右侧可查看到此材质的名称
9.3 递归模型节点
// 递归遍历所有模型节点批量修改材质
model.traverse(function(obj) {
if(obj.isMesh)
console.log(obj, 'obj');
})
9.4 修改车身的材质
//车身和两个门的材质
let bodyMesh = new THREE.MeshPhysicalMaterial({
color: "green",
metalness: 1, //车外壳金属度
roughness: 0.5, //车外壳粗糙度
clearcoat: 1.0, //物体表面清漆层或者说透明涂层的厚度
clearcoatRoughness: 0.03, //透明涂层表面的粗糙度
});
obj.material = material;
};
// 递归遍历所有模型节点批量修改材质
model.traverse(function (obj) {
if (obj.isMesh) {
if (obj.name == "Object_103" || obj.name == "Object_77" || obj.name == "Object_64") {
obj.material = bodyMesh;
}
}
});
10、添加图形界面
lil-gui:lil-gui 0.18.2 (georgealways.com)
10.1 定义面板,面板中的属性和方法
import GUI from "lil-gui";
const gui = new GUI();
const myObject = {
color: "#ffffff",
carOpen,
carIn,
carClose
};
gui
.addColor(myObject, "color")
.name("颜色")
.onChange((value) => {
//设置车身的颜色
material.color.set(value);
});
10.2 添加车的相关操作
//获取车门
let doors = [];
//在递归遍历所有模型节点的时候获取车门------详情见9.4
if (obj.name === 'Empty001_16' || obj.name === 'Empty002_20') {
// 门
doors.push(obj)
}
gui.add(myObject, "carOpen").name('打开车门');
gui.add(myObject, "carClose").name('关门车门')
gui.add(myObject, "carIn").name('车内视角');
gui.add(myObject, "carClose").name('车外视角')
function carOpen() {
for (let i = 0; i < doors.length; i++) {
setAnimationDoor({ x: 0 }, { x: Math.PI / 3 }, doors[i])
}
}
function carClose() {
for (let i = 0; i < doors.length; i++) {
setAnimationDoor({ x: Math.PI / 3 }, { x: 0 }, doors[i])
}
}
function 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 });
}
function 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 });
}
function 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()
}
function 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()
}
11、Raycaster(射线拾取模型)
注:通过点击材质,实现与10.2一样的功能
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
window.addEventListener("click", onPointerMove);
function onPointerMove(event) {
// calculate pointer position in normalized device coordinates
// (-1 to +1) for both components
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
// update the picking ray with the camera and pointer position
raycaster.setFromCamera(pointer, camera);
// calculate objects intersecting the picking ray
const intersects = raycaster.intersectObjects(scene.children);
console.log(intersects, "intersects");
for (let i = 0; i < intersects.length; i++) {
if (
intersects[i].object.name == "Object_77" ||
intersects[i].object.name == "Object_64"
) {
carOpen();
}else
{
carClose();
}
}
}
12、添加车的阴影
//渲染器支持阴影
render.shadowMap.enabled = true
// 递归遍历所有模型节点批量修改材质中添加以下代码:
//车模型产生阴影
obj.castShadow = true;
//地板接收阴影
mesh.receiveShadow = true;