WebGL的3D家居创意设计总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ztp_bb/article/details/84960878

回顾整个比赛,直到到现在还是觉得做的很水。不过大学期间有机会团队去开发自己的项目,对于技术或者是沟通的能力还有会有很大的提高。最开始选择这个命题的时候真的觉得自己玩大了,3D什么的完全没有接触过,毕竟是学Java的,但是真正开始接触WebGL或者说是three.js的时候,感觉迷上这个技术了,场景、相机、渲染器、光线选择、碰撞检测、粒子效果等等。不过这个技术的学习成本确实比较大,首先是就是被普遍吐槽的官方文档,确实写的不细致;再者就是英文材料,对于英语不好的童鞋并不友好;最后就是Three.js的坑实在是太多了,既然是坑就慢慢的填。我购买过一本Three.js开发指南,当时对于这个技术的学习也就停留在可以实现出我们设计的需求就够了(所以不会使用3D建模工具),整个项目是在2017年8月和9月这段时间完成的,9月底是截止日期了,其实很慌临提交的前一天还有bug。不过好在这个组最后进决赛的人数也不多,竞争压力比隔壁那个几个组小太多了,好在最后成绩不错。

最开始在设计项目的时候,按照规定的要求是模仿酷家乐制作一个在线的3D家装设计平台,我们将整个业务逻辑拆成两个主要的模块:第一个是户型制作模块,就是设计一个在线的建模工具,专门用于制作房屋模型的;另外一个就是3D家装模块,用来对制作好的户型进行装修。基本上核心的业务逻辑都是在前端完成的,前后端分离后台提供接口用来对数据进行保存交互,当时我是负责户型制作模块,不过在后续重构把整个前端都包了。在第一次编码的时候真的是js掌握的不好,ECMAScript、BOM、DOM就草草的了解,所以写出来的代码太丑了~已经不想吐槽了。下面几张是户型设计工具的截图,有些图标是copy酷家乐的,由于这个模块不能在手机端低分辨率的设备

    

    

上使用,所以就不需要过多的考虑响应式的问题,户型设计工具主要包括的有绘制墙壁、绘制门窗、自动生成地板,这几步是主要操作。这几个功能需要的额外功能很多,接下来做必要的说明:

  • 坐标转换:浏览器坐标是一个二维坐标,而我们想要点击的3D场景是一个三维坐标,我们在屏幕上点击一个位置如何映射到3D场景,这里用到的是光线选择器,简单来说就是在相机与我们点击的点构成了一条射线, 射线经过的所有物体都被存储在一个数组中,数组第一个元素就是点击的物体,我们通过这种方式去与3D场景中的元素进行交互。这里再多说两句,var selected  = intersects[0]获取的是点击的对象object,这个object还包括这几个属性 [ { distance, point, face, faceIndex, object }, … ],distance - 射线的起点到相交点的距离,point - 在世界坐标中的相交点坐标,face -相交的面,faceIndex - 相交的面的索引,另外现光线选择器选不到子对象。是因为intersectObject 这个方法参数如下( object, recursive : Boolean, optionalTarget : Array ) 第二个参数为true则检查后代对象,默认值为false。
event.preventDefault(); 

//以屏幕中心为原点,值的范围为-1到1.

mouse.x = (event.clientX / window.innerWidth) * 2 - 1;

mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; 

raycaster.setFromCamera( mouse, camera ); 

var intersects = raycaster.intersectObjects( scene.children );

var selected = intersects[0];
  • 平面图绘制:最开始考虑使用canvas,后来偷了个懒three.js的相机种类很多,THREE.OrthographicCamera相机就是一个平面相机,自顶向下看3D场景就是一个平面图,当需要切换到3D效果时,将相机的类型换为THREE.PerspectiveCamera并调整为合适的位置即可,所以整个绘制区域是一个3D场景,使用正交相机使其看起来是一个平面图。
  • 添加墙壁:这个功能类似于一个画板程序,具体的实现就不做说明了,通过renderer.render(scene, camera)去渲染画面,考虑到性能不需要一直循环渲染,每次需要刷新页面时调用即可。
  • 绘制矫正:鼠标点击是不精确的,尤其是在点击某一面墙壁时,可以将这个墙壁拆分为两面墙,但是点击处很难是精确的中心线的位置,这里需要做一下矫正,通过简单的计算将坐标偏移到正确的位置。另外比较重要的就是让直线保持水平垂直,单凭肉眼很难做到,这里需要一些偏移处理,这个模块设计鼠标移动过程进行检测,如果偏移距离小于15则认为用户在画水平或者垂直线,将对应的坐标赋值为确定值。
  • 地板生成:地板是以房间为单位,所以上面图片中的地板有两个(整个户型的大轮廓不会生成对应地板)。地板自动生成算法有两个步骤,一是遍历找出所有的地板回路,记录下所有的顶点,第二步是使用THREE.Shape生成地板图案,并附上指定的材质。并不是用户每次操作都会进行遍历,首先会根据连通图的边数 - 顶点数 + 1 =闭合区域个数来判定图中有没有闭合回路,如果有则dfs通过回溯的方式找到回路,每次找到回路进行两个判定,一是这个回路是否包含子回路,同样是利用连通图的边数 - 顶点数 + 1 =闭合区域个数进行判定,第二个判定是否已经所搜到相同的回路,将该回路与保存下来的其他回路进行比较,只有满足不包含子回路而且没有搜到过这两个条件才会将回路保存。生成地板就很简单了,根据顶点坐标使用THREE.Shape的moveTo和lineTo方法可以生成对应的mesh。
  • 3D展示:通过鼠标自由的拖动视角是利用了three.js自带的THREE.OrbitControls控件使用十分方便,但是一旦开始这个控件,即使当我们离开3D展示这个模块THREE.OrbitControls一直会不停的渲染,update开启控件,requestAnimationFrame一直进行渲染。使用window.cancelAnimationFrame(OrbitControlsAnmo)可以结束动画。
//开启控件
OrbitControls.update();
OrbitControlsAnmo = requestAnimationFrame(render);

//关闭动画
if (OrbitControlsAnmo)
        window.cancelAnimationFrame(OrbitControlsAnmo);
  • 保存户型:这个模块保存已经设计好的户型模型,以及一张户型平面图。户型模型通过JSON.stringify(scene.toJSON())方法导出场景的json格式的模型,提交后台通过I/O操作写入文件保存到服务器上,renderer.domElement.toDataURL('image/png')方法获取用户绘制的平面图的,这个方法拿到的是base64位编码,建议转为png格式的图片。

 

 

猜你喜欢

转载自blog.csdn.net/ztp_bb/article/details/84960878
今日推荐