第四章 使用three.js加載以圖片為紋理的模型(下)
在上一章里,為了演示的方便,我們選擇了一個簡單的模型。但是如前所述,在實際的生產環境中,一方面我們的模型更為復雜,另一方面我們的貼圖也不是普通的照片,而是處理過的uv圖。uv圖就是xyz三維圖通過變換形成的二維圖,類似數學里面學的極坐標變換。UV圖的制作可以借用一些軟件工具完成,在blender里面也有UV圖編輯器。
這一節我們選用three.js官方例程中的一個加載頭部模型的示例。原示例有三百多行代碼,我把它精簡到一百行多一點,包括擴了核心的uv圖加載顯示部分。而更多的處理代碼則直接刪掉,雖然顯示效果因此差了一些,但精簡的代碼更適合說明和演示。
效果圖如下:
對應的代碼如下:
var container, loader; var camera, scene, renderer; var mesh, directionalLight; var mouseX = 0, mouseY = 0; var targetX = 0, targetY = 0; var windowHalfX = window.innerWidth / 2; var windowHalfY = window.innerHeight / 2; init(); animate(); function init() { container = document.createElement( 'div' ); document.body.appendChild( container ); // 添加攝像機 camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 10000 ); camera.position.z = 900; scene = new THREE.Scene(); // 添加光源 scene.add( new THREE.AmbientLight( 0x222222 )); directionalLight = new THREE.DirectionalLight( 0xffeedd, 1 ); directionalLight.position.set( 1, -1, 1 ).normalize(); scene.add( directionalLight ); // 添加材質,其中包括uv圖紋理 var ambient = 0x111111, diffuse = 0xbbbbbb, specular = 0x070707, shininess = 50; specular = 0x555555; var shader = THREE.ShaderSkin[ "skin" ]; var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); // normal紋理,diffuse紋理,涉及3D建模知識,目前還不了解具體意義 uniforms[ "tNormal" ].value = THREE.ImageUtils.loadTexture( "leeperrysmith/Infinite-Level_02_Tangent_SmoothUV.jpg" ); uniforms[ "uNormalScale" ].value = 0.75; uniforms[ "tDiffuse" ].value = THREE.ImageUtils.loadTexture( "leeperrysmith/Map-COL.jpg" ); uniforms[ "passID" ].value = 1; // 設置一些顏色值 uniforms[ "uDiffuseColor" ].value.setHex( diffuse ); uniforms[ "uSpecularColor" ].value.setHex( specular ); uniforms[ "uAmbientColor" ].value.setHex( ambient ); uniforms[ "uRoughness" ].value = 0.185; uniforms[ "uSpecularBrightness" ].value = 0.8; var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true }; material = new THREE.ShaderMaterial( parameters ); // JSON加載器,需要自己指定uv貼圖時一般都使用JSON加載器 loader = new THREE.JSONLoader( true ); document.body.appendChild( loader.statusDomElement ); // 回調中暴露加載的模型的形狀 loader.load("leeperrysmith/LeePerrySmith.js", function( geometry ) { geometry.computeTangents(); // 以加載的模型的geometry和代碼創建的材質對象組建mesh對象加載到場景中顯示 mesh = new THREE.Mesh( geometry, material ); mesh.position.y = - 50; // scale參數一般是經驗參數,自己調節獲得 mesh.scale.set( 100, 100, 100 ); scene.add( mesh ); }); // 渲染 renderer = new THREE.WebGLRenderer( { antialias: false } ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setClearColorHex( 0x050505, 1 ); renderer.autoClear = false; container.appendChild( renderer.domElement ); // 鼠標移動事件,用來移動頭部轉動 document.addEventListener( 'mousemove', function ( event ) { mouseX = ( event.clientX - windowHalfX ); mouseY = ( event.clientY - windowHalfY ); }, false ); } function animate() { requestAnimationFrame( animate ); targetX = mouseX * .001; targetY = mouseY * .001; // 通過mesh的rotation實現動畫,和之前旋轉攝像機實現動畫不同 if ( mesh ) { // 目前尚不清楚0.05的參數如何計算得到,或許也是經驗值,調節獲得? mesh.rotation.y += 0.05 * ( targetX - mesh.rotation.y ); mesh.rotation.x += 0.05 * ( targetY - mesh.rotation.x ); } renderer.clear(); renderer.render( scene, camera ); }
對應的html代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <title>three.js webgl - materials - skin [Lee Perry-Smith]</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <style> body { background:#000; } </style> </head> <body> <script src="js/three.js"></script> <script src="js/ShaderSkin.js"></script> .... </body> </html>
工程中用到了一些資源,最主要的就是模型的JSON文件:http://mrdoob.github.com/three.js/examples/obj/leeperrysmith/LeePerrySmith.js, 然后是ShaderSkin.js的一個THREE擴展:http://mrdoob.github.com/three.js/examples/js/ShaderSkin.js,另外還有兩個uv圖片:http://mrdoob.github.com/three.js/examples/obj/leeperrysmith/Infinite-Level_02_Tangent_SmoothUV.jpg 和 http://mrdoob.github.com/three.js/examples/obj/leeperrysmith/Map-COL.jpg
自己去試試吧!
另,吐槽一些個人感受,僅供參考。在探索Three.js的過程中,深感文檔的缺失,不僅中文文檔少到可憐,英文文檔也極少。對初學者來說這是很惱火的事情。在無望之際,只能反復研究它的示例代碼,然后做一點反向工程,通過不斷的試驗去確定某些函數或API的具體作用。雖然最后結果還不錯,但回頭想想,如果文檔中對API介紹細致,同時配合詳盡的示例文檔,我花掉這么多時間做的探索或許就可以濃縮成半天的文檔閱讀了,這樣能節省多少時間哪。這個現象本身也說明three.js還處在發展的初期,還有非常多的工作要做。所以對那些打算使用three.js做產品的同仁,希望能慎之又慎,否則很有可能陷入泥沼不能自拔。當然,對那些本身以具備豐富的3D建模知識的同仁來說,很多困難都不值一提,我非常希望這樣的牛人能關注three.js這個領域,做一些普及工作,讓這個領域在中國能向前邁進一大步。