WebGL three.js學習筆記 自定義頂點創建幾何體

自定義頂點創建幾何體與克隆

Three.js自己已經有不少的網格模型,基本已經夠咱們的使用,可是若是咱們仍是想本身根據頂點座標來創建幾何模型的話,Three.js也是能夠的。javascript

基本效果如圖:
css

自定義幾何體
自定義幾何體

點擊查看demo演示

demo演示:https://nsytsqdtn.github.io/demo/vertices/vertices

實際上出於性能的考慮,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            width100%;
17            height850px;
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            antialiastrue
38        });//定義渲染器
39        renderer.setSize(window.innerWidth, window.innerHeight);//設置渲染的寬度和高度
40        document.getElementById("canvas-frame").appendChild(renderer.domElement);//將渲染器加在html中的div裏面
41        renderer.setClearColor(0x3333331.0);//渲染的顏色設置
42        renderer.shadowMapEnabled = true;//開啓陰影,默認是關閉的,太影響性能
43        renderer.shadowMapType = THREE.PCFSoftShadowMap;//陰影的一個類型
44
45
46        camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight, 110000);//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(000);//攝像機對焦的位置
56        //這三個參數共同做用才能決定畫面
57
58        scene = new THREE.Scene();
59
60        let light = new THREE.SpotLight(0xffffff1.00);//點光源
61        light.position.set(-4060-10);
62        light.castShadow = true;//開啓陰影
63        light.shadowMapWidth = 8192;//陰影的分辨率,能夠不設置對比看效果
64        light.shadowMapHeight = 8192;
65        scene.add(light);
66        light = new THREE.AmbientLight(0xcccccc0.2);//環境光,若是不加,點光源照不到的地方就徹底是黑色的
67        scene.add(light);
68
69        cameraControl();
70
71         vertices = [
72            new THREE.Vector3(131),
73            new THREE.Vector3(13-1),
74            new THREE.Vector3(1-11),
75            new THREE.Vector3(1-1-1),
76            new THREE.Vector3(-13-1),
77            new THREE.Vector3(-131),
78            new THREE.Vector3(-1-1-1),
79            new THREE.Vector3(-1-11)
80        ];//頂點座標,一共8個頂點
81
82         faces = [
83             new THREE.Face3(021),
84             new THREE.Face3(231),
85             new THREE.Face3(465),
86             new THREE.Face3(675),
87             new THREE.Face3(451),
88             new THREE.Face3(501),
89             new THREE.Face3(762),
90             new THREE.Face3(632),
91             new THREE.Face3(570),
92             new THREE.Face3(720),
93             new THREE.Face3(134),
94             new THREE.Face3(364),
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({opacity0.6color0x44ff44transparenttrue}),//透明度更改
105            new THREE.MeshBasicMaterial({color0x000000wireframetrue})//線條材質,讓觀察更直觀一點
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(353));
128        controlPoints.push(addControl(350));
129        controlPoints.push(addControl(303));
130        controlPoints.push(addControl(300));
131        controlPoints.push(addControl(050));
132        controlPoints.push(addControl(053));
133        controlPoints.push(addControl(000));
134        controlPoints.push(addControl(003));
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({opacity0.6color0xff44fftransparenttrue}),
143                    new THREE.MeshBasicMaterial({color0x000000wireframetrue})
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'-1010);
170            f1.add(controlPoints[i], 'y'-1010);
171            f1.add(controlPoints[i], 'z'-1010);
172
173        }
174    }
175
176   // 攝像機的控制,能夠採用鼠標拖動來控制視野
177    function cameraControl({
178        controller = new THREE.OrbitControls(camera, renderer.domElement);
179        controller.target = new THREE.Vector3(000);
180    }
181
182    let plane;
183
184    //初始化物體
185    function initObject({
186        //定義了一個地面
187        let planeGeometry = new THREE.PlaneGeometry(10010011);
188        let planeMaterial = new THREE.MeshLambertMaterial({
189            color0xffffff,
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

相關文章
相關標籤/搜索