threejs官方demo:webgl_camera.html源碼學習

源碼地址

threejs gihub
演示地址
我的例子

開始

對於threejs而言,camera是最基礎也是最重要的一部分。它意味着threejs的眼睛。

源碼分析

  1. 首先執行了一個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);
      }
    	
    
  2. 初始化完成之後就需要加動畫效果,調用animate

    function animate() {
      requestAnimationFrame(animate);
      render();
    }
    function render(){
    	// 操縱動畫
    	/***/
    	// 操縱完 渲染動畫
    	renderer.render(scene, activeCamera);
    }
    
  3. 加動畫

    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);
    
  4. 添加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);
    
    1. 在動的視角,比如我們例子中的正交視角,將邊框進行隱藏,然後在渲染第三視角的相機顯示。
    2. setViewport是設置渲染的位置。第一個是設置了左半邊爲渲染位置,將移動的相機視角渲染出來。
      然後再重新設置右半邊的位置,將第三方相機渲染出來。
  5. 添加切換事件
    爲了對比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;
      }
    }
    
  6. 窗口事件
    主要解決縮放事件進行縮放

    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();
    			}	
    

總結

  • 這個例子並不難,但是可以學到渲染是可以多次渲染,並且可以自己去分配渲染的位置和內容。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章