经过上一篇文章个人three.js学习记录(一)基本上是入门了three.js,可是这不够3D,此次我但愿能把以前作的demo弄出来,而后经过例子来分析操做步骤。javascript
上图是以前作的一个demo,有点丑,但愿不要介意。
这个主要是外面一层包裹着天空盒, 而后里面是一个由开顶的立方体作成的房子(暂且理解为房子)以及里面的家具构成,其中包括能够播放视频的电视,一个能够照的镜子,导入的沙发模型等html
首先,咱们须要上一篇文章的基础,这里再也不赘述,咱们直接进入主题,首先须要初始化咱们所须要的环境,代码以下:java
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>day0613</title> <style> body { background-color: #000; color: #fff; margin: 0; overflow: hidden; } </style> <script src="js/three.js"></script> <script src="js/stats.min.js"></script> <script src="js/dat.gui.js"></script> <script src="js/Detector.js"></script> <script src="js/DDSLoader.js"></script> <script src="js/OBJLoader.js"></script> <script src="js/MTLLoader.js"></script> <script src="js/OrbitControls.js"></script> <script src="js/day0613.js"></script> <script src="js/Mirror.js"></script> </head> <body> <!--用于加载视频资源--> <video id="video" src="video/sintel.ogv" style="display: none; left: 15px; top: 75px;"></video> <div id="webgl" style="position: absolute; left: 0; top: 0;"></div> <script type="text/javascript"> threeStart(); </script> </body> </html>
咱们须要加入<video>
加载咱们的视频,这是在咱们的模型中播放的纹理资源,而后div #webgl
是用于嵌入WebGL的渲染器的,接下来调用threeStart()
这个函数来进入咱们3D的世界git
var WALL_POSITION_Y = 150; var WALL_HEIGHT = 355; var WALL_WIDTH = 10; var WALL_LENGTH = 800; var TV_WIDTH = WALL_LENGTH / 4; var TV_HEIGHT = WALL_HEIGHT / 3; var TV_THICKNESS = 5; var SKYBOX_HEIGHT = 0; var MIRROR_WIDTH = 70; var MIRROR_HEIGHT = 150; var camera, scene, renderer, container; var material, controls, stats, video, texture, object; var verticalMirrorMesh; var mirror;
以上的变量我没有注释,若是不知道能够查下单词,记得我写的时候是查字典写的,翻译应该没错:-)github
在这里,由于涉及的东西不是可以一会儿就可以说出来,因此先来一个整体的概览可使得本身在内心有必定的印象,知道本身应该作些什么web
var threeStart = function () { //判断浏览器是否支持webGL if (!Detector.webgl) Detector.addGetWebGLMessage(); //窗口尺寸改变事件 window.addEventListener('resize', onWindowResize, false); /** * 窗口监听,用于改变窗口时可以实时保持窗口的比例 */ function onWindowResize() { //从新设置相机宽高比 camera.aspect = window.innerWidth / window.innerHeight; //更新相机的投影矩阵,这里没有理解什么意思,我把它理解为更新相机里面的各类参数 camera.updateProjectionMatrix(); //从新设置 renderer.setSize(window.innerWidth, window.innerHeight); } initRenderer(); initScene(); initCamera(); //加入灯光 initLight(); //这里是绘制TV、墙壁、镜子、地板 paint(); createSky(); //导入模型 initObj(); //回调重复渲染 arimate(); };
这里我前面的加入相机、渲染器、场景的部分先不说,咱们能够看到的是须要在原有个人three.js学习记录(一)基础上加入灯光、导入模型、绘制包含纹理的形状、渲染等数组
/** * 初始化灯光 */ function initLight() { //环境光,没有方向,任意方向的发射光线 var ambient = new THREE.AmbientLight(); //环境光强度 ambient.intensity = .8; //场景加入环境光 scene.add(ambient); //方向光,颜色十六进制表示 0xffeedd var directionalLight = new THREE.DirectionalLight(0xffeedd); //设置方向光的来源点 directionalLight.position.set(0, 10, 10).normalize(); scene.add(directionalLight); }
这里就是当咱们改变了视角以后这是一个连贯的动画,须要咱们的计算机重复的渲染场景才行,因此须要一个函数去重复的调用才行浏览器
/** * 回调函数,重画整个场景 */ function arimate() { if (video.readyState === video.HAVE_ENOUGH_DATA) { //设置咱们的纹理须要更新 这里的纹理就是视频纹理 //其实更新的就是将视频播放的画面从新截取到纹理,一帧一帧咱们看起来就是动画 if (texture) texture.needsUpdate = true; video.play(); } //镜子也须要渲染,它至关于摄像机从这个3d世界看到的渲染到平面(镜子) mirror.renderWithMirror(new THREE.Mirror(renderer, camera)); //渲染 renderer.render(scene, camera); //fps状态更新 stats.update(); //从新调用arimate requestAnimationFrame(arimate); }
我先不按照threeStart()
函数的调用顺序展开说明,这里我先来一个简单的建立天空盒createSky()
app
/** * 建立天空盒 */ function createSky() { //这部分是给出图片的位置及图片名 var imagePrefix = "img/sky/dawnmountain-"; var directions = ["xpos", "xneg", "ypos", "yneg", "zpos", "zneg"]; var imageSuffix = ".png"; //建立一个立方体而且设置大小 var skyGeometry = new THREE.CubeGeometry( 5000, 5000, 5000 ); //这里是用于天空盒六个面储存多个材质的数组 var materialArray = []; //循环将图片加载出来变成纹理以后将这个物体加入数组中 for (var i = 0; i < 6; i++) materialArray.push( new THREE.MeshBasicMaterial({ //这里imagePrefix + directions[i] + imageSuffix 就是将图片路径弄出来 map: THREE.ImageUtils.loadTexture( imagePrefix + directions[i] + imageSuffix ), side: THREE.BackSide //由于这里咱们的场景是在天空盒的里面,因此这里设置为背面应用该材质 })); //MultiMaterial能够将MeshBasicMaterial多个加载而后直接经过Mesh生成物体 var skyMaterial = new THREE.MultiMaterial( materialArray ); //加入形状skyGeometry和材质MultiMaterial var sky = new THREE.Mesh( skyGeometry, skyMaterial ); //设置天空盒的高度 sky.position.y = SKYBOX_HEIGHT; //场景当中加入天空盒 scene.add( sky ); }
虽然上面实现的东西不复杂,可是以上短短的代码须要理解确实不是很是容易,这里有涉及到了图片纹理的问题(有图形学的基础会好点吧,可是我上课的忘了差很少),可能我并不能真正意义的去理解,我如今只是须要快速的入门,可以简单的使用three.js就好了less
接下来咱们来绘制咱们的‘房子’,整体的代码以下:
/** * 绘制场景 */ function paint() { paintFloor(); paintMirror(); paintWall(); paintTV(); }
function paintFloor() { var loader = new THREE.TextureLoader; loader.load('img/floor.jpg', function (texture) { texture.wrapS = texture.wrapT = THREE.RepeatWrapping; //这里设置x和y超过了图片的像素以后进行的是重复绘制图片操做 texture.repeat = new THREE.Vector2(5, 5); //设置图片重复绘制的密度这里是5*5 //设置材质是双面应用该图片材质 var floorMaterial = new THREE.MeshBasicMaterial({map: texture, side: THREE.DoubleSide}); //地板使用PlaneGeometry生成平面 var floorGeometry = new THREE.PlaneGeometry(WALL_LENGTH, WALL_LENGTH); //生成地板的模型 var floor = new THREE.Mesh(floorGeometry, floorMaterial); //设置地板的位置 floor.position.y = -27; floor.rotation.x = Math.PI / 2; scene.add(floor);//场景加载该地板 }); }
咱们这个镜子是由两个部分组成的,一个是后面的一个盒子,一个就是镜面,盒子是为了看起来凸显一点,若是没有镜面镜子将会是这样的
加入镜片以后效果就不同了
如下是代码的实现
function paintMirror() { //这里是背面的盒子 var Geometry = new THREE.BoxGeometry(MIRROR_WIDTH + 3, MIRROR_HEIGHT + 3, 5); var Material = new THREE.MeshBasicMaterial({color:0x000, side: THREE.DoubleSide}); var Mesh = new THREE.Mesh(Geometry, Material); //肯定位置 Mesh.position.y = 50; Mesh.position.z = -220; Mesh.position.x = -395; Mesh.rotateY(Math.PI / 2); scene.add(Mesh); //three.js有一个Mirror.js用于生成镜子的,这是我在官方的示例看的 //这里有些代码不是很理解,都是直接使用官方给出的,不过按照个人理解 //主要是给它一个渲染器和照相机,它将3d世界看到的从新渲染一遍,可是须要不断渲染,因此在回调函数中须要更新 mirror = new THREE.Mirror( renderer, camera); verticalMirrorMesh = new THREE.Mesh( new THREE.PlaneBufferGeometry(MIRROR_WIDTH, MIRROR_HEIGHT), mirror.material ); verticalMirrorMesh.add( mirror ); }
function paintWall() { //图片加载器,加载成纹理 var loader = new THREE.TextureLoader; var wallOutside = loader.load('img/wall-outside.jpg'); var wallInside = loader.load('img/wall-inside.jpg'); //设置纹理的过滤方式 wallInside.wrapT = wallInside.wrapS = THREE.RepeatWrapping; wallInside.repeat = new THREE.Vector2(5, 5); //材质 var materials = []; materials.push(new THREE.MeshBasicMaterial()); //默认的材质,没有纹理 materials.push(new THREE.MeshBasicMaterial()); materials.push(new THREE.MeshBasicMaterial()); materials.push(new THREE.MeshBasicMaterial()); //这里设置了两种不一样图片生成的纹理,墙外的和墙内的 materials.push(new THREE.MeshBasicMaterial({map: wallOutside, side: THREE.DoubleSide})); materials.push(new THREE.MeshBasicMaterial({map: wallInside, side: THREE.DoubleSide})); //这6个基础材质的数组做为参数传递给MeshFaceMaterial var faceMaterial = new THREE.MultiMaterial(materials); //建立其中一面墙,而后其余的由此面墙生成 var Geometry = new THREE.BoxGeometry(WALL_LENGTH, WALL_HEIGHT, WALL_WIDTH); //设置墙的位置参数 var wallFront = new THREE.Mesh(Geometry, faceMaterial); wallFront.position.y = WALL_POSITION_Y; //经过clone函数能够获得一个全新的面,而后经过位置的改变做为其余墙面 var wallLeft = wallFront.clone(); wallLeft.rotation.y = 3 * Math.PI / 2; wallLeft.position.x = -WALL_LENGTH / 2; wallLeft.width = WALL_LENGTH + 100; var wallRight = wallFront.clone(); wallRight.rotation.y = Math.PI / 2; wallRight.position.x = WALL_LENGTH / 2; var wallBack = wallFront.clone(); wallBack.rotation.y = Math.PI; wallBack.position.z = -WALL_LENGTH / 2; wallFront.position.z = WALL_LENGTH / 2; scene.add(wallFront); scene.add(wallLeft); scene.add(wallRight); scene.add(wallBack); }
这里跟镜子的实现是同样的,背面一个盒子(加了纹理),而后加一个面用于贴入视频纹理的,代码以下:
function paintTV() { //获取咱们的视频的元素 video = document.getElementById('video'); //将咱们的视频加载为纹理(播放时的每一帧均可以将它看做图片) texture = new THREE.Texture(video); //这里是屏幕 var tvScreenGeometry = new THREE.PlaneGeometry(TV_WIDTH - 2, TV_HEIGHT - 4); //将视频纹理加入 var tvScreenMaterial = new THREE.MeshBasicMaterial({map: texture, side: THREE.DoubleSide}); var tvScreen = new THREE.Mesh(tvScreenGeometry, tvScreenMaterial); tvScreen.position.y = WALL_HEIGHT / 3 + 1; tvScreen.position.z = -WALL_LENGTH / 2 + 8; //这里是屏幕后面的盒子 var loader = new THREE.TextureLoader(); var tvGeometry = new THREE.BoxGeometry(TV_WIDTH, TV_HEIGHT, TV_THICKNESS); var tvMaterial = new THREE.MeshBasicMaterial({map: loader.load('img/tv.jpg')}); var tv = new THREE.Mesh(tvGeometry, tvMaterial); tv.position.y = WALL_HEIGHT / 3; tv.position.z = -WALL_LENGTH / 2 + TV_THICKNESS; scene.add(tvScreen); scene.add(tv); }
/** * 初始化模型 */ function initObj() { //这里咱们是用max导出的obj模型包含材质 //这里是直接官网的例子 // THREE.Loader.Handlers.add(/\.dds$/i, new THREE.DDSLoader()); //材质加载器 var mtlLoader = new THREE.MTLLoader(); //设置路径 mtlLoader.setPath('./model/'); //导入材质 mtlLoader.load('room.mtl', function (materials) { //材质导入调用这个回调函数(钩子函数) // materials.preload(); //obj模型加载器 var objLoader = new THREE.OBJLoader(); //设置将传入材质参数 objLoader.setMaterials(materials); //设置路径 objLoader.setPath('./model/'); //导入模型时能够加入执行信息和错误信息的函数,这里我没有加入 //onProgress, onError //导入模型 objLoader.load('room.obj', function (object) { //将导入的模型旋转到合适的位置 object.rotation.y = Math.PI; scene.add(object); }); }); }
这里除了以上的东西还有其余的小组件,如fps监听器(我截屏时左下角的东西),还有咱们的控制器,控制器其实就是经过改变咱们的模型位置或者是照相机位置达到的移动效果等等这些里面所用到的我没有在这里拿出来,若是须要的能够将代码拿回去研究
由于我以前是须要快速入门three.js,因此没有好好的系统地学习,因此我以为我并无掌握好这项技能,并且在上课(图形学,OpenGL)也没有利用好机会努力地学,图形学基础有点差,因此我若是要接触这类相关的仍是须要努力将图形学弄好,而且理解three.js的东西
基础部分推荐
http://www.hewebgl.com/article/articledir/1
https://www.zhihu.com/question/36367846?from=profile_question_card
http://blog.csdn.net/doupi520/article/category/6645411
除了以上还有上篇个人three.js学习记录(一)推荐的
以上的代码已经上传Github