《月熊志》技术解析——可爱的3D月熊
翻开月熊志的第一本书,首先映入眼帘的就是一只可爱的月熊——Jasper。它是贯穿月熊志的主角,在这页中可以看到月熊及场景是3D的并且是无插件的,那么它是如何被活灵活现的搬到网页中的呢?这篇文章来解答这个疑惑。http://static.cdn.pcbeta.com/data/attachment/album/201312/26/142442o9d0dncc70757c1a.png 在这个页面,为了更好的把月熊的生长环境和氛围更好的渲染出来,我们通过WebGL把Jasper搬到浏览器上,让大家可以更好的了解它。WebGL是一种3D绘图标准,允许把JS和OpenGL ES 2.0结合在一起,无需插件并且可以利用GPU来进行图形渲染,在网页上流畅的展示3D场景和模型,接下来我们就来了解这些是如何实现的:导出模型
http://static.cdn.pcbeta.com/data/attachment/album/201312/26/1424434kevqqzcv4q5kqz6.png 首先当然是在3D建模软件中制作模型,这部分不多介绍,以Blender为例,在输出时选择Three.js并勾选Morph animation(变形动画)即可导出带动画的JS文件。http://static.cdn.pcbeta.com/data/attachment/album/201312/26/142443kz4cccdex0wxh0c1.png 选择Export >Three.jshttp://static.cdn.pcbeta.com/data/attachment/album/201312/26/142444hx21g9l9hgml5xwg.png勾选Morphanimation建立场景
在Three.js的3D世界,我们至少需要创建以下对象:
1. 一个场景(scene)
2. 一个渲染器(renderer)
3. 一个摄像机(camera) //创建场景var scene = new THREE.Scene();//创建摄像机var camera= new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1,10000);//创建渲染器,并设置透明背景和抗锯齿为trueVarrenderer = new THREE.WebGLRenderer({ alpha: true, antialias: true});//设置视口和窗口大小一致renderer.setSize(window.innerWidth,window.innerHeight);导入地面我们使用的是.js格式的模型文件,所以要用Three.js的JSONLoader对象。 var loader = new THREE.JSONLoader(); JSONLoader上的load方法类似jQuery的get方法,第一个参数传入模型文件的地址,第二个参数是回调函数,回调函数同样有2个参数,第一个是模型(geometry),第二个是材质(materials)。 loader.load("models/ground.js",function (geometry, materials) { //创建地面模型 varground = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials)); //缩放到合适大小 ground.scale.x= ground.scale.y = ground.scale.z = 50; //旋转模型角度 ground.rotation.y= Math.PI; //设置模型位置 ground.position.y= -150; ground.position.z= 200; //添加模型到地面 scene.add(ground); }); //添加 renderer 元素到文档中document.body.appendChild(renderer.domElement);//让摄像机对准场景camera.lookAt(scene.position);//渲染画面renderer.render(scene, camera); 这个时候运行页面,就能看到模型了,不过是全黑的,因为现在还没有打上灯光。http://static.cdn.pcbeta.com/data/attachment/album/201312/26/142444lv7ymlraa4mzcvnc.png 添加光源我们需要2个光源,一个半球光和一个平行光。 半球光可以用来模拟环境光,他是在场景上方的一个光源。半球光的构造函数HemisphereLight有两个参数:颜色和光线强度,这里分别设置为白色和 0.7。var hemisphereLight = newTHREE.HemisphereLight(0xffffff, 0xffffff, .7);scene.add(hemisphereLight); 平行光是一组具有方向的没有衰减的平行光线,可以用它来模拟太阳光:虽然遥远但打在物体上的光都来自同一个方向。 环境光的构造函数THREE.DirectionalLight也是两个参数——16进制的颜色值和光线的强度(默认为1)。 var directionalLight = newTHREE.DirectionalLight(0xffffff, .6);directionalLight.position.set(600, 1000,700);directionalLight.castShadow = true;directionalLight.shadowCameraNear = 50;directionalLight.shadowCameraFar = 3000;directionalLight.shadowCameraLeft = -1000;directionalLight.shadowCameraRight = 1000;directionalLight.shadowCameraTop = 1000;directionalLight.shadowCameraBottom =-1000;directionalLight.shadowDarkness = .2;//directionalLight.shadowCameraVisible =true;scene.add(directionalLight); 平行光有很多属性,可以调整光的范围、方向等,这里就不一一介绍。shadowCameraVisible是一个比较特殊的属性,设置为true后,你可以看到光的框架,方便调试。 打上合适的光源后,就能看到久违的地面了:http://static.cdn.pcbeta.com/data/attachment/album/201312/26/1424447qau180qzr79dqm7.png 模型默认的材质是平滑的,要显示为最终的棱角分明的风格需要设置下材质,注意新加的循环:loader.load("models/ground.js",function (geometry, materials) { for(var i = 0; i < materials.length; i++) { materials.shading= THREE.NoShading; }; varground = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials)); ground.scale.x= ground.scale.y = ground.scale.z = 50; ground.rotation.y= Math.PI; ground.position.y= -150; ground.position.z= 200; scene.add(ground); });http://static.cdn.pcbeta.com/data/attachment/album/201312/26/142445kogkr2qoavkav3go.png 导入月熊操作和导入地面一样,因为导出JS时选的是Morph animation,所以在创建模型是相应的应该选择MorphAnimMesh。 loader.load("models/bear.js",function (geometry, materials) { varmaterial = materials; material.shading= THREE.NoShading; bear= new THREE.MorphAnimMesh(geometry, new THREE.MeshFaceMaterial(materials)); bear.position.z= 200; bear.position.y= -150; bear.scale.x= bear.scale.y = bear.scale.z = 50; scene.add(bear); });
http://static.cdn.pcbeta.com/data/attachment/album/201312/26/142445gwxvugub8gatnvxt.png
但是这个时候熊还不会动,需要将材质的morphTargets设置为true,并且设置动画的播放时长等,最后循环渲染画面。 loader.load("models/bear.js", function(geometry, materials) { varmaterial = materials; material.morphTargets= true; material.shading= THREE.NoShading; bear= new THREE.MorphAnimMesh(geometry, new THREE.MeshFaceMaterial(materials)); bear.setFrameRange(0,290); //设置起始帧和结束帧 bear.duration= 290 * 24 / 1000; //设置动画播放时长 …} var clock = new THREE.Clock();//计时器,用于更新动画function animate() { requestAnimationFrame(animate); bear.updateAnimation(clock.getDelta()); render();}; function render() { camera.lookAt(scene.position); renderer.render(scene, camera);}; animate(); 设置投影Three.js的光源默认不会导致物体间的投影,打开投影需要执行以下几步: 打开渲染器的地图阴影: renderer.shadowMapEnabled = true;启用光线的投影:light.castShadow = true;把模型设置为生成投影:mesh.castShadow = true;把模型设置为接收阴影:mesh.receiveShadow= true; 跟随鼠标旋转摄像头简单的说就是根据鼠标的位置计算摄像机的x坐标和y坐标。 var mouseX = 0, mouseY = 0; function render() { camera.position.x += (mouseX - camera.position.x) * .05; camera.position.y += (-mouseY - camera.position.y) * .05; camera.lookAt(scene.position); renderer.render(scene, camera);}; function onDocumentMouseMove(event) { mouseX = (event.clientX - windowHalfX) * 2; mouseY = (event.clientY - windowHalfY) * 2;}; document.addEventListener("mousemove",onDocumentMouseMove, false);animate();http://static.cdn.pcbeta.com/data/attachment/album/201312/26/1424459vxzhqmsq67z9v99.png
超赞啊...{:5_266:} 技术科普 感谢…… 谢谢,看了,还是不懂。
页:
[1]