使用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>
完整代码

 

     

 

       

 

      

     

      

posted @ 2017-07-12 09:31  faker_archer  阅读(17216)  评论(2编辑  收藏  举报