three.js 02-05 之相机

来源:互联网 发布:伊藤网络超市 编辑:程序博客网 时间:2024/05/16 17:08

    前一篇我们介绍了 Three.js 中 Mesh 网格对象的一些常用函数及属性。本篇我们将介绍 Three.js 中有关相机的概念,照例我们先上一个完整的示例,代码如下:

<!DOCTYPE html><html><head>    <title>示例 02.05 - 相机</title><script src="../build/three.js"></script><script src="../build/js/controls/OrbitControls.js"></script><script src="../build/js/libs/stats.min.js"></script><script src="../build/js/libs/dat.gui.min.js"></script><script src="../jquery/jquery-3.2.1.min.js"></script>    <style>        body {            /* 设置 margin 为 0,并且 overflow 为 hidden,来完成页面样式 */            margin: 0;            overflow: hidden;        }/* 统计对象样式 */#Stats-output {position: absolute;left: 0px;top: 0px;}    </style></head><body><!-- 用于 WebGL 输出的 Div --><div id="WebGL-output"></div><!-- 用于统计 FPS 输出的 Div --><div id="Stats-output"></div><!-- 运行 Three.js 示例的 Javascript 代码 --><script type="text/javascript">var scene;var camera;var render;var controls;var stats;var guiParams;var gui;var plane;var mesh;var centerSphere;    // 当所有元素加载完毕后,就执行我们 Three.js 相关的东西    $(function() {stats = initStats();scene = new THREE.Scene();camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); // 2147483647camera.position.set(120, 60, 180);render = new THREE.WebGLRenderer( {antialias: true} ); // antialias 抗锯齿render.setSize(window.innerWidth, window.innerHeight);render.setClearColor(0xEEEEEE, 1.0);render.shadowMap.enabled = true; // 允许阴影投射$('#WebGL-output')[0].appendChild(render.domElement);window.addEventListener('resize', onWindowResize, false);var target = new THREE.Vector3(scene.position.x, scene.position.y + 10, scene.position.z);controls = new THREE.OrbitControls(camera, render.domElement);controls.target = target; // 解决 camera.lookAt(x, y, z) 失效问题camera.lookAt(target);scene.add(new THREE.AxisHelper(20));// 加入坐标轴// 加入一个平面var planeGeometry = new THREE.PlaneGeometry(180, 180);var planeMaterial = new THREE.MeshLambertMaterial( {color: 0xFFFFFF} );plane = new THREE.Mesh(planeGeometry, planeMaterial);plane.rotation.x = -0.5 * Math.PI; // 沿着 X轴旋转-90°plane.position.x = 0;plane.position.y = 0;plane.position.z = 0;plane.receiveShadow = true; // 几何平面接收阴影scene.add(plane);// 为相机目标设置一个球var centerGeometry = new THREE.SphereGeometry(2);var centerMaterial = planeMaterial.clone();centerSphere = new THREE.Mesh(centerGeometry, centerMaterial);centerSphere.position.set(target.x, controls.target.y, target.z);scene.add(centerSphere);// 加入立方体var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);        for (var j = 0; j < (planeGeometry.parameters.height / 5); j++) {            for (var i = 0; i < planeGeometry.parameters.width / 5; i++) {                var rnd = Math.random() * 0.75 + 0.25;                var cubeMaterial = new THREE.MeshLambertMaterial();                cubeMaterial.color = new THREE.Color(rnd, 0, 0);                var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);                cube.position.z = -((planeGeometry.parameters.height) / 2) + 2 + (j * 5);                cube.position.x = -((planeGeometry.parameters.width) / 2) + 2 + (i * 5);                cube.position.y = 2;                scene.add(cube);            }        }// 加入一个环境光源var ambientLight = new THREE.AmbientLight(0x292929);scene.add(ambientLight);// 加入一个方向灯灯光源// 注:基础材质 MeshBasicMaterial 不会对光源产生反应,因此要改用 MeshLambertMaterial 或 MeshPhongMaterial 材质才有效果var directionalLight = new THREE.DirectionalLight(0xffffff, 0.7);        directionalLight.position.set(-20, 40, 60);        scene.add(directionalLight);// 定义 gui 控制参数guiParams = new function() {this.switchCamera = function () {if (camera instanceof THREE.PerspectiveCamera) {camera = new THREE.OrthographicCamera(window.innerWidth / -16, window.innerHeight / 16, window.innerHeight / 16, window.innerWidth / -16, -200, 500);guiParams.perspective = "Orthographic";} else {camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);guiParams.perspective = "Perspective";}var target = scene.position.clone();controls = new THREE.OrbitControls(camera, render.domElement);target = target;camera.position.set(120, 60, 180);camera.lookAt(target);}this.perspective = "Perspective";}// 定义 gui 并绑定参数gui = new dat.GUI();gui.add(guiParams, 'switchCamera');gui.add(guiParams, 'perspective').listen();renderScene();    });/** 初始化 stats 统计对象 */function initStats() {stats = new Stats();stats.setMode(0); // 0 为监测 FPS;1 为监测渲染时间$('#Stats-output').append(stats.domElement);return stats;}/** 渲染场景 */function renderScene() {stats.update();centerSphere.position.set(controls.target.x, controls.target.y, controls.target.z);requestAnimationFrame(renderScene);render.render(scene, camera);}/** 当浏览器窗口大小变化时触发 */function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();render.setSize(window.innerWidth, window.innerHeight);}</script></body></html>
通过浏览器观察时,默认使用的相机叫透视相机,这是一种最自然的相机。正如你所看到的那样,离相机越远的方块被渲染得越小。通过右上角的 switchCamera 工具,我们可以来在 Three.js 支持的两种相机中回切换。Three.js 中的另一种相机叫正投影相机,在这种相机下,场景中所有的方块渲染出来的尺寸看起来都一样大,对象和相机之间的距离不会影响物体的渲染结果。这种相机通常用在二维游戏或 2.5D 地图中。
    我们先来看看透视相机 PerspectiveCamera,它的常用参数如下表所示:

参数描述fov (视野)fov 表示视野(field of view)。这是从相机位置能够看到的部分场景。例如,人类有差不多180°的视野;而一些鸟类拥有差不多完整的360°的视野;
但是由于计算机显示器不能完全显示我们所看到的景象,所以一般会选择较小的区域。多数游戏会选择60°到90°左右的视野。推荐默认值:45°aspect (宽高比)这是渲染结果输出区的横向宽度与纵向高度的比值。我们的例子中使用了整个窗口作为输出界面。推荐默认值:window.innerWidth / window.innerHeightnear (近面)near 属性定义的是从距离相机多近的地方开始渲染场景。一般情况下我们会为这个属性设置一个很小的值,从而可以渲染从相机位置可以
看到的几乎所有的物体。推荐默认值:0.1far (远面)far 属性定义的是相机可以从它所处的位置看多远。如果我们把这个属性设置得太低,那么场景中的一部分可能不会被渲染;如果太高,在某些情况
下又会影响渲染的效率。推荐默认值:1000下图很好地展示了这些属性:


    接下来我们看看正投影相机 OrthographicCamera,它的常用参数如下表所示:

参数描述left (左边界)这个属性是可视范围的左平面。可以把它当做是可渲染部分的左侧面边界。如果把它设为 -100,那么就不会看到任何比这个左侧面更远的对象right (右边界)跟 left 属性同理top (上边界)跟 left 属性同理bottom (下边界)跟 left 属性同理near (近面)基于相机所在的位置,从这一点开始渲染场景far (远面)基于相机所在的位置,场景一直渲染到这一点下图很好地展示了这些属性:


    一般情况下,在没有明确指出相机的目标时,相机会指向场景的中心,用坐标表示就是 position(0, 0, 0)。但我们也可以轻松改变相机所看到的目标位置,即通过执行 camera.lookAt(x, y, z); 代码来达到目的。我们的示例中则使用了另一种写法,即:camera.lookAt(new THREE.Vector3(x, y, z)); 同样可以实现相同的目的。另外,我们还在场景中央添加了一个球体,来始终指向相机的目标。

未完待续···