源碼地址
開始
對於threejs而言,camera是最基礎也是最重要的一部分。它意味着threejs的眼睛。
源碼分析
-
首先執行了一個
init
函數const SCREEN_WIDTH = window.innerWidth; const SCREEN_HEIGHT = window.innerHeight; var frustumSize = 600; // 比例 var renderer; // 渲染器 var activeCamera, activeHelper; // 活動的攝像機和邊框 var cameraPerspectiveHelper, cameraPerspective; // 移動的 透視攝像機 var cameraOrtho, cameraOrthoHelper; //移動的 正交相機 var mesh; // 生成的 球體 var scene; // 場景 var cameraRig; // 相機組 var camera; //第三視角的相機 var stats; // stats 渲染率檢測器 function init() { var aspect = SCREEN_WIDTH / SCREEN_HEIGHT; var container = document.createElement("div"); // 節點 document.body.appendChild(container); // 添加到html body scene = new THREE.Scene(); // 初始化場景 // 第一個相機 camera = new THREE.PerspectiveCamera(50, 0.5 * aspect, 1, 10000); camera.position.z = 2500; // 第二個相機 透視相機 cameraPerspective = new THREE.PerspectiveCamera(50, 0.5 * aspect, 150, 1000); // 這個是將相機的邊框顯示出來 cameraPerspectiveHelper = new THREE.CameraHelper(cameraPerspective); scene.add(cameraPerspectiveHelper); // 第三個相機 是正交攝像機 cameraOrtho = new THREE.OrthographicCamera( (0.5 * frustumSize * aspect) / -2, (0.5 * frustumSize * aspect) / 2, frustumSize / 2, frustumSize / -2, 150, 1000 ); // 同樣是把相機的邊框顯示出來 cameraOrthoHelper = new THREE.CameraHelper(cameraOrtho); scene.add(cameraOrthoHelper); // 將第二個相機作爲運動的相機 activeCamera = cameraPerspective; activeHelper = cameraPerspectiveHelper; // 重置相機的方向 cameraOrtho.rotation.y = Math.PI; cameraPerspective.rotation.y = Math.PI; // 利用組來進行控制 cameraRig = new THREE.Group(); cameraRig.add(cameraPerspective); cameraRig.add(cameraOrtho); scene.add(cameraRig); // 創建一個球體 mesh = new THREE.Mesh( new THREE.SphereBufferGeometry(100, 16, 8), new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }) ); scene.add(mesh); var mesh2 = new THREE.Mesh( new THREE.SphereBufferGeometry(50, 16, 8), new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }) ); mesh2.position.y = 150; // 將球體2 添加到球體1 合併爲一組 mesh.add(mesh2); // 第三個球體 var mesh3 = new THREE.Mesh( new THREE.SphereBufferGeometry(5, 16, 8), new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true }) ); mesh3.position.z = 150; // 添加到攝像機組 合併 cameraRig.add(mesh3); // 生成星星 var geometry = new THREE.BufferGeometry(); var vertices = []; for (var i = 0; i < 10000; i++) { vertices.push(THREE.Math.randFloatSpread(2000)); // x vertices.push(THREE.Math.randFloatSpread(2000)); // y vertices.push(THREE.Math.randFloatSpread(2000)); // z } geometry.setAttribute( "position", new THREE.Float32BufferAttribute(vertices, 3) ); // 根據geometry 裏的信息 生成 points var particles = new THREE.Points( geometry, new THREE.PointsMaterial({ color: 0x888888 }) ); scene.add(particles); // 渲染器 renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); // 添加到節點 container.appendChild(renderer.domElement); renderer.autoClear = false; stats = new Stats(); container.appendChild(stats.dom); }
-
初始化完成之後就需要加動畫效果,調用
animate
function animate() { requestAnimationFrame(animate); render(); } function render(){ // 操縱動畫 /***/ // 操縱完 渲染動畫 renderer.render(scene, activeCamera); }
-
加動畫
function render() { // 獲取時間 var r = Date.now() * 0.0005; // 通過時間 來控制動畫 mesh.position.x = 700 * Math.cos(r); mesh.position.z = 700 * Math.sin(r); mesh.position.y = 700 * Math.sin(r); mesh.children[0].position.x = 70 * Math.cos(2 * r); mesh.children[0].position.z = 70 * Math.sin(r); // 控制攝像機移動 if (activeCamera === cameraPerspective) { cameraPerspective.fov = 35 + 30 * Math.sin(0.5 * r); cameraPerspective.far = mesh.position.length(); cameraPerspective.updateProjectionMatrix(); cameraPerspectiveHelper.update(); cameraPerspectiveHelper.visible = true; cameraOrthoHelper.visible = false; } else { cameraOrtho.far = mesh.position.length(); cameraOrtho.updateProjectionMatrix(); cameraOrthoHelper.update(); cameraOrthoHelper.visible = true; cameraPerspectiveHelper.visible = false; } // cameraRig.lookAt(mesh.position); renderer.clear(); activeHelper.visible = false; renderer.setViewport(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); renderer.render(scene, activeCamera); activeHelper.visible = true; renderer.setViewport(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); renderer.render(scene, camera);
-
添加2個窗口
renderer.clear(); activeHelper.visible = false; renderer.setViewport(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); renderer.render(scene, activeCamera); activeHelper.visible = true; renderer.setViewport(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); renderer.render(scene, camera);
- 在動的視角,比如我們例子中的正交視角,將邊框進行隱藏,然後在渲染第三視角的相機顯示。
setViewport
是設置渲染的位置。第一個是設置了左半邊爲渲染位置,將移動的相機視角渲染出來。
然後再重新設置右半邊的位置,將第三方相機渲染出來。
-
添加切換事件
爲了對比2個透視和正交相機的區別,這裏可以去自由地切換移動相機類型。document.addEventListener( 'keydown', onKeyDown, false ); function onKeyDown() { switch (event.keyCode) { case 79 /*O*/: activeCamera = cameraOrtho; activeHelper = cameraOrthoHelper; break; case 80 /*P*/: activeCamera = cameraPerspective; activeHelper = cameraPerspectiveHelper; break; } }
-
窗口事件
主要解決縮放事件進行縮放function onWindowResize() { SCREEN_WIDTH = window.innerWidth; SCREEN_HEIGHT = window.innerHeight; aspect = SCREEN_WIDTH / SCREEN_HEIGHT; renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT ); camera.aspect = 0.5 * aspect; camera.updateProjectionMatrix(); cameraPerspective.aspect = 0.5 * aspect; cameraPerspective.updateProjectionMatrix(); cameraOrtho.left = - 0.5 * frustumSize * aspect / 2; cameraOrtho.right = 0.5 * frustumSize * aspect / 2; cameraOrtho.top = frustumSize / 2; cameraOrtho.bottom = - frustumSize / 2; cameraOrtho.updateProjectionMatrix(); }
總結
- 這個例子並不難,但是可以學到渲染是可以多次渲染,並且可以自己去分配渲染的位置和內容。