最近公司要做一个桥梁的模型,前端加载模型并操作模型,中间遇到了一些问题,在这里写一个demo记录一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>threejs</title> <style> body,html{ margin: 0px; width: 100%; height: 100%; } #model{ width: calc(100% - 200px); height: 500px; } </style> </head> <body> <div style="width: 100%;height: 100%;display: flex;"> <div style="width: 200px;height: 100%;background-color: pink;"></div> <div id="model"></div> </div> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@v0.162.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@v0.162.0/examples/jsm/" } } </script> <script src="./js/threeModel.js" type="module"></script> </body> </html>
注意:当加载模型的标签是100%宽时,three.js中的raycaster用法是canvas是整个页面窗口的,所以它的代码是:
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
但是由于我的模型展示标签没有占满整个页面,所以用上述方法获取模型老是不对,但实际上我的项目中渲染模型的区域只有页面中的一块区域,所以这么些写获取到的部位总是不准确,终于在一个博主那找到了解决方案,在此记录一下:
let getClientRect = threeModel.getBoundingClientRect();
mouse.x = ((event.clientX - getClientRect .left) / width) * 2 - 1;
mouse.y = -((event.clientY - getClientRect .top) / height) * 2 + 1;
点击加轮廓颜色时发现模型场景颜色变了,选要加几行代码:
import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'
// 要加在EffectComposer之后
const gammaCorrectionShader = new ShaderPass( GammaCorrectionShader );
composer.addPass( gammaCorrectionShader );
完整的threeModel.js文件
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { OutlinePass } from "three/addons/postprocessing/OutlinePass.js";
import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'
const scene = new THREE.Scene();
const threeModel = document.getElementById("model");
const width = threeModel.offsetWidth;
const height = threeModel.offsetHeight;
// 创建相机
const camera = new THREE.PerspectiveCamera(30,width / height,1,3000);
camera.position.set(-69, 137, 153);
camera.lookAt(0,0,0); // 坐标原点
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.toneMappingExposure = 5; // 增加曝光度
renderer.setSize(width, height);
this.renderer.setClearColor(0xffffff, 1);
threeModel.appendChild(renderer.domElement);
// 添加灯光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); // 平行光
directionalLight.position.set(480,200,300);
scene.add(directionalLight);
const ambient = new THREE.AmbientLight(0xffffff, 1); // 环境光
scene.add(ambient);
loadModel();
function loadModel() {
const loader = new GLTFLoader();
loader.load(
"../model/梁式桥.gltf",
(gltf) => {
gltf.scene.rotation.y = -Math.PI / 8; // y轴旋转
scene.add(gltf.scene);
animate();
},
(xhr) => {
// 计算模型加载进度条
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
(error) => {
console.error("An error happened while loading the model", error);
}
);
}
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 添加惯性
// 设置垂直旋转范围,从而只允许左右旋转
controls.minPolarAngle = Math.PI / 4; // 最小垂直角度为0度
controls.maxPolarAngle = Math.PI / 2; // 最大垂直角度为180度
// 禁用缩放和平移功能(可选)
controls.enableZoom = true;
controls.enablePan = false;
var highlightedObject = null;
// 监听鼠标点击事件
renderer.domElement.addEventListener('click', onClick, false);
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene,camera);
composer.addPass(renderPass);
// 使用OutlinePass场景会变暗
const gammaCorrectionShader = new ShaderPass( GammaCorrectionShader );
composer.addPass( gammaCorrectionShader );
const v2 = new THREE.Vector2(width,height);
const outlinePass = new OutlinePass(v2,scene,camera);
function onClick(event) {
let getBoundingClientRect = threeModel.getBoundingClientRect()
let mouse = new THREE.Vector2();
// 计算鼠标点击位置的归一化设备坐标
mouse.x = ((event.clientX - getBoundingClientRect .left) / width) * 2 - 1;
mouse.y = -((event.clientY - getBoundingClientRect .top) / height) * 2 + 1;
let raycaster = new THREE.Raycaster();
// 通过射线投射来获取鼠标点击位置的对象
raycaster.setFromCamera(mouse, camera);
let intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
outlinePass.selectedObjects = [intersects[0].object];
outlinePass.visibleEdgeColor.set(0xffff00);
outlinePass.edgeThickness = 4;
outlinePass.edgeStrength = 6;
composer.addPass(outlinePass);
}
}
function animate() {
composer.render();
requestAnimationFrame(animate);
}