本文原创 如转载请注明出处!!!

本博客地址http://www.cnblogs.com/we-jack

本文原创,如果有同样需求的小伙伴请第一时间联系我 或者在留言区留言 

上次为大家提供了3D模型的展示之后 发现网上有很多想要计算3D模型表面积和体积的需求 那么经过掉了几百根头发的艰辛历程之后 终于为大家解决了这一需求 按照惯例先上图为证

当然我这样写 有的人认为我在忽悠 你说你的体积 表面积是这就是这啊 没有可验证性么  

那好~ 没有对比就没有伤害 下面是某3D打印网站上传同样模型后给出的数据 各位看官们看好了

实打实的讲道理 他给出的体积还是负值 我都给转正了[:斜眼笑]

好了 闲话少絮 直接给大家上代码 (JS代码如果有需要的直接留言找我)

1 <head>
2     <meta charset="UTF-8" />
3     <title>WebGL</title>
4     <script type="text/javascript" charset="utf-8" src="js/three.js"></script>
5     <script src="js/STLLoader.js"></script>
6     <script src="js/TrackballControls.js"></script>
7     <script src="js/AsciiEffect.js"></script>
8     <style>body{overflow:hidden;background:#eee}</style>
9 </head>

先引入我们几个必须的JS (之前有人问OBJ模型怎么上传 这个问题可以直接百度three.js的使用方法)

接下来用JS定义一个画布

1     var WIDTH,HEIGHT;
2     var    renderer;

定义好了之后 我们初始化一下我们的three

 1 function initThree() {
 2         WIDTH = document.documentElement.clientWidth/2;<!--{foreach from=$recommended_goods item=rgoods}--> <!-- {/foreach} -->
 3         HEIGHT = document.documentElement.clientHeight/2;
 4         /* 渲染器 */
 5         renderer = new THREE.WebGLRenderer();
 6         renderer.setSize(WIDTH , HEIGHT);
 7         renderer.setClearColor(new THREE.Color(0x66666));
 8 
 9         renderer.gammaInput = true;
10         renderer.gammaOutput = true;
11 
12         document.body.appendChild(renderer.domElement);
13     }

下来是摄像头定义

 1 /* 摄像头 */
 2     var camera;
 3     function initCamera() {
 4         var VIEW_ANGLE = 45,
 5                 ASPECT = WIDTH / HEIGHT,
 6                 NEAR = 0.1,
 7                 FAR = 10000;
 8         camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
 9         camera.position.set(20, 0, 0);
10         //设置视野的中心坐标
11         camera.lookAt(scene.position);
12     }

场景植入

1 /* 场景 */
2     var scene;
3     function initScene() {
4         scene = new THREE.Scene();
5     }

灯光定义

 1 /* 灯光 */
 2     var light,light2,light3;
 3     function initLight() {
 4         //平行光
 5         light = new THREE.DirectionalLight(0xFFFFFF);
 6         light.position.set(0, 99, 0).normalize();
 7         scene.add(light);
 8         //环境光
 9         light2 = new THREE.AmbientLight(0x999999);
10         scene.add(light2);
11         //点光源
12         light3 = new THREE.PointLight(0x00FF00);
13         light3.position.set(300, 0, 0);
14         scene.add(light3);
15     }

动画定义

1 function animate() {
2         requestAnimationFrame( animate );
3         controls.update();
4         effect.render( scene, camera );
5     }

调用所有方法开始执行

1 function threeStart() {
2         initThree();
3         initScene();
4         initCamera();
5         initLight();
6         initObject();
7         initControl();
8         animate();
9     }

显示对象

/* 显示对象 */
    var cube;
    function initObject(){
        // ASCII file
        var loader = new THREE.STLLoader();

        loader.addEventListener( \'load\', function ( event ) {
            var loading = document.getElementById("Loading");
            loading.parentNode.removeChild(loading);
            var geometry = event.content;
            var Area = 0.0;
            var volumes = 0.0;

    for(var i = 0; i < geometry.faces.length; i++){
        var Pi = geometry.faces[i].a;
        var Qi = geometry.faces[i].b;
        var Ri = geometry.faces[i].c;

        var P = new THREE.Vector3(geometry.vertices[Pi].x, geometry.vertices[Pi].y, geometry.vertices[Pi].z);
        var Q = new THREE.Vector3(geometry.vertices[Qi].x, geometry.vertices[Qi].y, geometry.vertices[Qi].z);
        var R = new THREE.Vector3(geometry.vertices[Ri].x, geometry.vertices[Ri].y, geometry.vertices[Ri].z);
//        volumes += volumeOfT(P, Q, R);  会产生负数..............
        volumes += volumeOfT(R, Q, P);
         Area += AreaOfTriangle(R, Q, P);
    }
    SurfaceArea = (Area / 100).toFixed(2);
        console.log(\'表面积:\'+SurfaceArea);
    loadedObjectVolume = (volumes / 1000).toFixed(2);
        console.log(\'体积:\'+loadedObjectVolume);

    //砖红色
    var material = new THREE.MeshPhongMaterial( { ambient: 0xff5533, color: 0xff5533, specular: 0x111111, shininess: 200 } );
    //纯黑色
//            var material = new THREE.MeshBasicMaterial( { envMap: THREE.ImageUtils.loadTexture( \'http://localhost:8080/textures/metal.jpg\', new THREE.SphericalReflectionMapping() ), overdraw: true } ) ;
    //粉色 带阴影
//            var material = new THREE.MeshLambertMaterial( { color:0xff5533, side: THREE.DoubleSide } );
    //灰色
//            var material = new THREE.MeshLambertMaterial({color: 000000});    //材质设定  (颜色)

    var mesh = new THREE.Mesh( geometry, material );
    var center = THREE.GeometryUtils.center(geometry);
    var boundbox=geometry.boundingBox;
    var vector3 = boundbox.size(null);
    var vector3 = boundbox.size(null);
    console.log(vector3);
    var scale = vector3.length();
    camera.position.set(scale, 0, 0);
    camera.lookAt(scene.position);
    scene.add(camera);

    //利用一个轴对象以可视化的3轴以简单的方式。X轴是红色的。Y轴是绿色的。Z轴是蓝色的。这有助于理解在空间的所有三个轴的方向。要添加这个帮手,使用下面的代码:
    var axisHelper = new THREE.AxisHelper(800);
    scene.add(axisHelper);

    //周围边框
    bboxHelper = new THREE.BoxHelper();
    bboxHelper.visible = true;
    var meshMaterial = material;
    mainModel = new THREE.Mesh(geometry, meshMaterial);
    bboxHelper.update(mainModel);
    bboxHelper.geometry.computeBoundingBox();
    scene.add(bboxHelper);


        //它吸引的特定对象3D对象(它看起来像金字塔)与线几何,这有助于可视化什么指定摄像机包含在它的视锥。
//            var cameraParObj = new THREE.Object3D();
//            cameraParObj.position.y = 200;
//            cameraParObj.position.z = 700;
//            scene.add(cameraParObj);
//            perspectiveCamera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.01, 1500);
//            cameraParObj.add(perspectiveCamera);
//            var cameraHelper = new THREE.CameraHelper(perspectiveCamera);
//            scene.add(cameraHelper);


        //地板网格
        //var gridHelper = new THREE.GridHelper(500, 40); // 500 is grid size, 20 is grid step
        //gridHelper.position = new THREE.Vector3(0, 0, 0);
        //gridHelper.rotation = new THREE.Euler(0, 0, 0);
        //scene.add(gridHelper);
        //var gridHelper2 = gridHelper.clone();
        //gridHelper2.rotation = new THREE.Euler(Math.PI / 2, 0, 0);
        //scene.add(gridHelper2);
        //var gridHelper3 = gridHelper.clone();
        //gridHelper3.rotation = new THREE.Euler(Math.PI / 2, 0, Math.PI / 2);
        //scene.add(gridHelper3);

        //var grid = new THREE.GridHelper(300, 40, 25, [0, 0, 1], 0x000055, 0.2, true, "#FFFFFF", "left");
        //scene.add(grid);

        mesh.position.set(0,0,0);
//            mesh.position.x = scene.position.x;
//            mesh.position.y = scene.position.y ;
//            mesh.position.z = scene.position.z;
        scene.add(mesh);

        renderer.clear();
        renderer.render(scene, camera);
    } );
    // loader.load( \'3dfile/莫比乌斯环.STL\' );
    loader.load( \'3dfile/狼人.STL\' );
}

    //控制
    var effect;
    var controls;
    function initControl(){
        effect = new THREE.AsciiEffect( renderer );
        effect.setSize( WIDTH, HEIGHT );
        controls = new THREE.TrackballControls( camera,renderer.domElement);
    }

代码上面的注释都非常清楚了,根据这些我们很清楚的会看到在求体积的时候 我们必须先要得到一个四面体的有符号体积 – 基于你的三角形并在原点处排在最前面。音量的标志来自您的三角形是否指向原点的方向。(三角形的法线本身取决于顶点的顺序,这就是为什么你在下面看不到它的原因。)

这一点大家要是不懂的话 可以去搜索一下 一种计算任意非凸多面体积分性质的符号方法 这个方法详细的讲解了 多面体的体积算法 

在代码的77行处我们可以看到 我在拿到多面体的每一个面之后进行了遍历从而求出中心点到每个面的距离 再根据THREE提供的方法 得到三个有效点 然后将所有的点进行组合 使用 (-v321 + v231 + v312 – v132 – v213 + v123)/6.0; 求得体积

在计算表面积的时候出现了一点问题 如果模型出现在网格下面那么就会出现负值 但是数字的值是没有问题的 所以我们可以去将组合重新排序从而使负值转正 当然也可以使用绝对值的方法使数值转正

最后声明 如需转载请注明出处!!!

本文原创,如果有同样需求的小伙伴请第一时间联系我 或者在留言区留言 

版权声明:本文为we-jack原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/we-jack/p/8608623.html