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