一個完整的3D環境包含以下元素:
1.場景(Scene):是物體、光源等元素的容器,
2.相機(Camera):控制視角的位置、范圍以及視覺焦點的位置,一個3D環境中只能存在一個相機
3.物體對象(Mesh):包括二維物體(點、線、面)、三維物體、粒子
4.光源(Light):包括全局光、平行光、點光源
5.渲染器(Renderer):指定渲染方式,如webGL\canvas2D\Css2D\Css3D等。
6.控制器(Control): 相機控件,可通過鍵盤、鼠標控制相機的移動
場景(Scene)
物體、光源、控制器的添加必須使用secen.add(object)添加到場景中才能渲染出來。
一個3D項目中可同時存在多個scene,通過切換render的scene來切換顯示場景
var scene = new THREE.Scene(); var mesh=scene.getObjectByName("sky");//獲取場景中name=sky的物體;
相機(Camera)
基本概念
相機根據投影方式分兩種:正交投影相機和透視投影相機
正交投影相機:THREE.OrthographicCamera(left, right, top, bottom, near, far),大小不因遠近而變化
透視投影相機:THREE.PerspectiveCamera(fov, aspect, near, far),遵循近大遠小的空間規則
一般情況下,我們使用的是透視投影相機,其參數為:
fov: 垂直方向夾角
aspect:可視區域長寬比 width/height
near:渲染區域離攝像機最近的距離
far:渲染區域離攝像機最遠的距離,3僅在距離攝像機near和far間的區域會被渲染到 canvas中
相機有兩個重要的參數:
camera.position:控制相機在整個3D環境中的位置(取值為3維坐標對象-THREE.Vector3(x,y,z))
camera.lookAt:控制相機的焦點位置,決定相機的朝向(取值為3維坐標對象-THREE.Vector3(x,y,z))
以下代碼創建一個垂直夾角為45度、渲染區域為距離鏡頭1px到1000000px的透視投影相機,設置其位置為x:500px,y:0,z:500px,鏡頭朝向空間坐標軸原點x:0,y:0,z:0
var camera = new THREE.PerspectiveCamera( 45, 1920 / 1000, 1, 1000000 ); camera.position.set(500, 0, 500); camera.lookAt(new THREE.Vector3(0,0,0));
主要應用
-
1.設置相機焦點位置為原點坐標或某物體的位置坐標:camera.lookAt(new THREE.Vector3(0,0,0)),循環改變camera.position的位置,可以實現圍繞物體旋轉 360度觀看物體的動畫。
-
2.同時循環設置camera.lookAt和camera.position,可以實現以第一人稱視角在空間自由移動的動畫
物體(Mesh)
一個完整的物體對象mesh包括形狀Geometry和材質Material
- Mesh:三維物體,包括Geometry、Material,設置其name屬性可以通過scene.getObjectByName(name)獲取該物體對象;
- Geometry:包括平面Plane、圓形Circle、立方體Cube、球體Sphere、圓柱Cylinder、多面體Polyhedron等,擁有以下屬性:
- Material:包括基礎材質MeshBasicMaterial,深度材質MeshDepthMaterial、法向材質MeshNormalMaterial、面材質MeshFacelMaterial、朗柏材質MeshLambertMaterial、phone材質MeshPhongMaterial、着色器材質ShaderMaterial
以下代碼以創建一個立方體為例
//畫一個立方體 var cubeGeometry=new THREE.BoxGeometry(4,4,4); var cubeMaterial=new THREE.MeshLambertMaterial({color:0xff0000}); cube=new THREE.Mesh(cubeGeometry,cubeMaterial); cube.position.set(-4,3,0); //投射陰影 cube.castShadow=true; scene.add(cube);
記住以下幾點:
- 1.物體mesh的大小是由構成三維物體形狀的頂點坐標
mesh.geometry.vertices
和縮放級別mesh.scale
(vector3)決定的,初始化創建物體時由傳入的參數確定了形狀頂點坐標mesh.geometry.vertices
,后面需要修改物體大小使用mesh.scale
進行縮放 - 2.物體的透明度是材質的透明度屬性
mesh.material.opacity
決定的,若需要設置透明度,需將材質的是否支持半透明屬性mesh.material.transparent
設置為true - 3.在頁面運行期間需要改變物體的材質或屬性需要同步設置
mesh.material.needsUpdate=true
- 4.若物體使用了THREE.MultiMaterial材質(如上面的例子如果多張圖作為貼圖材質),則會將所有材質放到
mesh.meterials
數組中,修改材質需要對每一個材質mesh.materials.material
進行單獨修改 - 5.修改
mesh.rotation
、mesh.position
、mesh.visible
可設置物體旋轉角度、位置、可見性。注意將物體的visible從false改成true,可能會造成畫面卡頓
光源(Light)
全局光:THREE.AmbientLight,影響整個scene的光源,一般是為了弱化陰影或調整整體色調,可設置光照顏色,以顏色的明度確定光源亮度
平行光:THREE.DirectionalLight,模擬類似太陽的光源,所有被照射的區域亮度是一致的,可設置光照顏色、光照方向(通過向量確定方向),以顏色的明度確定光源亮度
點光源:THREE.PointLight:單點發光,照射所有方向,可設置光照強度,光照半徑和光顏色
以下分別在 scene中添加了全局光,1個平行光及5個點光源,項目中平行光作為主要光源、點光源主要用於暗部補光。
var ambient = new THREE.AmbientLight( 0xcccccc ); scene.add( ambient ); var directionalLight = new THREE.DirectionalLight( 0xcccccc ); directionalLight.position.set( -2, 9, 1).normalize();//設置平行光方向 scene.add( directionalLight ); var pointlight = new THREE.PointLight(0xffffff, 1, 3000); pointlight.position.set(0, 0, 2000);//設置點光源位置 scene.add( pointlight ); var pointlight2 = new THREE.PointLight(0xffffff, 1, 2000); pointlight2.position.set(-1000, 1000, 1000); scene.add( pointlight2 ); var pointlight_back = new THREE.PointLight(0xffffff, 1, 2000); pointlight_back.position.set(0, 0, -2000); scene.add( pointlight_back ); var pointlight_left = new THREE.PointLight(0xffffff, 1, 2000); pointlight_left.position.set( -2000, 0,0); scene.add( pointlight_left ); var pointlight_right = new THREE.PointLight(0xffffff, 1, 2000); pointlight_right.position.set(0, -2000, 0); scene.add( pointlight_right );
渲染器(Renderer)
一般情況下我們使用的是WebGL渲染器,
var renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio( window.devicePixelRatio );//設置canvas的像素比為當前設備的屏幕像素比,避免高分屏下模糊 renderer.setSize( _container.offsetWidth, _container.offsetHeight );//設置渲染器大小,即canvas畫布的大小 container.appendChild( renderer.domElement );//在頁面中添加canvas
控制器(Controls)
FlyControls:飛行控制,用鍵盤和鼠標控制相機的移動和轉動
OrbitControls::軌道控制器,模擬軌道中的衛星,繞某個對象旋轉平移,用鍵盤和鼠標控制相機位置
PointerLockControls:指針鎖定,鼠標離開畫布依然能被捕捉到鼠標交互,主要用於游戲
TrackballControls:軌跡球控制器,通過鍵盤和鼠標控制前后左右平移和縮放場景
TransformControls:變換物體控制器,可以通過鼠標對物體的進行拖放等操作
項目中使用的是軌道控制器OrbitControls,並限制了上下旋轉的角度范圍和滾輪控制相機離中心點的最大距離和最小距離
//軌道控制器 var controls = new THREE.OrbitControls(camera,document.getElementById("space")); controls.maxPolarAngle=1.5;//上下兩極的可視區域的最大角度 controls.minPolarAngle=1;//上下兩極的可視區域最小角度 controls.enableDamping=true;//允許遠近拉伸 controls.enableKeys=false;//禁止鍵盤控制 controls.enablePan=false;//禁止平移 controls.dampingFactor = 1;//鼠標滾動一個單位時拉伸幅度 controls.rotateSpeed=1;//旋轉速度 controls.minDistance=0;//離中心物體的最近距離 controls.maxDistance=1000;//離中心物體的最遠距離
下面用以上基礎知識寫一個threejs的場景例子
左上角有fps顯示,右上角有gui控制,可改變參數和點擊按鈕添加mesh,源碼如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script src="three.min.js"></script> <script src="OrbitControls.js"></script> <script src="stats.js"></script> <script src="dat.gui.js"></script> <style> body { margin: 0; overflow: hidden; } </style> </head> <body> <div id="space"></div> <div id="stats"></div> <script> var render,scene,camera,stats,planeGeometry,plane; var cube,sphere; var step=0; function init(){ //場景 scene=new THREE.Scene(); scene.background =new THREE.Color(0xC2E65A); //相機 camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000); //相機方位和朝向 camera.position.set(-30,40,30); //渲染器 render=new THREE.WebGLRenderer(); render.alpha=true; render.setSize(window.innerWidth,window.innerHeight); render.shadowMapEnabled=true; //坐標軸 紅線是X軸,綠線是Y軸,藍線是Z軸 // var axes=new THREE.AxisHelper(20); // scene.add(axes); //畫一個平板 planeGeometry=new THREE.PlaneGeometry(60,40,1,1); var planeMaterial=new THREE.MeshLambertMaterial({color:0x364801}); plane=new THREE.Mesh(planeGeometry,planeMaterial); //繞x軸旋轉90度 plane.rotation.x=-0.5*Math.PI; plane.position.set(0,0,0); //接受陰影 plane.receiveShadow=true; scene.add(plane); //畫一個立方體 var cubeGeometry=new THREE.BoxGeometry(4,4,4); var cubeMaterial=new THREE.MeshLambertMaterial({color:0xff0000}); cube=new THREE.Mesh(cubeGeometry,cubeMaterial); cube.position.set(-4,3,0); //投射陰影 cube.castShadow=true; scene.add(cube); //畫一個球體 var sphereGeometry=new THREE.SphereGeometry(4,20,20); var sphereMaterial=new THREE.MeshLambertMaterial({color:0x00eeff}); sphere=new THREE.Mesh(sphereGeometry,sphereMaterial); sphere.position.set(20,4,2); sphere.castShadow=true; scene.add(sphere); //添加光源 var spotLight=new THREE.SpotLight(0xffffff); spotLight.position.set(-40,60,-10); spotLight.castShadow=true; scene.add(spotLight); //添加霧化效果 //scene.fog=new THREE.FogExp2(0xffffff,0,01); //全場景統一材質 //scene.overrideMaterial=new THREE.MeshLambertMaterial({color:0xffffff}); //軌道控制器 var controls = new THREE.OrbitControls(camera,document.getElementById("space")); controls.maxPolarAngle=1.5;//上下兩極的可視區域的最大角度 controls.minPolarAngle=1;//上下兩極的可視區域最小角度 controls.enableDamping=true;//允許遠近拉伸 controls.enableKeys=false;//禁止鍵盤控制 controls.enablePan=false;//禁止平移 controls.dampingFactor = 1;//鼠標滾動一個單位時拉伸幅度 controls.rotateSpeed=1;//旋轉速度 controls.minDistance=0;//離中心物體的最近距離 controls.maxDistance=1000;//離中心物體的最遠距離 //顯示fps stats=initStats(); document.getElementById("space").appendChild(render.domElement); renderScene(); //參數外控 var gui=new dat.GUI(); gui.add(UISettings,'rotationSpeed',0,0.5); gui.add(UISettings,'bouncingSpeed',0,0.5); gui.add(UISettings,'AddMesh'); gui.add(UISettings,'RemoveMesh'); gui.add(UISettings,'Clear'); gui.add(UISettings,'NumberOfObject').listen(); //事件 window.addEventListener( 'resize', onResize, false ); } window.onload=init; //參數控制 var UISettings=new function(){ this.rotationSpeed=0.02; this.bouncingSpeed=0.03; this.NumberOfObject=0; this.AddMesh=function(){ var cubeGeometry; var r=Math.ceil((Math.random()*10)); var cubeSize=Math.ceil((Math.random()*5)); //隨機幾何體 switch(r) { case 1: cubeGeometry=new THREE.SphereGeometry(cubeSize); break; case 2: cubeGeometry=new THREE.CylinderGeometry(cubeSize,cubeSize,cubeSize); break; case 3: cubeGeometry=new THREE.IcosahedronGeometry(cubeSize); break; case 4: cubeGeometry=new THREE.OctahedronGeometry(cubeSize); break; case 5: cubeGeometry=new THREE.TetrahedronGeometry(cubeSize); break; case 6: cubeGeometry=new THREE.TorusGeometry(cubeSize,1,cubeSize,cubeSize); break; default: cubeGeometry=new THREE.BoxGeometry(cubeSize,cubeSize,cubeSize); } var cubeMaterial=new THREE.MeshLambertMaterial({color:Math.random()*0xffffff}); var cube1=new THREE.Mesh(cubeGeometry,cubeMaterial); cube1.castShadow=true; cube1.name="cube-"+scene.children.length; cube1.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width)); cube1.position.y = Math.round((Math.random() * 5)); cube1.position.z = -20 + Math.round((Math.random() * planeGeometry.parameters.height)); scene.add(cube1); this.NumberOfObject=scene.children.length-2; }; this.RemoveMesh=function(){ var allchildren=scene.children; var lastobject=allchildren[allchildren.length-1]; if(lastobject instanceof THREE.Mesh) { scene.remove(lastobject); this.NumberOfObject=scene.children.length-2; } }; this.Clear=function(){ for(var i=scene.children.length-1;i>=0;i--) { var lastobject=scene.children[i]; if(lastobject instanceof THREE.Mesh&& lastobject !=plane) { scene.remove(lastobject); } } this.NumberOfObject=scene.children.length-2; }; } //動畫 function renderScene(){ stats.update(); scene.traverse(function(obj){ if(obj instanceof THREE.Mesh && obj !=plane) { //自旋轉 obj.rotation.x+=UISettings.rotationSpeed; obj.rotation.y+=UISettings.rotationSpeed; obj.rotation.z+=UISettings.rotationSpeed; } }); //彈跳 step+=UISettings.bouncingSpeed; sphere.position.x=20+(10*(Math.cos(step))); sphere.position.y=2+(10*Math.abs(Math.sin(step))); requestAnimationFrame(renderScene); camera.lookAt(scene.position); render.render(scene,camera); } //設置fps function initStats() { var stats=new Stats(); stats.setMode(0); stats.domElement.style.position='absolute'; stats.domElement.style.left='0px'; stats.domElement.style.top='0px'; stats.domElement.style.width='90px'; document.getElementById("stats").appendChild(stats.domElement); return stats; } //窗口自適應 function onResize(){ camera.aspect=window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); render.setSize(window.innerWidth,window.innerHeight); } </script> </body> </html>