使用three.js加載3dmax資源,以及實現場景中的陰影效果


       使用three.js可以方便的讓我們在網頁中做出各種不同的3D效果。如果希望2D繪圖內容,建議使用canvas來進行。但很多小伙伴不清楚到底如何為我們繪制和導入的圖形添加陰影效果,更是不清楚到底如何導入我們已經制作好的3dmax資源。所以這篇教程將簡要介紹如何將我們用3dmax制作好的資源導入進來,以及如何為我們導入的資源,包括所有自己繪制的圖形添加陰影。也有很多小伙伴表示根本記不住這些八股文一般的代碼。其實,每次需要編寫代碼的時候參考官方案例即可,不必背誦代碼。如果編的多,那自然就記住了。如果編的少,我們也沒有必要付出大把時間背誦這些我們很少使用的代碼。

       首先,先介紹如何導入3dmax的資源。這里注意,經過我自己的測試,如果直接從本地打開文件的方式打開編寫的網頁,谷歌、IE等瀏覽器將無法顯示我們自己加載的資源,原因是由於本地打開文件后是file協議,所以瀏覽器會因為安全性問題阻止我們加載本地資源。而火狐瀏覽器卻可以正常打開。所以建議大家調試時使用火狐瀏覽器,或者使用tomcat、apache等先建立一個本地服務器,通過域名來訪問自己編寫的網頁。不推薦修改瀏覽器的安全性設置。

       我們先用3dmax制作一個圖形,這里選擇其自帶的茶壺。用3dmax制作茶壺的教程網上實在太多,所以這里不再贅述,請不會的小伙伴搜索教程即可,幾步即可搞定。        當然,制作好了之后不要忘記導出。我們需要將其導出成為一個mtl文件和一個obj文件。這一步操作大多制作茶壺的教程也都有,同樣是點點鼠標就行。至於材質等,我們這里不多考慮,畢竟學習要從簡單開始。

       導出如上圖的兩個文件之后,我們就可以參考官方的代碼導入我們自己的素材了。

       首先,我們除了three.js文件之外,還需要引入個三源文件。一個是OBJLoader.js,一個是MTLLoader.js,一個是DDSLoader.js。這些是官方提供的加載我們本地資源的庫文件,可以從官網下載。https://github.com/mrdoob/three.js/blob/master/examples/webgl_loader_obj_mtl.html    這個網址既是官方案例。我們需要的文件也可以在這里下載到。

       以下代碼便是將素材導入的代碼,我們除了像官方那樣導入文件之外,還加入了陰影效果。

 1 var onError = function ( xhr ) { };
 2                 THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
 3                 var mtlLoader = new THREE.MTLLoader();
 4                 mtlLoader.setPath( './' );       //設置我們需要加載的mtl文件路徑
 5                 mtlLoader.load( 'lyn.mtl', function( material ) {      //這里加載我們需要的文件名  
 6                     material.preload();
 7                     var objLoader = new THREE.OBJLoader();
 8                     objLoader.setMaterials( material );      //材質,也可自定義
 9                     objLoader.setPath( './' );               //設置要加載的obj文件的路徑
10                     objLoader.load( 'lyn.obj', function ( object ) {           //加載obj文件
11                         object.position.z = 1;         //這里設置我們的素材相對於原來的大小以及旋轉縮放等
12                         object.position.y = -0.5;
13                         object.scale.x = 0.2;
14                         object.scale.y = 0.2;
15                         object.scale.z = 0.2;
16                         object1 = object;               //這里是對素材設置陰影的操作
17                         for(var k in object.children){  //由於我們的素材並不是看上去的一個整體,所以需要進行迭代
18                                                         //對其中的所有孩子都設置接收陰影以及投射陰影
19                                                         //才能看到陰影效果
20                             object.children[k].castShadow = true;   //設置該對象可以產生陰影
21                             object.children[k].receiveShadow = true;  //設置該對象可以接收陰影
22                         }
23                         scene.add( object1 );
24                         
25                     }, onProgress, onError );
26                 });

       上述的代碼除了設置陰影以及調整大小之外,都是八股文,需要的時候復制粘貼即可,如果經常從事這方面開發,才建議檢查源代碼的實現。有時我們會發現,即便導入后,我們也無法看到素材。我們需要考慮以下幾方面問題。第一方面,我們是否將我們的3dmax素材做的太大或者太小。太大的話,我們只能看到素材的一部分,造成一種看不到的假象。太小,又會看不清楚或者無法顯示。這種問題就需要各位根據我們攝像機的視距等來調整了。還有一種問題,就是由於我們沒有為我們的素材設置材質,而且我們的代碼中沒有添加光源,導致只顯示黑漆漆的一片。所以,如果要看到這個素材,我們還需要添加光照。

      以下是添加聚光燈光源的代碼,因為聚光燈光源可以聚焦,我們演示會方便一些。小伙伴們也可以自己動手嘗試其他光源。記住,我們需要的是點光源或者平行光等光源。環境光是無法生成陰影的。但如果希望周圍顯示的更加清楚,我們也可以同時添加點光源和環境光,只是環境光的光強需要弱一些,避免環境光過強影響陰影的正常顯示。

function SpotLight(){
                light = new THREE.SpotLight( '#ffffff' ,1);
                light.castShadow = true;
                light.distance = 50; 
                light.angle = 0.6; 
                light.decay = 2;
                light.penumbra = 0.2;          
                light.position.set( 3, 2, 1 );
                light.shadow.camera.near = 1;
                light.shadow.camera.far = 3;
                light.shadow.camera.visible = true;            
                light.shadow.mapSize.width = 1024;
                light.shadow.mapSize.height = 1024;                                    
                light.target = sp;
                scene.add(light);
            }

     我們還需要一個地板,將陰影投射到我們的地板上,這樣才能看到陰影。而之前我們講到過receiveShadow這個屬性。假設我們創建了一個添加了材質的圖形sp。我們需要使用sp.receiveShadow=true來讓其可以接收陰影。如果設置為false,會出現什么情況呢?

並沒有生成陰影。那如果我們設置為true,會是什么樣呢?

       可以看到,已經生成了陰影。所以,如果我們要讓一個物體可以產生陰影,需要設置castShadow這個屬性為true,而生成了陰影,總需要投射到某個物體上,才能被觀察到。所以,接收投影需要將receiveShadow這個屬性設置為true。

       完整的效果如下

       以下是完整代碼。其中庫文件以及3dmax的素材文件這里不提供,需要自己生成或者自己下載。也可以只學習陰影的生成方法。代碼編寫略倉促,不過除了各種事件的控制等,其他方面應該還是比較清晰的。歡迎批評之爭。

  1 <!DOCTYPE html>
  2 <html>
  3     <head>
  4         <style>
  5             html,
  6             body {
  7                 width: 100%;
  8                 height: 100%;
  9             }
 10             
 11             body {
 12                 margin: 0;
 13             }
 14             
 15             canvas {
 16                 width: 100%;
 17                 height: 100%
 18             }
 19         </style>
 20     </head>
 21     <body>
 22 
 23         <script src="js/three.min.js"></script>        
 24         <script src="js/jquery-1.12.4.js"></script>
 25         <script src="js/OBJLoader.js"></script>
 26         <script src="js/MTLLoader.js"></script> 
 27         <script src="js/DDSLoader.js"></script>       
 28         <script>
 29             var scene = new THREE.Scene();
 30             var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000);
 31             camera.position.z = 6;
 32             camera.position.y = 1;
 33             camera.position.x = 2;
 34             camera.lookAt(new THREE.Vector3(0, 0, 0)); 
 35 
 36             var other = new THREE.Object3D();
 37             other.add(camera);           
 38             scene.add(other);
 39 
 40             var renderer = new THREE.WebGLRenderer();
 41             renderer.setSize(window.innerWidth, window.innerHeight);
 42             document.body.appendChild(renderer.domElement);
 43 
 44             var geometry = new THREE.BoxGeometry(1,1,1);
 45             var material = new THREE.MeshPhongMaterial({
 46                 color : '#2194ce',
 47                 specular : '#111111',
 48                 specular : 10                                               
 49             });
 50             var sp = new THREE.Mesh(geometry,material);
 51             sp.position.z = -0.5;
 52 
 53             var geometry = new THREE.ConeGeometry( 0.5, 1, 6 );
 54             var material2 = new THREE.MeshPhongMaterial({
 55                 color : '#2194ce',
 56                 specular : '#ffffff',
 57                 shininess : 100               
 58             });
 59             var sp2 = new THREE.Mesh(geometry,material2);
 60             sp2.position.x = -2.5;
 61             sp2.position.z = -1;   
 62 
 63             var ball = new THREE.SphereGeometry( 0.5, 32, 32 ); 
 64             var material3 = new THREE.MeshPhongMaterial({
 65                 color : '#2194ce',
 66                 specular : '#111111',
 67                 shininess : 100               
 68             });      
 69             var myBall = new THREE.Mesh(ball,material3);
 70             myBall.position.z = 1;
 71             myBall.position.x = -1;
 72             myBall.position.y = -1;
 73             myBall.castShadow = true;
 74             myBall.receiveShadow = true;   
 75 
 76             var light2 = new THREE.SpotLight( '#ffffff' ,1);
 77             light2.castShadow = true;
 78             light2.distance = 50; 
 79             light2.angle = 0.3; 
 80             light2.decay = 2;
 81             light2.penumbra = 0.2;          
 82             light2.position.set( -2, 5, -2 );
 83             light2.shadow.camera.near = 1;
 84             light2.shadow.camera.far = 3;
 85             light2.shadow.camera.visible = true;            
 86             light2.shadow.mapSize.width = 1024;
 87             light2.shadow.mapSize.height = 1024;                                    
 88             light2.target = sp;
 89             scene.add(light2);
 90             lightHelper2 = new THREE.SpotLightHelper(light2);
 91             scene.add(lightHelper2);
 92 
 93             renderer.shadowMap.enabled = true;
 94             
 95             var matFloor = new THREE.MeshPhongMaterial( { color:0x808080 } );            
 96             var geoFloor = new THREE.BoxGeometry( 200, 0.1, 200 );
 97             var mshFloor = new THREE.Mesh( geoFloor, matFloor );
 98             var ambient = new THREE.AmbientLight( 0x111111);    
 99             var lightHelper;        
100 
101             var light;
102             SpotLight();
103             lightHelper = new THREE.SpotLightHelper( light );
104 
105             sp.castShadow = true;
106             sp.receiveShadow = true;
107             sp2.castShadow = true;
108             sp2.receiveShadow = true;
109             mshFloor.castShadow = true;
110             mshFloor.receiveShadow = true;
111             mshFloor.position.set( 0, -2, 0 );
112             
113 
114             scene.add( mshFloor );
115             scene.add(sp);
116             scene.add(sp2);
117             scene.add(myBall);
118             scene.add( light );
119             scene.add(ambient);
120             scene.add(lightHelper);            
121            // 0.9854        
122             
123             //聚光燈光源
124            function SpotLight(){
125                 light = new THREE.SpotLight( '#ffffff' ,1);
126                 light.castShadow = true;
127                 light.distance = 50; 
128                 light.angle = 0.6; 
129                 light.decay = 2;
130                 light.penumbra = 0.2;          
131                 light.position.set( 3, 2, 1 );
132                 light.shadow.camera.near = 1;
133                 light.shadow.camera.far = 3;
134                 light.shadow.camera.visible = true;            
135                 light.shadow.mapSize.width = 1024;
136                 light.shadow.mapSize.height = 1024;                                    
137                 light.target = sp;
138                 scene.add(light);
139             }
140 
141             //點光源
142             function PointLight(){
143                 light = new THREE.PointLight('#ffffff',1,50,2);
144                     light.castShadow = true;
145                     light.position.set( 3, 2, 1 );           
146                     light.shadow.mapSize.width = 1024;
147                     light.shadow.mapSize.height = 1024;  
148                     scene.add(light);                    
149             }
150 
151             //平行光
152             function DirectLight(){
153                 light = new THREE.DirectionalLight('#ffffff',1);
154                     light.castShadow = true;
155                     light.position.set( 3, 2, 1 ); 
156                     light.decay = 2;
157                     light.penumbra = 0.2;          
158                     light.shadow.mapSize.width = 1024;
159                     light.shadow.mapSize.height = 1024;  
160                     scene.add(light); 
161             }
162 
163             var onProgress = function ( xhr ) {
164                     if ( xhr.lengthComputable ) {
165                         var percentComplete = xhr.loaded / xhr.total * 100;
166                         console.log( Math.round(percentComplete, 2) + '% downloaded' );
167                     }
168                 };
169 
170                 var onError = function ( xhr ) { };
171                 THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
172                 var mtlLoader = new THREE.MTLLoader();
173                 mtlLoader.setPath( './' );       //設置我們需要加載的mtl文件路徑
174                 mtlLoader.load( 'lyn.mtl', function( material ) {      //這里加載我們需要的文件名  
175                     material.preload();
176                     var objLoader = new THREE.OBJLoader();
177                     objLoader.setMaterials( material );      //材質,也可自定義
178                     objLoader.setPath( './' );               //設置要加載的obj文件的路徑
179                     objLoader.load( 'lyn.obj', function ( object ) {           //加載obj文件
180                         object.position.z = 1;         //這里設置我們的素材相對於原來的大小以及旋轉縮放等
181                         object.position.y = -0.5;
182                         object.scale.x = 0.2;
183                         object.scale.y = 0.2;
184                         object.scale.z = 0.2;
185                         object1 = object;               //這里是對素材設置陰影的操作
186                         for(var k in object.children){  //由於我們的素材並不是看上去的一個整體,所以需要進行迭代
187                                                         //對其中的所有孩子都設置接收陰影以及投射陰影
188                                                         //才能看到陰影效果
189                             object.children[k].castShadow = true;   //設置該對象可以產生陰影
190                             object.children[k].receiveShadow = true;  //設置該對象可以接收陰影
191                         }
192                         scene.add( object1 );
193                         
194                     }, onProgress, onError );
195                 });
196 
197 
198             var render = function() {
199                 requestAnimationFrame(render);
200                 lightHelper.update();
201                                       
202                 other.rotation.y += 0.01;
203                 sp2.rotation.x += 0.01;
204 
205                 renderer.render(scene, camera);
206             }
207             
208             render();
209             
210             //設置場景不停旋轉
211             var tmp = 0;
212             var timer = setInterval(function(){
213                 if(tmp == 0){
214                     var route = (5 - light.position.y) / 50;
215                     light.position.y += route;
216                     if(route <= 0.001){
217                         tmp = 1;
218                     }
219                 }else{
220                     var route = (light.position.y - 1) / 50;
221                     light.position.y -= route;
222                     if(route <= 0.001){
223                         tmp = 0;
224                     }
225                 }
226             },15);
227 
228            //設置圖中的立方體可以旋轉
229             var left = false;
230             var right = false;
231             var boxLeft = false;
232             var boxRight = false;
233             var boxUp = false;
234             var boxDown = false;
235             var object1 = '';                        
236             setInterval(function(){                
237                 if(left){                                                            
238                     object1.rotation.y -= 0.02;                  
239                 }else if(right){                                        
240                     object1.rotation.y += 0.02;            
241                 }else if(boxLeft){
242                     sp.rotation.y -= 0.02;
243                 }else if(boxRight){
244                     sp.rotation.y += 0.02;
245                 }else if(boxUp){
246                     sp.rotation.x -= 0.02;
247                 }else if(boxDown){
248                     sp.rotation.x += 0.02;
249                 }
250             },25);
251 
252             document.onkeydown = function(ev){
253                 var ev = ev || event;
254                 if(ev.keyCode == 65)
255                     left = true;
256                 else if(ev.keyCode == 68)
257                     right = true;   
258                 else if(ev.keyCode == 37)
259                     boxLeft = true;
260                 else if(ev.keyCode == 38)
261                     boxUp = true;
262                 else if(ev.keyCode == 39)
263                     boxRight = true;
264                 else if(ev.keyCode == 40)
265                     boxDown = true; 
266                 else if(ev.keyCode == 80){
267                     scene.remove(light);
268                     PointLight();
269                 }else if(ev.keyCode == 83){
270                     scene.remove(light);
271                     SpotLight();
272                 }else if(ev.keyCode == 17){
273                     scene.remove(light);
274                     DirectLight();
275                 }else if(ev.keyCode == 90){
276                     if(light.intensity < 10)
277                         light.intensity += 1;
278                 }else if(ev.keyCode == 88){
279                     if(light.intensity > 0)
280                         light.intensity -= 1;
281                 }else if(ev.keyCode == 67){
282                     scene.remove(sp);
283                     geometry = new THREE.BoxGeometry(1,1,1);
284                     material = new THREE.MeshPhongMaterial({
285                         color : '#A44A32',
286                         specular : '#ffffff',
287                         specular : 100                                               
288                     });
289                     var sp = new THREE.Mesh(geometry,material);
290                     sp.position.z = -0.5;
291                     scene.add(sp);
292                 }else if(ev.keyCode == 86){
293                     scene.remove(sp);
294                     geometry = new THREE.BoxGeometry(1,1,1);
295                     material = new THREE.MeshPhongMaterial({
296                         color : '#2194ce',
297                         specular : '#111111',
298                         specular : 100                                               
299                     });
300                     var sp = new THREE.Mesh(geometry,material);
301                     sp.position.z = -0.5;
302                     scene.add(sp);
303                 }     
304             }
305 
306             document.onkeyup = function(ev){
307                 var ev = ev || event;
308                 if(ev.keyCode == 65)
309                     left = false;
310                 else if(ev.keyCode == 68)
311                     right = false;
312                 else if(ev.keyCode == 37)
313                     boxLeft = false;
314                 else if(ev.keyCode == 38)
315                     boxUp = false;
316                 else if(ev.keyCode == 39)
317                     boxRight = false;
318                 else if(ev.keyCode == 40)
319                     boxDown = false; 
320             }
321 
322 
323         </script>
324     </body>
325 </html>
完整代碼

 

     

 

       

 

      

     

      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM