使用three.js加载3dmax资源,以及实现场景中的阴影效果

时间:2022-05-12 06:09:03

       使用three.js可以方便的让我们在网页中做出各种不同的3D效果。如果希望2D绘图内容,建议使用canvas来进行。但很多小伙伴不清楚到底如何为我们绘制和导入的图形添加阴影效果,更是不清楚到底如何导入我们已经制作好的3dmax资源。所以这篇教程将简要介绍如何将我们用3dmax制作好的资源导入进来,以及如何为我们导入的资源,包括所有自己绘制的图形添加阴影。也有很多小伙伴表示根本记不住这些八股文一般的代码。其实,每次需要编写代码的时候参考官方案例即可,不必背诵代码。如果编的多,那自然就记住了。如果编的少,我们也没有必要付出大把时间背诵这些我们很少使用的代码。

       首先,先介绍如何导入3dmax的资源。这里注意,经过我自己的测试,如果直接从本地打开文件的方式打开编写的网页,谷歌、IE等浏览器将无法显示我们自己加载的资源,原因是由于本地打开文件后是file协议,所以浏览器会因为安全性问题阻止我们加载本地资源。而火狐浏览器却可以正常打开。所以建议大家调试时使用火狐浏览器,或者使用tomcat、apache等先建立一个本地服务器,通过域名来访问自己编写的网页。不推荐修改浏览器的安全性设置。

       我们先用3dmax制作一个图形,这里选择其自带的茶壶。用3dmax制作茶壶的教程网上实在太多,所以这里不再赘述,请不会的小伙伴搜索教程即可,几步即可搞定。 使用three.js加载3dmax资源,以及实现场景中的阴影效果       当然,制作好了之后不要忘记导出。我们需要将其导出成为一个mtl文件和一个obj文件。这一步操作大多制作茶壶的教程也都有,同样是点点鼠标就行。至于材质等,我们这里不多考虑,毕竟学习要从简单开始。

使用three.js加载3dmax资源,以及实现场景中的阴影效果       导出如上图的两个文件之后,我们就可以参考官方的代码导入我们自己的素材了。

       首先,我们除了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,会出现什么情况呢?

使用three.js加载3dmax资源,以及实现场景中的阴影效果

并没有生成阴影。那如果我们设置为true,会是什么样呢?

使用three.js加载3dmax资源,以及实现场景中的阴影效果

       可以看到,已经生成了阴影。所以,如果我们要让一个物体可以产生阴影,需要设置castShadow这个属性为true,而生成了阴影,总需要投射到某个物体上,才能被观察到。所以,接收投影需要将receiveShadow这个属性设置为true。

       完整的效果如下

使用three.js加载3dmax资源,以及实现场景中的阴影效果       以下是完整代码。其中库文件以及3dmax的素材文件这里不提供,需要自己生成或者自己下载。也可以只学习阴影的生成方法。代码编写略仓促,不过除了各种事件的控制等,其他方面应该还是比较清晰的。欢迎批评之争。

使用three.js加载3dmax资源,以及实现场景中的阴影效果使用three.js加载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>
完整代码