Three.js 实现3D三维树模型方案

在做智慧园区项目时,经常遇到植物模型,为了移动端和模型轻量化,则使用three.js代码生成,本节主要讲解植物模型创建思路。

这是玉兰模型

1、模型分为树干、树叶、花3类,树干通过平面图描点,获得变径管道坐标和直径,树叶通过平面图描点拉伸,获得三角形数组,花由花瓣、花托、花丝组成,花丝为管道体、花瓣、花托为拉伸几何,材质使用透明图片。

2、变径管道坐标和直径通过VolatilePipe转换,获得UV数组、positions数组、normals数组。

3、树叶拉伸后UV生成方式

//创建UV值 strDistance为距离值,sheight为拉伸长度,拉伸之后坐标数量增加1倍
        function getLsUvs(strDistance, sheight) {
            var jsonDce = JSON.parse("[" + strDistance + "]");
            var strUv_x = "";
            var strUv_y1 = "";
            var strUv_y2 = "";
            for (var i = 0; i < jsonDce.length; i++) {
                if (i == 0) {
                    strUv_x = "0";
                    strUv_y1 = "0";
                    strUv_y2 = "1";
                } else {
                    strUv_x = strUv_x + "," + keep15Decimal(jsonDce[i] / jsonDce[jsonDce.length - 1]);
                    strUv_y1 = strUv_y1 + "," + "0";
                    strUv_y2 = strUv_y2 + "," + "1";
                }
            }
            return strUv_x + "#" + strUv_y1 + "$" + strUv_x + "#" + strUv_y2;
        }

        //四舍五入保留15位小数(若第二位小数为0,则保留一位小数)
        function keep15Decimal(num) {
            var result = parseFloat(num);
            if (isNaN(result)) {
                return "传递参数错误,请检查!";
            }
            result = Math.round(num * 100000000000000) / 100000000000000;
            return result;
        }

4、树叶三角形数组生成positions数组、normals数组


        //三角形数组转Buffer值
        function TrgleToBufferValue(triangleArry) {
            // 点数
            var triangles = triangleArry.length;
            //三角形数组
            var trianglePointArry = triangleArry;

            var geometry = new THREE.BufferGeometry();

            var trgle_positions = [];
            // 点的法向量
            var trgle_normals = [];
            var trgle_colors = [];
            var trgle_indexs = [];
            var trgle_center = [];

            //总的各坐标值
            var total_x = 0;
            var total_y = 0;
            var total_z = 0;

            var color = new THREE.Color();

            // abc, 三个顶点位置
            var pA = new THREE.Vector3();
            var pB = new THREE.Vector3();
            var pC = new THREE.Vector3();
            // c点到b点的方向向量
            var cb = new THREE.Vector3();
            // a点到b点的方向向量
            var ab = new THREE.Vector3();

            for (var i = 0; i < triangles; i++) {
                //if (i > 0) { break;}
                var triangleObj = trianglePointArry[i];//一个三角形对象
                var onePoint = triangleObj[0];
                var twoPoint = triangleObj[1];
                var threePoint = triangleObj[2];

                // positions
                var ax = onePoint[0];
                var ay = onePoint[1];
                var az = onePoint[2];

                var bx = twoPoint[0];
                var by = twoPoint[1];
                var bz = twoPoint[2];

                var cx = threePoint[0];
                var cy = threePoint[1];
                var cz = threePoint[2];

                // 添加一个三角形的3个顶点, 每个顶点有xyz三个数据
                trgle_positions.push(keepNumDecimal(ax), keepNumDecimal(ay), keepNumDecimal(az));
                trgle_positions.push(keepNumDecimal(bx), keepNumDecimal(by), keepNumDecimal(bz));
                trgle_positions.push(keepNumDecimal(cx), keepNumDecimal(cy), keepNumDecimal(cz));


                total_x = total_x + ax + bx + cx;
                total_y = total_y + ay + by + cy;
                total_z = total_z + az + bz + cz;

                // 求法向量, 首先设置三角形的三个顶点
                pA.set(ax, ay, az);
                pB.set(bx, by, bz);
                pC.set(cx, cy, cz);
                // 求出两个方向向量
                cb.subVectors(pC, pB);
                ab.subVectors(pA, pB);
                // 叉积, 求法向量
                cb.cross(ab);
                // 单位化这个法向量
                cb.normalize();

                var nx = cb.x;
                var ny = cb.y;
                var nz = cb.z;
                // 添加法向量到法向量数组中
                //三角形的三个顶点的法向量相同, 因此复制三份
                trgle_normals.push(keepNumDecimal(nx), keepNumDecimal(ny), keepNumDecimal(nz));
                trgle_normals.push(keepNumDecimal(nx), keepNumDecimal(ny), keepNumDecimal(nz));
                trgle_normals.push(keepNumDecimal(nx), keepNumDecimal(ny), keepNumDecimal(nz));

            }

            //计算中点
            var center_x = parseInt(total_x / (triangles * 3));
            var center_y = parseInt(total_y / (triangles * 3));
            var center_z = parseInt(total_z / (triangles * 3));
            trgle_center = [center_x, center_y, center_z];
            return { uvs: [], positions: trgle_positions, normals: trgle_normals, center: trgle_center };

        }

5、通过BufferGeometry生成模型

        //获得BufferMesh
        function GetOneBufferGeometry(arryPositions, arryNormals, arryUvs, arryIndexs, meshMaterial, meshName) {
             var geometry = new THREE.BufferGeometry();
            var positions = arryPositions;
            // 点的法向量
            var normals = arryNormals;
            var uvs = arryUvs;
            //var colors = arryColors;
            //var indexs = arryIndexs;
            // 加入位置信息
            geometry.addAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
            // 加入法向量信息
            geometry.addAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
            //有UV数组时,添加UV映射
            if (uvs.length > 0) {
                //设置UV  UV坐标的数量要大于等于三角形的数量,否则模型空白
                geometry.addAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
            }
            geometry.computeBoundingSphere();
            var mesh = new THREE.Mesh(geometry, meshMaterial);         
            mesh.name = meshName;
            return mesh;
        }

6、在三维场景中,多数模型都可以使用变径管道和线条拉伸结合,重成模型UV、positions、normals,使用BufferGeometry快速渲染。

技术交流:873201677@qq.com

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值