一、模型居中
上文加載模型後發現threejs的原點對應的是revit的項目基點,這樣導緻初始加載後左右拖動建築是圍繞Y旋轉,體驗很差,是以模型初始化時候模型居中、旋轉,代碼如下:
function setCenter() {
var box3 = new THREE.Box3();
// 計算層級模型group的包圍盒
// 模型group是加載一個三維模型傳回的對象,包含多個網格模型
//擴充此包圍盒的邊界,使得對象及其子對象在包圍盒内,包括對象和子對象的世界坐标的變換。
box3.expandByObject(scene);
var center = new THREE.Vector3(0, 0, 0);
// 計算一個層級模型對應包圍盒的幾何體中心在世界坐标中的位置
box3.getCenter(center);
console.log('檢視幾何體中心坐标', center);
console.log('坐标', scene.position);
// 重新設定模型的位置,使之居中。
scene.position.x = scene.position.x - center.x
scene.position.y = scene.position.y - center.y
scene.position.z = scene.position.z - center.z;
console.log('坐标', scene.position);
}
原理是将場景放入到box中,拓展包圍盒與場景的大小一緻,然後通過包圍盒居中的坐标計算後将場景居中。
如果想和revit展示一緻,需要對模型進行180度的旋轉,因為revit和threejs的坐标系表示不同,左手坐标系和右手坐标系。
gLTFLoader.load(url, function(obj) {
obj.scene.rotateY(Math.PI);//旋轉180度
.....
}, onProgress);``
二、z-fighting 問題
加載的模型有些疊加的部分随着錄影機的拉動會閃爍,如圖:
查詢資料是因為疊加部分的mesh共用了一個深度值,渲染器不知道該渲染那個導緻閃爍,這便是Z-Fighting問題,相關問題的詳細說明:
https://www.cnblogs.com/lst619247/p/9098845.html
this.renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("canvas-model-view"),
antialias: true, //設定抗鋸齒
alpha: true, //背景透明
autoClear: true,
logarithmicDepthBuffer: true
});
需要将logarithmicDepthBuffer參數設為true即可
二、平滑移動
要想使錄影機平滑移動至某一點,需要貝塞爾曲線,貝塞爾曲線通過很少的控制點,去生成複雜的平滑曲線,通過擷取到了曲線上的點來移動錄影機進而達到平滑移動的效果,threejs提供了CubicBezierCurve3方法來實作貝塞爾曲線。
function toPosition(targetX,targetY,targetZ){
//擷取目前camera位置
let camPosition=viewer.camera.position; //擷取錄影機目前位置
let newPosition=new THREE.Vertex(targetX,targetY,targetZ); //設定目标位置
let curve=addLines(camPosition,newPosition).curve; //繪制貝塞爾曲線
//取curve的10個點
let points=curve.getPoints(10);
let index=0;
//錄影機每50毫秒移動一個點的位置
let a=setInterval(function () {
viewer.camera.position.set(points[index].x,points[index].y,points[index].z);
viewer.camera.lookAt(new THREE.Vertex(0,0,0))
index++;
viewer.render();
if(index>10){
clearInterval(a);
}
},50);
}
// 添加線條
function addLines(v0, v3) {
// 計算向量夾角
let angle = v0.angleTo(v3) * 270 / Math.PI / 10; // 0 ~ Math.PI
let aLen = angle * 50,
hLen = angle * angle * 120;
let p0 = new THREE.Vector3(0, 0, 0);
// 開始,結束點
// let v0 = groupDots.firstObjectren[0].position;
// let v3 = groupDots.children[1].position;
// 法線向量
let rayLine = new THREE.Ray(p0, getVCenter(v0.clone(), v3.clone()));
// 頂點坐标
let vtop = rayLine.at(hLen / rayLine.at(1).distanceTo(p0));
// 控制點坐标
let v1 = getLenVcetor(v0.clone(), vtop, aLen);
let v2 = getLenVcetor(v3.clone(), vtop, aLen);
// 繪制貝塞爾曲線
let curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);
let geo = new THREE.Geometry();
geo.vertices = curve.getPoints(10);
let mat = new THREE.LineBasicMaterial({color: 0xff0000});
return {
curve: curve,
lineMesh: new THREE.Line(geo, mat)
};
}
// 計算v1,v2 的中點
function getVCenter(v1, v2) {
let v = v1.add(v2);
return v.divideScalar(2);
}
// 計算V1,V2向量固定長度的點
function getLenVcetor(v1, v2, len) {
let v1v2Len = v1.distanceTo(v2);
return v1.lerp(v2, len / v1v2Len);
}
效果如下: