使用 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 2 3 4 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 20 21 22 23 24 25 26 27 28 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 324 325
来源: http://www.cnblogs.com/zhnblog/p/7153781.html