代码拉取完成,页面将自动刷新
<!DOCTYPE html>
<html lang="en">
<head>
<title>Threejs实现汽车3D展示,改变汽车颜色,汽车爆炸分解合体效果</title>
<meta charset="utf-8">
<script type="text/javascript" src="libs/statistics.js"></script>
<script type="text/javascript" src="libs/steak.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script type="importmap">
{
"imports": {
"three": "./libs/jsm/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import {
OrbitControls
} from './libs/jsm/OrbitControls.js';
import {
GLTFLoader
} from './libs/jsm/GLTFLoader.js';
import {
RGBELoader
} from './libs/jsm/RGBELoader.js';
import {
TWEEN
} from './libs/jsm/tween.module.min.js';
import {
GUI
} from './libs/jsm/lil-gui.module.min.js';
let camera, scene, renderer, stats, gui;
let grid;
let controls;
let carModel;
const wheels = [];
let bodyMaterial, detailsMaterial, glassMaterial;
function init() {
const container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(render);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.85;
container.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize);
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.position.set(4.25, 1.4, -4.5);
controls = new OrbitControls(camera, container);
controls.enableDamping = true;
controls.maxDistance = 90;
controls.target.set(0, 0.5, 0);
controls.update();
scene = new THREE.Scene();
scene.background = new THREE.Color(0x333333);
scene.environment = new RGBELoader().load('assets/textures/equirectangular/venice_sunset_1k.hdr');
scene.environment.mapping = THREE.EquirectangularReflectionMapping;
scene.fog = new THREE.Fog(0x333333, 10, 15);
grid = new THREE.GridHelper(20, 40, 0xffffff, 0xffffff);
grid.material.opacity = 0.2;
grid.material.depthWrite = false;
grid.material.transparent = true;
scene.add(grid);
// materials
bodyMaterial = new THREE.MeshPhysicalMaterial({
color: 0xff0000,
metalness: 1.0,
roughness: 0.5,
clearcoat: 1.0,
clearcoatRoughness: 0.03,
sheen: 0.5
});
detailsMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 1.0,
roughness: 0.5
});
glassMaterial = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
metalness: 0.25,
roughness: 0,
transmission: 1.0
});
const map = new THREE.TextureLoader().load("assets/textures/hotspot.png");
const material = new THREE.SpriteMaterial({
map: map
});
const sprite = new THREE.Sprite(material);
sprite.scale.set(0.1, 0.1, 0.1);
sprite.position.set(1, 0.8, 0.2);
// scene.add(sprite);
var sprite2 = sprite.clone()
sprite2.position.set(-1, 0.8, 0.2);
// scene.add(sprite2);
var sphereGeometry = new THREE.SphereGeometry(0.02, 50, 50);
var sphereMaterial = new THREE.MeshBasicMaterial({
color: "#ffffff"
});
var sphere1 = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere1.position.set(1, 0.8, 0.2);
// scene.add(sphere1);
var sphere2 = sphere1.clone()
sphere2.position.set(-1, 0.8, 0.2);
// scene.add(sphere2);
const shadow = new THREE.TextureLoader().load('assets/models/ferrari_ao.png');
const loader = new GLTFLoader();
loader.load('assets/models/car.glb', function(gltf) {
initExplodeModel(gltf.scene)
carModel = gltf.scene.children[0];
carModel.getObjectByName('body').material = bodyMaterial;
carModel.getObjectByName('rim_fl').material = detailsMaterial;
carModel.getObjectByName('rim_fr').material = detailsMaterial;
carModel.getObjectByName('rim_rr').material = detailsMaterial;
carModel.getObjectByName('rim_rl').material = detailsMaterial;
carModel.getObjectByName('trim').material = detailsMaterial;
carModel.getObjectByName('glass').material = glassMaterial;
wheels.push(
carModel.getObjectByName('wheel_fl'),
carModel.getObjectByName('wheel_fr'),
carModel.getObjectByName('wheel_rl'),
carModel.getObjectByName('wheel_rr')
);
// shadow
const mesh = new THREE.Mesh(
new THREE.PlaneGeometry(0.655 * 4, 1.3 * 4),
new THREE.MeshBasicMaterial({
map: shadow,
blending: THREE.MultiplyBlending,
toneMapped: false,
transparent: true
})
);
mesh.rotation.x = -Math.PI / 2;
mesh.renderOrder = 2;
carModel.add(mesh);
scene.add(carModel);
});
createGUI();
}
function createGUI() {
const states = ['红色', '蓝色', '黄色'];
const emotes = ['聚合分散', '重置'];
const params = {
聚合分散: 0,
重置: reset,
};
const bc = {
车身颜色: '#ffffff',
opacity: 1,
}
const gc = {
玻璃颜色: '#ffffff',
opacity: 1,
}
const dc = {
座椅颜色: '#ffffff',
opacity: 1,
}
const gui = new GUI();
gui.addColor(bc, '车身颜色').onChange(function() {
bodyMaterial.color.set(bc.车身颜色);
});
gui.addColor(dc, '座椅颜色').onChange(function() {
detailsMaterial.color.set(dc.座椅颜色);
});
gui.addColor(gc, '玻璃颜色').onChange(function() {
glassMaterial.color.set(gc.玻璃颜色);
});
gui.add(params, emotes[0], 1, 30).step(0.1).name(emotes[0]).onChange(function(value) {
applyScalar(value)
});
gui.add(params, emotes[1]);
}
function initExplodeModel(model) {
//模型包围盒
var modelBox3 = new THREE.Box3();
var meshBox3 = new THREE.Box3();
//获取模型的包围盒
modelBox3.setFromObject(model);
//计算模型的中心点坐标,这个为爆炸中心
var explodeCenter = modelBox3.getCenter(new THREE.Vector3());
model.traverse(function(value) {
if (value.isMark || value.isMarkChild || value.isLine || value.isSprite) return;
if (value.isMesh) {
meshBox3.setFromObject(value);
//获取每个mesh的中心点,爆炸方向为爆炸中心点指向mesh中心点
var meshCenter = meshBox3.getCenter(new THREE.Vector3());
//计算爆炸方向
value.userData.worldDir = new THREE.Vector3().subVectors(meshCenter, explodeCenter).normalize();
// 爆炸距离 mesh中心点到爆炸中心点的距离
value.userData.worldDistance = new THREE.Vector3().subVectors(meshCenter, explodeCenter);
// 原始坐标
value.userData.originPosition = value.getWorldPosition(new THREE.Vector3());
// mesh中心点
value.userData.meshCenter = meshCenter.clone();
value.userData.explodeCenter = explodeCenter.clone();
}
})
const helper = new THREE.Box3Helper(modelBox3, 0xffff00);
// scene.add(helper);
}
// 合并
function merge() {
console.log("合并")
}
// 分散
function applyScalar(scalar) {
let model = carModel.clone();
carModel.traverse(function(value) {
if (!value.isMesh || !value.userData.originPosition) return;
const distance = value.userData.worldDir
.clone()
.multiplyScalar(value.userData.worldDistance.length() * scalar);
const offset = new THREE.Vector3().subVectors(
value.userData.meshCenter,
value.userData.originPosition
);
const center = value.userData.explodeCenter;
const newPos = new THREE.Vector3().copy(center).add(distance).sub(offset);
const localPosition = value.parent?.worldToLocal(newPos.clone());
localPosition && value.position.copy(localPosition);
});
}
function reset() {
location.reload();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function render() {
// TWEEN.update();
controls.update();
renderer.render(scene, camera);
const time = -performance.now() / 1000;
for (let i = 0; i < wheels.length; i++) {
wheels[i].rotation.x = time * Math.PI * 2;
}
grid.position.z = -(time) % 1;
}
init();
// 拾取对象
function pickupObjects(event) {
// 点击屏幕创建一个向量
var raycaster = new THREE.Raycaster();
var vector = new THREE.Vector2((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window
.innerHeight) * 2 + 1);
raycaster.setFromCamera(vector, camera);
let intersects = raycaster.intersectObjects(scene.children);
// console.log(intersects)
}
document.addEventListener('click', pickupObjects, false); //监听单击拾取对象初始化球体
</script>
</body>
</html>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。