Three.js模型测量

2,091 阅读3分钟

Three.js的模型测量


模型测量原理

通过Three.js提供的模型包围盒Three.Box3()方法将模型包围在一个区域中,具体过程是Box3()方法在三维空间内创建一个立方体边界对象,这个立方体边界对象通过计算一系列顶点坐标得到两个名为MaxMin的对象,这两个对象分别装载着包围盒中与原点(0,0,0)坐标相比坐标值最大和最小且在包围盒内两点间直线距离最大的两个三维空间顶点坐标。

image.png

Max和Min对象在三维空间中的位置关系如上图所示。

image.png

模型的长宽高直接通过Max对象的x,y,z值与Min对象的值对应相减即可。

测量公式参数说明如下表

image.png

模型测量的实现

模型测量借助Three.js中的数学方法Box3先将模型包围在创建的立方体边界对象内,通过setFromObject方法计算包围盒的三维对象,主要计算对象和子对象的世界坐标变换,接着获取模型的包围盒,计算包围盒Max对象和Min对象的坐标值,通过变量传递计算好的长宽高值到CSS2DObject对象中,使用CSS2DRenderer将预设文本和长宽高值渲染到场景中。最后场景渲染器调用渲染方法时,updateMatrixWorld方法才会更新模型及其子对象的世界矩阵本地矩阵,实现测量的功能。当模型旋转、缩放、平移时,模型对象及其子对象的坐标都会分别与旋转矩阵、缩放矩阵、平移矩阵得到新的旋转矩阵、缩放矩阵、平移矩阵,然后将这三个矩阵相乘得到本地矩阵,对象的世界矩阵就等于其世界矩阵,子对象的世界矩阵是其父对象的世界矩阵与其本地矩阵的乘积,若不更新坐标矩阵,模型及其包围盒的坐标将不会随着模型的旋转、缩放、平移而变化。

接下来上代码

首先设置四大天王:灯光、相机、渲染器、控制器

        const axesHelper = new THREE.AxesHelper( 5000 );
	scene.add( axesHelper );


        //灯光
        var point = new THREE.PointLight(0xffffff);
        point.position.set(400, 200, 300); //点光源位置
        scene.add(point); //点光源添加到场景中
        var ambient = new THREE.AmbientLight("#0c0c0c");//环境光
        scene.add(ambient);//环境光加入到场景中

        //相机
        var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
        camera.position.set(200, 300, 300);
        camera.lookAt(scene.position);
        // cameraTarget = new THREE.Vector3( 0, - 0.1, 0 );


        //渲染器
        var renderer = new THREE.WebGLRenderer();
        renderer.setSize( window.innerWidth, window.innerHeight );
        renderer.setClearColor(0xb9d3ff, 1);//背景颜色
	document.body.appendChild( renderer.domElement );

        var labelRenderer = new THREE.CSS2DRenderer();
        labelRenderer.setSize(window.innerWidth, window.innerHeight);
        labelRenderer.domElement.style.position = 'absolute';
        // 相对鼠标的相对偏移
        labelRenderer.domElement.style.top = '0px';
        labelRenderer.domElement.style.left = '0px';
        // //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
        labelRenderer.domElement.style.pointerEvents = 'none';
        document.body.appendChild(labelRenderer.domElement);

        //控制器
        var controls = new THREE.TrackballControls(camera,renderer.domElement);
        controls.rotateSpeed = 2.0;
        controls.zoomSpeed = 1.2;
	controls.panSpeed = 0.8;

这里用的模型是通过three.js创建的默认模型

        //模型
        var geometry1 = new THREE.BoxGeometry( 100, 200, 300 );
        var material = new THREE.MeshLambertMaterial( { color: 0xFFC0CB } );
        var cube1 = new THREE.Mesh( geometry1, material);
        cube1.position.set(0, 100, 0);
        scene.add( cube1 );

创建和获取模型包围盒

        // 模型包围盒
        var Geo1Box = new THREE.Box3();
        Geo1Box.name = 'Geo1';
        Geo1Box.setFromObject(cube1);
        group.updateMatrixWorld(true);//更新矩阵
        scene.add(new THREE.Box3Helper(Geo1Box,0xffff00));
        console.log(Geo1Box);

        //获取模型的包围盒
        Geo1Box.expandByObject(cube1);
	//获取长宽高
	var length = Geo1Box.max.x - Geo1Box.min.x;
	var width = Geo1Box.max.z - Geo1Box.min.z;
	var height = Geo1Box.max.y - Geo1Box.min.y;

创建CSS2D,这里的长宽高暂时写死了,可以自己写一个业务让长宽高自己判定

        // 创建CSS2D
        // 长度
        const lengthDiv = document.createElement( 'div' );
	lengthDiv.className = 'label';
	lengthDiv.textContent = '宽度:' + ' ' + length;
	lengthDiv.style.marginTop = '-1em';
        lengthDiv.style.cssText = "color: #fff; font-size:22px; font-family: sans-serif; padding: 2px; background: rgba(22, 0, 0, 0.5); border-radius:5px; left: 0%; top: 0%)"
	const lengthLabel = new THREE.CSS2DObject( lengthDiv );
        lengthDiv.style.pointerEvents = 'none';
	lengthLabel.position.set( 0, 0, 170 );
	cube1.add( lengthLabel );
        // 宽度
        const widthDiv = document.createElement( 'div' );
	widthDiv.className = 'label';
	widthDiv.textContent = '长度:' + ' ' + width;
	widthDiv.style.marginTop = '-1em';
        widthDiv.style.cssText = "color: #fff; font-size:22px; font-family: sans-serif; padding: 2px; background: rgba(22, 0, 0, 0.5); border-radius:5px; left: 0%; top: 0%)"
	widthLabel = new THREE.CSS2DObject( widthDiv );
        widthDiv.style.pointerEvents = 'none';
	widthLabel.position.set( 70, 0, 0 );
	cube1.add( widthLabel );
        // 高度
        const heightDiv = document.createElement( 'div' );
	heightDiv.className = 'label';
	heightDiv.textContent = '高度:' + ' ' + height;
	heightDiv.style.marginTop = '-1em';
        heightDiv.style.cssText = "color: #fff; font-size:22px; font-family: sans-serif; padding: 2px; background: rgba(22, 0, 0, 0.5); border-radius:5px; left: 0%; top: 0%)"
	heightLabel = new THREE.CSS2DObject( heightDiv );
        heightDiv.style.pointerEvents = 'none';
	heightLabel.position.set( 0, 150, 0 );
	cube1.add( heightLabel );

好了,要记录的就这么多,之后有空再完善吧。

gitee仓库链接