第四章 使用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这个领域,做一些普及工作,让这个领域在中国能向前迈进一大步。