项目中有一个电子围栏的需求,之前已经通过点,线,面的博文中的代码实现了多变形电子围栏的绘制.圆形的还未实现,未了达到与之前样式统一,因此无法通过three.js的管道来实现.

ThreeBSP

three.js本身并没有提供用于几何体布尔运算的构造函数,需要借助一个库ThreeBSP.js实现。几何体的布尔运算可以借助数学中学习的差集、并集、交集概念去理解, 几何体之间的运算本质上就是两个顶点集合的运算,具体运算的算法可以查看计算几何学的理论内容,多数的三维软件基本都有布尔运算的相关命令, 尤其是机械类的三维建模软件,对于计算机辅助设计有兴趣的可以多研究。
Three.js 绘制空心圆柱体 - 图1

绘制一个外圆内方的铜钱

  1. 绘制一个简易铜钱形状几何体很简单,绘制一个扁圆柱减去一个立方体就可以。
  1. /**
  2. * 创建网格模型
  3. */
  4. //几何体对象
  5. var cylinder = new THREE.CylinderGeometry(50,50,5,40);//圆柱
  6. var box = new THREE.BoxGeometry(40,5,40);//立方体
  7. //材质对象
  8. var material=new THREE.MeshPhongMaterial({color:0x0000ff});
  9. //网格模型对象
  10. var cylinderMesh=new THREE.Mesh(cylinder,material);//圆柱
  11. var boxMesh=new THREE.Mesh(box,material);//立方体
  12. //包装成ThreeBSP对象
  13. var cylinderBSP = new ThreeBSP(cylinderMesh);
  14. var boxBSP = new ThreeBSP(boxMesh);
  15. var result = cylinderBSP.subtract(boxBSP);
  16. //ThreeBSP对象转化为网格模型对象
  17. var mesh = result.toMesh();
  18. scene.add(mesh);//网格模型添加到场景中
  1. 网格Mesh模型对象作为构造函数ThreeBSP()的参数,可以把three.js的普通Mesh模型包装为ThreeBSP对象,表示对应Mesh对象的ThreeBSP对象可以进行布尔运算 ,计算结果在执行toMesh()方法可以把ThreeBSP对象重新转化为Mesh对象。

布尔计算方法

方法 作用
intersrct 交集、重合的部分
union 并集、组合、相加
subtract 差集、相减

绘制圆形电子围栏

经过上面的一些内容,我们可以通过2个圆柱形,通过差集相减的方法来获取一个环形圆柱体,底部加入一个圆形底座,就完成了圆形电子围栏

  1. /**
  2. * 创建网格模型
  3. */
  4. let textureLoader = new THREE.TextureLoader();
  5. let texture = textureLoader.load('./images/danger_wall.png');
  6. texture.wrapS = THREE.RepeatWrapping; //水平方向如何包裹
  7. texture.wrapT = THREE.RepeatWrapping; // 垂直方向如何包裹
  8. // uv两个方向纹理重复数量、看板中重复数量
  9. texture.repeat.set(15, 1);
  10. var smallCylinderGeom = new THREE.CylinderGeometry(40, 35, 100, 100, 14); //- 中心小圆
  11. var largeCylinderGeom = new THREE.CylinderGeometry(40, 40, 10, 100, 4); //- 外圆
  12. var smallCylinderBSP = new ThreeBSP(smallCylinderGeom); //- 中心圆bsp对象
  13. var largeCylinderBSP = new ThreeBSP(largeCylinderGeom); //- 外圈圆bsp对象
  14. var intersectionBSP = largeCylinderBSP.subtract(smallCylinderBSP); //- 获取差集
  15. var redMaterial = new THREE.MeshBasicMaterial({ //- 设置空心圆柱体的样式
  16. map: texture,
  17. transparent: true,
  18. opacity: 1
  19. })
  20. var hollowCylinder = intersectionBSP.toMesh(redMaterial); //- 添加样式
  21. hollowCylinder.position.y += 5; //- 由于高度为10,翻转后有5的值在基准坐标之下,所以做Y轴校准
  22. scene.add(hollowCylinder); //- 添加到场景中
  23. //- 添加底座
  24. var geometry = new THREE.CircleGeometry(40, 100);
  25. var material = new THREE.MeshBasicMaterial({
  26. color: 0xFF0018,
  27. side: THREE.DoubleSide,
  28. transparent: true,
  29. opacity: 0.1
  30. });
  31. var circle = new THREE.Mesh(geometry, material);
  32. circle.rotateX(Math.PI / 2)
  33. scene.add(circle);

显示效果:

image.png

完整代码:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>绘制空心圆柱体</title>
  6. <style>
  7. body {
  8. margin: 0;
  9. overflow: hidden;
  10. /* 隐藏body窗口区域滚动条 */
  11. }
  12. </style>
  13. <!--引入three.js三维引擎-->
  14. <script src="./js-r119/three.js"></script>
  15. <script src="./js-r119/controls/OrbitControls.js"></script>
  16. <script src='./js-r119/libs/tween.min.js'></script>
  17. <!-- <script src='./js-r119/libs/stats.min.js'></script> -->
  18. <script src="https://johnson2heng.github.io/three.js-demo/lib/threebsp.js"></script>
  19. </head>
  20. <body>
  21. <script>
  22. /**
  23. * 创建场景对象Scene
  24. */
  25. var scene = new THREE.Scene();
  26. /**
  27. * 创建网格模型
  28. */
  29. let textureLoader = new THREE.TextureLoader();
  30. let texture = textureLoader.load('./images/danger_wall.png');
  31. texture.wrapS = THREE.RepeatWrapping; //水平方向如何包裹
  32. texture.wrapT = THREE.RepeatWrapping; // 垂直方向如何包裹
  33. // uv两个方向纹理重复数量、看板中重复数量
  34. texture.repeat.set(15, 1);
  35. var smallCylinderGeom = new THREE.CylinderGeometry(40, 35, 100, 100, 14); //- 中心小圆
  36. var largeCylinderGeom = new THREE.CylinderGeometry(40, 40, 10, 100, 4); //- 外圆
  37. var smallCylinderBSP = new ThreeBSP(smallCylinderGeom); //- 中心圆bsp对象
  38. var largeCylinderBSP = new ThreeBSP(largeCylinderGeom); //- 外圈圆bsp对象
  39. var intersectionBSP = largeCylinderBSP.subtract(smallCylinderBSP); //- 获取差集
  40. var redMaterial = new THREE.MeshBasicMaterial({ //- 设置空心圆柱体的样式
  41. map: texture,
  42. transparent: true,
  43. opacity: 1
  44. })
  45. var hollowCylinder = intersectionBSP.toMesh(redMaterial); //- 添加样式
  46. hollowCylinder.position.y += 5; //- 由于高度为10,翻转后有5的值在基准坐标之下,所以做Y轴校准
  47. scene.add(hollowCylinder); //- 添加到场景中
  48. //- 添加底座
  49. var geometry = new THREE.CircleGeometry(40, 100);
  50. var material = new THREE.MeshBasicMaterial({
  51. color: 0xFF0018,
  52. side: THREE.DoubleSide,
  53. transparent: true,
  54. opacity: 0.1
  55. });
  56. var circle = new THREE.Mesh(geometry, material);
  57. circle.rotateX(Math.PI / 2)
  58. scene.add(circle);
  59. /**
  60. * 光源设置
  61. */
  62. //点光源
  63. var point = new THREE.PointLight(0xffffff);
  64. point.position.set(400, 200, 300); //点光源位置
  65. scene.add(point); //点光源添加到场景中
  66. //环境光
  67. var ambient = new THREE.AmbientLight(0x444444);
  68. scene.add(ambient);
  69. /**
  70. * 相机设置
  71. */
  72. var width = window.innerWidth; //窗口宽度
  73. var height = window.innerHeight; //窗口高度
  74. var k = width / height; //窗口宽高比
  75. var s = 200; //三维场景显示范围控制系数,系数越大,显示的范围越大
  76. //创建相机对象
  77. var camera = new THREE.PerspectiveCamera(45, width / height, 0.25, 1000)
  78. camera.position.set(200, 300, 200); //设置相机位置
  79. camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
  80. /**
  81. * 创建渲染器对象
  82. */
  83. var renderer = new THREE.WebGLRenderer();
  84. renderer.setSize(width, height);//设置渲染区域尺寸
  85. renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
  86. document.body.appendChild(renderer.domElement); //body元素中插入canvas对象
  87. // 辅助坐标系 参数250表示坐标系大小,可以根据场景大小去设置
  88. let axisHelper = new THREE.AxisHelper(250);
  89. scene.add(axisHelper);
  90. // 获取与射线相交的对象数组
  91. function getIntersects(event) {
  92. event.preventDefault();
  93. // console.log("event.clientX:" + event.clientX)
  94. // console.log("event.clientY:" + event.clientY)
  95. // 声明 raycaster 和 mouse 变量
  96. var raycaster = new THREE.Raycaster();
  97. var mouse = new THREE.Vector2();
  98. // 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1
  99. mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  100. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  101. //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
  102. raycaster.setFromCamera(mouse, camera);
  103. // 获取与raycaster射线相交的数组集合,其中的元素按照距离排序,越近的越靠前
  104. var intersects = raycaster.intersectObjects(scene.children);
  105. //返回选中的对象数组
  106. return intersects;
  107. }
  108. /**
  109. * 点击模型对象把镜头拉近
  110. */
  111. //- 添加鼠标对视觉相机的操作
  112. var controls = new THREE.OrbitControls(camera, renderer.domElement);
  113. //- 渲染函数
  114. function render() {
  115. //执行渲染操作 指定场景、相机作为参数
  116. renderer.render(scene, camera);
  117. requestAnimationFrame(render)
  118. TWEEN.update();
  119. controls.update();
  120. }
  121. render();
  122. </script>
  123. </body>
  124. </html>