以three.js方便的讓我們在網頁上做出各種不同的3D效果。如果希望2D繪圖內容,建議使用canvas來進行。但許多小夥伴不清楚到底如何為我們繪製和導入的圖形添加陰影效果,更是不清楚到底如何導入我們已經製作好的3dmax資源。所以這篇教學將簡要介紹如何將我們用3dmax製作好的資源導入進來,以及如何為我們導入的資源,包括所有自己繪製的圖形添加陰影。也有很多小夥伴表示根本記不住這些八股文一般的代碼。其實,每次需要寫程式碼的時候參考官方案例即可,不必背誦程式碼。如果編的多,那自然就記住了。如果編的少,我們也沒有必要付出大把時間背誦這些我們很少使用的程式碼。
首先,先介紹如何導入3dmax的資源。這裡注意,經過我自己的測試,如果直接從本地打開文件的方式打開編寫的網頁,谷歌、IE等瀏覽器將無法顯示我們自己加載的資源,原因是由於本地打開文件後是file協議,所以瀏覽器會因為安全性問題而阻止我們載入本機資源。而火狐瀏覽器卻可以正常打開。所以建議大家在調試時使用火狐瀏覽器,或是使用tomcat、apache等先建立一個本地伺服器,透過網域來存取自己寫的網頁。不建議修改瀏覽器的安全性設定。
我們先用3dmax製作圖形,這裡選擇自備的茶壺。用3dmax製作茶壺的教學網路上實在太多,所以這裡不再贅述,請不會的小夥伴搜尋教學即可,幾步即可搞定。 當然,並在製作好了之後不要忘記匯出。我們需要將其匯出成為一個mtl檔案和一個obj檔案。這一步操作大多製作茶壺的教學也都有,同樣是點點滑鼠就行。至於材質等,我們這裡不多考慮,畢竟學習要從簡單開始。
以上圖的兩個檔案匯出之後,我們就可以參考官方的程式碼匯入我們自己的素材了。
首先,我們除了three.js檔案之外,還需要引入個三個檔案。一個是OBJLoader.js,一個是MTLLoader.js,一個是DDSLoader.js。這些是官方提供的加載我們本地資源的庫文件,可以從官網下載。 這個網址既是官方案例。我們需要的文件也可以在這裡下載到。
以下程式碼便是將素材導入的程式碼,除了像官方一樣導入檔案之外,還加入了陰影效果。
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?1.1.11"></script> 24 <script src="js/jquery-1.12.4.js?1.1.11"></script> 25 <script src="js/OBJLoader.js?1.1.11"></script> 26 <script src="js/MTLLoader.js?1.1.11"></script> 27 <script src="js/DDSLoader.js?1.1.11"></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>
以上是使用three.js做出的3D效果圖?的詳細內容。更多資訊請關注PHP中文網其他相關文章!