首先需要一个扫描模型,工业上有专门的设备去采集模型的面然后通过建模软件去处理外表面贴图
我们这里取了一个ford汽车的发动机模型
为了让three.js能够使用,使用blender把模型保存为glb格式
为了让页面加载glb模型更快,需要对模型文件进行压缩
压缩 GLB(GL Transmission Format Binary)模型可以通过多种技术来实现,以减少文件大小并提高加载速度。以下是一些常见的方法:
1. 几何数据简化
- 网格简化:使用工具(如 Blender、MeshLab 或 Simplify3D)简化模型的几何结构,减少多边形数量。
- LOD(Level of Detail):创建不同细节级别的模型,根据视距加载不同的细节级别。
2. 纹理压缩
- 纹理分辨率:降低纹理的分辨率,尤其是在模型的细节不明显的部分。
- 纹理格式:使用压缩纹理格式(如 DDS、KTX 或 ASTC)来减少纹理文件的大小。
3. 使用 Draco 压缩
-
Draco:Google 的 Draco 库可以对 3D 网格进行高效压缩。您可以使用
gltf-pipeline
工具将 GLB 模型与 Draco 压缩结合起来。npm install -g gltf-pipeline gltf-pipeline -i model.glb -o model-draco.glb -d
ford-engine-model-4.glb 是我压缩后的模型,把压缩好的模型文件放入项目中
这里直接贴代码了, three.js是纯js框架,所以没有用vue和react来集成
<!DOCTYPE html>
<html lang="en">
<head>
<title>car engine gbl</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<style>
body {
margin: 0 !important;
}
a {
color: #2fa1d6;
}
p {
max-width: 600px;
margin-left: auto;
margin-right: auto;
padding: 0 2em;
}
body #info {
position: fixed;
font-size: 22px;
width: 100%;
right: 0;
text-align: left;
}
.content-parent {
padding: 0 40px;
display: flex;
justify-content: space-between;
}
.content > div {
display: inline-block;
font-size: 16px;
font-weight: bold;
/* color: tomato; */
}
.value {
font-size: 16px;
margin-left: 16px;
}
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
import {TransformControls} from 'three/addons/controls/TransformControls.js';
import { DRACOLoader } from "./jsm/loaders/DRACOLoader.js"
function getRandomRGBColor() {
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
// 如果需要,可以转换为十六进制字符串
var hex = '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
// 返回RGB对象或十六进制字符串,根据你的需要
// return { r, g, b }; // 返回RGB对象
return hex; // 返回十六进制字符串
}
// 使用函数
console.log(getRandomRGBColor()); // 输出类似 #3A72FF 的随机颜色(十六进制字符串)
let container, stats, clock, gui, mixer, actions, activeAction, orbitControls, transformControls;
let camera, scene, renderer, model, dracoLoader;
const OOI = {};
init();
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xe0e0e0);
container = document.createElement('div');
document.body.appendChild(container);
//camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 8000 );
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 5000);
camera.position.set(-40, 35, 50);
//camera.lookAt( -5, -3, 0 );
camera.lookAt(scene.position);
//scene.fog = new THREE.Fog(0xe0e0e0, 20, 100);
// const axesHelper = new THREE.AxesHelper(5);
// scene.add(axesHelper);
clock = new THREE.Clock();
// lights
// const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x8d8d8d, 3 );
// hemiLight.position.set( 0, 20, 0 );
// scene.add( hemiLight );
//
const dirLight = new THREE.DirectionalLight(0xffffff, 8);
dirLight.position.set(0, 20, 10);
scene.add(dirLight);
// ground
// const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0xcbcbcb, depthWrite: false } ) );
// mesh.rotation.x = - Math.PI / 2;
// scene.add( mesh );
// const grid = new THREE.GridHelper(5, 40, 0x000000, 0x000000);
// grid.material.opacity = 1;
// grid.material.transparent = false;
// scene.add(grid);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize);
// stats
// stats = new Stats();
// container.appendChild(stats.dom);
dracoLoader = new DRACOLoader()
// 设置解压路径,draco_decoder.js文件所在位置
dracoLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/");
dracoLoader.setDecoderConfig({ type: 'js' });
dracoLoader.preload();
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load('models/gltf/engine/ford-engine-model-4.glb', function (gltf) {
model = gltf.scene;
scene.add(model);
//createGUI( model, gltf.animations );
model.traverse(n => {
if (n.name === 'Scene') OOI.Scene = n
});
const targetPosition = OOI.Scene.position.clone();
orbitControls = new OrbitControls(camera, renderer.domElement);
orbitControls.minDistance = 30;
orbitControls.maxDistance = 50;
orbitControls.enableDamping = false;
orbitControls.enablePan = false;
orbitControls.enableRotate = false;
orbitControls.target.copy(targetPosition);
// transformControls = new TransformControls(camera, renderer.domElement);
// transformControls.size = 0.75;
// transformControls.showX = false;
// transformControls.showY = false;
// transformControls.showZ = false;
// transformControls.space = 'world';
// transformControls.attach(OOI.Scene);
// scene.add(transformControls);
//transformControls.addEventListener('mouseDown', () => orbitControls.enabled = false);
//transformControls.addEventListener('mouseUp', () => orbitControls.enabled = true);
let angle = 0;
const rotationSpeed = 0.0005; // 每帧旋转的角度
animate = function() {
// 围绕 y 轴旋转
angle += rotationSpeed;
camera.position.x = Math.cos(angle) * orbitControls.minDistance;
camera.position.z = Math.sin(angle) * orbitControls.minDistance;
camera.lookAt(orbitControls.target);
orbitControls.update();
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
}, undefined, function (e) {
console.error(e);
});
}
function fadeToAction(name, duration) {
activeAction = actions[name];
if (previousAction !== activeAction) {
previousAction.fadeOut(duration);
}
activeAction
.reset()
.setEffectiveTimeScale(1)
.setEffectiveWeight(1)
.fadeIn(duration)
.play();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function animate() {
const dt = clock.getDelta();
if (mixer) mixer.update(dt);
renderer.render(scene, camera);
stats.update();
}
</script>
</body>
</html>
贴一个three.js驱动ford-engine-model-4.glb 的页面效果,实现360°旋转
滚轮事件实现拉近
旋转到背面