前言
这个问题其实提早之前就解决了,一直懒得更新博客。 趁着最近“山竹”台风天放假,我就来更新一波。
其实区域限制在OrbitContrl 中是有的,但是OrbitControl的上下交互不及EditorControl 来得舒服。 所以,客户要求使用EditorControl的效果,但是EditorControl又没有OrbitControl的区域限制效果。 倘若不限制区域的话,用户将很有可能不知所踪,这样交互就很糟糕。
于是为了解决这个问题,我就借鉴了OrbitControl的区域限制写法。
OrbitControl区域限制剖析 OrbitControl.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 this .minDistance = 0 ;this .maxDistance = Infinity ;this .minZoom = 0 ;this .maxZoom = Infinity ;this .minPolarAngle = 0 ; this .maxPolarAngle = Math .PI ; this .minAzimuthAngle = - Infinity ; this .maxAzimuthAngle = Infinity ;
上面的部分就是OrbitControl中关于限制区域的API 那么这些API是怎么运作的呢? 我们可以追踪minDistance的去向。 在169行代码可以找到这一行。
1 2 spherical.radius = Math .max ( scope.minDistance , Math .min ( scope.maxDistance , spherical.radius ) );
也就说让 spherical.radius 的最大最小范围只能是 maxDistance 和 minDistance 那么什么是 spherical.radius 呢? 继续追查spherical
1 2 var spherical = new THREE .Spherical ();
在260行可以看到这里声明了spherical 什么是 THREE.Spherical ? 又去官方文档查找资料
从这里可以知道 spherical 和数学运算有关,通过spherical可以限制摄像机做圆周运动。 radius是圆心半径,而 phi 和 theta 就分别是经纬度,刚好和官方说的 spherical coordinates 吻合。
由此可以知道远近距离就是通过限制这个球坐标系的半径实现的。
那么同理 minPolarAngle 和 minAzimuthAngle 就是分别限制 经度 和 纬度 了。
EditorContrl 区域限制修改
EditorContrl 和 OrbitControl 不是用相同的方法开发的,直接复制代码就别想了。 通过观察EditorControl的API可以发现, this.pan this.zoom this.rotate 这三个函数是控制移动的关键部分。
而这其中 this.rotate 使用了spherical的方法。 因此迁移代码也就不难了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 this .rotate = function ( delta ) { vector.copy ( object.position ).sub ( center ); spherical.setFromVector3 ( vector ); spherical.theta += delta.x ; spherical.phi += delta.y ; spherical.theta = Math .max ( scope.minAzimuthAngle , Math .min ( scope.maxAzimuthAngle , spherical.theta ) ); spherical.phi = Math .max ( scope.minPolarAngle , Math .min ( scope.maxPolarAngle , spherical.phi ) ); spherical.makeSafe (); vector.setFromSpherical ( spherical ); object.position .copy ( center ).add ( vector ); object.lookAt ( center ); scope.dispatchEvent ( changeEvent ); };
如此一来就实现了经纬度旋转限制的效果 但是修改this.zoom的时候就遇到了麻烦 这里的this.zoom并没有使用 spherical 而是直接计算 摄像机 到 聚焦中心 的距离
如此就无法直接使用 sphercial 的代码套用 在这个过程中也遇到了很多坑,搞不好就是旋转好好地,一平移镜头就会跳。 本来想着不用 spherical 来解决的,最后还是妥协了,把spherical加进去计算就OK了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 this .zoom = function ( delta ) { var distance = object.position .distanceTo ( center ); delta.multiplyScalar ( distance * scope.zoomSpeed ); if ( delta.length () > distance ) return ; vector.copy ( object.position ).sub ( center ); spherical.setFromVector3 ( vector ); spherical.radius += delta.z ; spherical.radius = Math .max ( scope.minDistance , Math .min ( scope.maxDistance , spherical.radius ) ); vector.setFromSpherical ( spherical ); object.position .copy ( center ).add ( vector ); scope.dispatchEvent ( changeEvent ); };
最后的最后就是需要限制生相机的聚焦中心了。 这个功能OrbitControl也没有,但是想到spherical的区域限制实现。 自然也就有限制中心的想法。
查了 Three.js 提供的数学运算,其中就有BOX3 的方法 这样就可以用类似的方法将聚焦中心限制在盒子中了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 this .panLimitMax = new THREE .Vector3 (10 ,3 ,10 );this .panLimitMin = new THREE .Vector3 (-10 ,-3 ,-10 );var box = new THREE .Box3 ();box.min = this .panLimitMin ; box.max = this .panLimitMax ; this .pan = function ( delta ) { var distance = object.position .distanceTo ( center ); delta.multiplyScalar ( distance * scope.panSpeed ); delta.applyMatrix3 ( normalMatrix.getNormalMatrix ( object.matrix ) ); object.position .add ( delta ); center.add ( delta ); vector.copy ( object.position ).sub ( center ); spherical.setFromVector3 ( vector ); box.min = this .panLimitMin ; box.max = this .panLimitMax ; box.clampPoint (center,center); vector.setFromSpherical ( spherical ); object.position .copy ( center ).add ( vector ); scope.dispatchEvent ( changeEvent ); };
如此就实现了将摄像机聚焦中心限制在盒子中的效果,其实根据同样的方法可以迁移到球中。
总结
突然发现其实很多复杂的东西Three.js都已经做好给你了,我并不清楚 sphercial 和 BOX3 背后的数学运算,但是既然提供了相关的API,只管用就是了。 所以自己还是未能深入到WebGL的深层学习中,只是学习Three.js流于表面的东西。
十月将近了,后面就是和研究了快一年的Three.js道别的时候了。 时间真的过得很快,想来去年11月份的时候,聪哥让我研究WebGL。 我就看了Lynda.com 和 pluralsight.com 的教程入坑了,在那里我学习到了Three.js 后面的学习教程越来越少,只能靠看官方提供的文档和案例了,但是还是能做出一些东西,感觉还是挺欣慰的。因为这些的学习,自己的视力严重下降也真的让我感到悲哀…
最近也将 THREE_View.js 的API文档写好了,后面继续Three.js的开发就相对简单了,通过自己封装的脚本可以写少很多很多的代码。