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

总结

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