Three.js自己已经有不少的网格模型,基本已经够咱们的使用,可是若是咱们仍是想本身根据顶点坐标来创建几何模型的话,Three.js也是能够的。javascript
基本效果如图:
css
实际上出于性能的考虑,three.js是认为咱们的几何体在整个生命周期中是不会改变的,可是咱们仍是想使用dat.gui.js去实时更新咱们自定义几何体的顶点信息。html
当顶点信息发生变化时,咱们就须要使用
geometry.verticesNeedUpdate = true;
可是在每一帧渲染完后这个值又会变为false,因此咱们须要每次渲染中都更新这个值。java
完整代码以下:git
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Three.js</title>
6 <script src="../../../Import/three.js"></script>
7 <script src="../../../Import/stats.js"></script>
8 <script src="../../../Import/Setting.js"></script>
9 <script src="../../../Import/OrbitControls.js"></script>
10 <script src="../../../Import/dat.gui.min.js"></script>
11 <script src="../../../Import/SceneUtils.js"></script>
12 <style type="text/css">
13 div#canvas-frame {
14 border: none;
15 cursor: pointer;
16 width: 100%;
17 height: 850px;
18 background-color: #333333;
19 }
20 </style>
21</head>
22<body onload="threeStart()">
23<div id="canvas-frame"></div>
24<script>
25 let renderer, camera, scene;
26 let controller;
27 let controls;
28 let vertices;
29 let faces;
30 let controlPoints = [];
31 let geom;
32 let mesh;
33
34 //初始化渲染器
35 function initThree() {
36 renderer = new THREE.WebGLRenderer({
37 antialias: true
38 });//定义渲染器
39 renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染的宽度和高度
40 document.getElementById("canvas-frame").appendChild(renderer.domElement);//将渲染器加在html中的div里面
41 renderer.setClearColor(0x333333, 1.0);//渲染的颜色设置
42 renderer.shadowMapEnabled = true;//开启阴影,默认是关闭的,太影响性能
43 renderer.shadowMapType = THREE.PCFSoftShadowMap;//阴影的一个类型
44
45
46 camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight, 1, 10000);//perspective是透视摄像机,这种摄像机看上去画面有3D效果
47
48 //摄像机的位置
49 camera.position.x = 10;
50 camera.position.y = 15;
51 camera.position.z = 15;
52 camera.up.x = 0;
53 camera.up.y = 1;//摄像机的上方向是Y轴
54 camera.up.z = 0;
55 camera.lookAt(0, 0, 0);//摄像机对焦的位置
56 //这三个参数共同做用才能决定画面
57
58 scene = new THREE.Scene();
59
60 let light = new THREE.SpotLight(0xffffff, 1.0, 0);//点光源
61 light.position.set(-40, 60, -10);
62 light.castShadow = true;//开启阴影
63 light.shadowMapWidth = 8192;//阴影的分辨率,能够不设置对比看效果
64 light.shadowMapHeight = 8192;
65 scene.add(light);
66 light = new THREE.AmbientLight(0xcccccc, 0.2);//环境光,若是不加,点光源照不到的地方就彻底是黑色的
67 scene.add(light);
68
69 cameraControl();
70
71 vertices = [
72 new THREE.Vector3(1, 3, 1),
73 new THREE.Vector3(1, 3, -1),
74 new THREE.Vector3(1, -1, 1),
75 new THREE.Vector3(1, -1, -1),
76 new THREE.Vector3(-1, 3, -1),
77 new THREE.Vector3(-1, 3, 1),
78 new THREE.Vector3(-1, -1, -1),
79 new THREE.Vector3(-1, -1, 1)
80 ];//顶点坐标,一共8个顶点
81
82 faces = [
83 new THREE.Face3(0, 2, 1),
84 new THREE.Face3(2, 3, 1),
85 new THREE.Face3(4, 6, 5),
86 new THREE.Face3(6, 7, 5),
87 new THREE.Face3(4, 5, 1),
88 new THREE.Face3(5, 0, 1),
89 new THREE.Face3(7, 6, 2),
90 new THREE.Face3(6, 3, 2),
91 new THREE.Face3(5, 7, 0),
92 new THREE.Face3(7, 2, 0),
93 new THREE.Face3(1, 3, 4),
94 new THREE.Face3(3, 6, 4),
95 ];//顶点索引,每个面都会根据顶点索引的顺序去绘制线条
96
97 geom = new THREE.Geometry();
98 geom.vertices = vertices;
99 geom.faces = faces;
100 geom.computeFaceNormals();//计算法向量,会对光照产生影响
101
102 //两个材质放在一块儿使用
103 let materials = [
104 new THREE.MeshLambertMaterial({opacity: 0.6, color: 0x44ff44, transparent: true}),//透明度更改
105 new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})//线条材质,让观察更直观一点
106
107 ];
108 //建立多材质对象,要引入SceneUtils.js文件,若是只有一个材质就不须要这个函数
109 mesh = THREE.SceneUtils.createMultiMaterialObject(geom, materials);
110 mesh.children.forEach(function (e) {
111 e.castShadow = true
112 });
113 scene.add(mesh);
114
115 initDat();
116 }
117 //可视化面板
118 function initDat() {
119 function addControl(x, y, z) {
120 controls = new function () {
121 this.x = x;
122 this.y = y;
123 this.z = z;
124 };
125 return controls;
126 }
127 controlPoints.push(addControl(3, 5, 3));
128 controlPoints.push(addControl(3, 5, 0));
129 controlPoints.push(addControl(3, 0, 3));
130 controlPoints.push(addControl(3, 0, 0));
131 controlPoints.push(addControl(0, 5, 0));
132 controlPoints.push(addControl(0, 5, 3));
133 controlPoints.push(addControl(0, 0, 0));
134 controlPoints.push(addControl(0, 0, 3));
135
136 //克隆一个几何体
137 let addClone = new function () {
138 this.clone = function () {
139
140 let clonedGeometry = mesh.children[0].geometry.clone();
141 let materials = [
142 new THREE.MeshLambertMaterial({opacity: 0.6, color: 0xff44ff, transparent: true}),
143 new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})
144
145 ];
146
147 let mesh2 = THREE.SceneUtils.createMultiMaterialObject(clonedGeometry, materials);
148 mesh2.children.forEach(function (e) {
149 e.castShadow = true
150 });
151
152 mesh2.translateX(Math.random()*4+3);
153 mesh2.translateZ(Math.random()*4+3);
154 mesh2.name = "clone";
155 //删掉场景中已经存在的克隆体,再从新建立一个
156 scene.remove(scene.getChildByName("clone"));
157 scene.add(mesh2);
158
159
160 }
161 };
162
163 let gui = new dat.GUI();
164
165 gui.add(addClone, 'clone');
166
167 for (let i = 0; i < 8; i++) {
168 let f1 = gui.addFolder('Vertices ' + (i + 1));//把每一个顶点的三个坐标都收拢在一个Folder里面,更加美观方便
169 f1.add(controlPoints[i], 'x', -10, 10);
170 f1.add(controlPoints[i], 'y', -10, 10);
171 f1.add(controlPoints[i], 'z', -10, 10);
172
173 }
174 }
175
176 // 摄像机的控制,能够采用鼠标拖动来控制视野
177 function cameraControl() {
178 controller = new THREE.OrbitControls(camera, renderer.domElement);
179 controller.target = new THREE.Vector3(0, 0, 0);
180 }
181
182 let plane;
183
184 //初始化物体
185 function initObject() {
186 //定义了一个地面
187 let planeGeometry = new THREE.PlaneGeometry(100, 100, 1, 1);
188 let planeMaterial = new THREE.MeshLambertMaterial({
189 color: 0xffffff,
190 });
191 plane = new THREE.Mesh(planeGeometry, planeMaterial);
192 plane.rotation.x = -0.5 * Math.PI;
193 plane.position.x = 15;
194 plane.receiveShadow = true;//开启地面的接收阴影
195 scene.add(plane);//添加到场景中
196 // initCustomObj();
197 }
198
199 //定义的一个功能文件
200 function initSetting() {
201 loadAutoScreen(camera, renderer);
202 loadFullScreen();
203 loadStats();
204 }
205
206 //动画
207 function render() {
208 stats.update();
209 //单材质几何体要更新顶点的话使用这一段语句
210 // for (let i = 0; i < 8; i++) {
211 // console.log(mesh);
212 // mesh.geometry.vertices[i].set(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z);
213 // mesh.geometry.verticesNeedUpdate = true;
214 // mesh.geometry.computeFaceNormals();
215 // }
216 let vertices = [];
217 for (let i = 0; i < 8; i++) {
218 vertices.push(new THREE.Vector3(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z));
219 }
220 mesh.children.forEach(function (e) {
221 e.geometry.vertices = vertices;
222 e.geometry.verticesNeedUpdate = true;//通知顶点更新
223 e.geometry.elementsNeedUpdate = true;//特别重要,通知线条链接方式更新
224 e.geometry.computeFaceNormals();
225 });
226
227 requestAnimationFrame(render);
228 renderer.render(scene, camera);
229 }
230 //主函数
231 function threeStart() {
232 initThree();
233 initObject();
234 initSetting();
235 render();
236 }
237</script>
238</body>
239</html>
复制代码
*github
在顶点发生变化时,若是是多材质对象的话,须要使用遍历每个子对象来进行更新顶点数据。而且不只要更新顶点,还要更新线条的链接方式geometry.elementsNeedUpdate = true,不然是没有效果的。(甚至尝试了一下不更新顶点,只更新线条也是能够达到实时更新的效果)算法
1let vertices = [];
2 for (let i = 0; i < 8; i++) {
3 vertices.push(new THREE.Vector3(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z));
4 }
5 mesh.children.forEach(function (e) {
6 e.geometry.vertices = vertices;
7 e.geometry.verticesNeedUpdate = true;//通知顶点更新
8 e.geometry.elementsNeedUpdate = true;//特别重要,通知线条链接方式更新
9 e.geometry.computeFaceNormals();
10 });
复制代码
若是是单一的材质几何体,就不须要去遍历每个子物体,直接把几何体的每个顶点值更改,而后在通知顶点更新,就能够了。canvas
1 //单材质几何体要更新顶点的话使用这一段语句
2 for (let i = 0; i < 8; i++) {
3 console.log(mesh);
4 mesh.geometry.vertices[i].set(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z);
5 mesh.geometry.verticesNeedUpdate = true;
6 mesh.geometry.computeFaceNormals();
7 }
复制代码
老版本的three.js,SceneUtils是没有单独拿出来做为一个js文件的,是直接写在three.js里。
并且使用69版本的three.js时,不须要更新线条的链接方式也能够实现实时更新。可是103版本试了不少次,都不行。
另外,使用的OrbitControls.js和dat.gui.min.js最好都是和本身用的Three.js版本要一致,不然可能会报错。有一些教程的示例程序版本可能就比较旧了,若是直接拿来用可能会出问题,注意分辨一下。app