最近业余在折腾Three.js,其中碰到一个问题是如何在给定的一组三维坐标点,画出一个自定义几何体。
查阅多篇文章后觉得国内很少有写自定义形状的文章,通过文字和代码说明让读者可了解到通过Three.js自定义一个形状;以此记录此文供后来人参考。
我们使用铅笔在一个三维坐标上画一个长方体时需要知道8个点,如下蓝色坐标点分别时A、B、C、D、E、F、G、H ;
我们使用红色铅笔按照顺序链接8个点,如下就形成了长方体的结构。
其实在连线时,我们可以将每个面的四个点,拆分为两两组合的三角形; 如A、B、D、C这个面可以拆分为ABD、BDC,见如下图(1)、(2) 图(1) 图(2)
然后我们再在长方体的面上涂一层颜色。
以上我们就通过铅笔画出了一个长方体。
总结:3个点形成三角形,多个三角形形成面,多个面形成几何体。请继续往下看,你会发现Three中画几何体是一样的。
在Three中,有一个类叫THREE.Geometry,是THREE的核心类,所有的几何图形都是基于THREE.Geometry扩展,看官方说明
以上说明是THREE.Geometry可直接自定义几何形状,用编码易用性来降低了性能,需要性能高的可使用BufferGeometries。let geometry = new THREE.Geometry();
let A = new THREE.Vector3(0, 0, 0.5); geometry.vertices.push(A); let B = new THREE.Vector3(1, 0, 0.5); geometry.vertices.push(B); let C = new THREE.Vector3(0, 0.5, 0.5); geometry.vertices.push(C); let D = new THREE.Vector3(1, 0.5, 0.5); geometry.vertices.push(D); let E = new THREE.Vector3(1, 0, -0.5); geometry.vertices.push(E); let F = new THREE.Vector3(1, 0.5, -0.5); geometry.vertices.push(F); let G = new THREE.Vector3(0, 0.5, -0.5); geometry.vertices.push(G); let H = new THREE.Vector3(0, 0, -0.5); geometry.vertices.push(H);
由于vertices 是一个数组,A、B、C、D、E、F、G、H所在索引分别为0、1、2、3、4、5、6、7。
// ABC三角形 let ABC = new THREE.Face3(0, 1, 2); // 其中 0, 1, 2是三个点在vertices中的索引 geometry.faces.push(ABC); // BDC三角形 let BDC = new THREE.Face3(1, 3, 2); // 其中 1, 3, 2是三个点在vertices中的索引 geometry.faces.push(BDC);
通过以上两个三角形,我们可以组合成一个四边形ABDC面。
如上操作BDFE、DCGF等剩余面,我们就可以得到长方体的六个面;从而配置成了一个长方体。
geometry.center();
let material = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide, //两面可见 color: 0xdfdfdf, //颜色 }); this.customCube = new THREE.Mesh(geometry, material);
<body> <style type="text/css"> html, body, canvas{ width: 100%; height: 100%; margin: 0; padding: 0; } </style> <canvas></canvas> <script src="https://cdn.bootcss.com/three.js/91/three.min.js"></script> <script type="text/javascript"> (function(){ let canvas = document.querySelector('canvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; let renderer = new THREE.WebGLRenderer({ context: canvas.getContext("webgl"), antialias: true }); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.setSize(canvas.width, canvas.height); renderer.setClearColor(0xffffff, 1); scene = new THREE.Scene(); scene.background = new THREE.Color(0xaaaaaa); let AxesHelper = new THREE.AxesHelper(5); scene.add(AxesHelper); let camera = new THREE.OrthographicCamera( -2, 2, canvas.height/canvas.width * 2, -canvas.height/canvas.width* 2, 0.2, 100 ); camera.position.set(5, 5, 20); camera.lookAt(scene.position); scene.add(camera); let directionalLight = new THREE.DirectionalLight(0xffffff, 1, 100); directionalLight.position.set(0, 0, 2); scene.add(directionalLight); // 自定义几何体 let geometry = new THREE.Geometry(); let A = new THREE.Vector3(0, 0, 0.5); geometry.vertices.push(A); let B = new THREE.Vector3(1, 0, 0.5); geometry.vertices.push(B); let C = new THREE.Vector3(0, 0.5, 0.5); geometry.vertices.push(C); let D = new THREE.Vector3(1, 0.5, 0.5); geometry.vertices.push(D); let E = new THREE.Vector3(1, 0, -0.5); geometry.vertices.push(E); let F = new THREE.Vector3(1, 0.5, -0.5); geometry.vertices.push(F); let G = new THREE.Vector3(0, 0.5, -0.5); geometry.vertices.push(G); let H = new THREE.Vector3(0, 0, -0.5); geometry.vertices.push(H); // [A,B,C,D,E,F,G,H] // [0,1,2,3,4,5,6,7] // 前面 let ABC = new THREE.Face3(0, 1, 2); // 其中 0, 1, 2是三个点在vertices中的索引 geometry.faces.push(ABC); let CDB = new THREE.Face3(2,3,1); geometry.faces.push(CDB); // 后面 let EFG = new THREE.Face3(4,5,6); geometry.faces.push(EFG); let EHG = new THREE.Face3(4,7,6); geometry.faces.push(EHG); // 上面 let CDF = new THREE.Face3(2,3,5); geometry.faces.push(CDF); let CFG = new THREE.Face3(2,5,6); geometry.faces.push(CFG); // 下面 let ABE = new THREE.Face3(0,1,4); geometry.faces.push(ABE); let AHE = new THREE.Face3(0,7,4); geometry.faces.push(AHE); // 右侧面 let BDE = new THREE.Face3(1,3,4); geometry.faces.push(BDE); let DFE = new THREE.Face3(3,5,4); geometry.faces.push(DFE); // 左侧面 let ACH = new THREE.Face3(0,2,7); geometry.faces.push(ACH); let CGH = new THREE.Face3(2,6,7); geometry.faces.push(CGH); // 让geometry在坐标系居中 geometry.center() // 计算出颜色,使得geometry在光照下能看到多个面 geometry.computeFaceNormals(); // 开始渲染 let material = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide, //两面可见 color: 0xdfdfdf, //三角面颜色 }); let customCube = new THREE.Mesh(geometry, material); scene.add(customCube); renderer.render(scene, camera); animate(); // 让自定义的几何体动起来 function animate() { customCube.rotation.x += 0.01; customCube.rotation.y += 0.02; customCube.rotation.z += 0.01; renderer.render(scene, camera); window.requestAnimationFrame(animate); } })(); </script> </body>
知道以上方法后你可以在三维空间画想要的多面几何体。并结合JS循环可减少上面的代码量。
以上内容如有错误请指正。