照相機又分為正交投影照相機與透視投影照相機
舉個簡單的例子來說明正交投影與透視投影照相機的區別。使用透視投影照相機獲得的結果是類似人眼在真實世界中看到的有“近大遠小”的效果(如下圖中的(a));
而使用正交投影照相機獲得的結果就像我們在數學幾何學課上老師教我們畫的效果,對於在三維空間內平行的線,投影到二維空間中也一定是平行的(如下圖中的(b))。
(a)透視投影,(b)正交投影
那么,你的程序需要正交投影還是透視投影的照相機呢?
一般說來,對於制圖、建模軟件通常使用正交投影,這樣不會因為投影而改變物體比例;而對於其他大多數應用,通常使用透視投影,因為這更接近人眼的觀察效果。當然,照相機的選擇並沒有對錯之分,你可以更具應用的特性,選擇一個效果更佳的照相機。
正交投影照相機(Orthographic Camera)設置起來較為直觀,它的構造函數是:
THREE.OrthographicCamera(left, right, top, bottom, near, far)
這六個參數分別代表正交投影照相機拍攝到的空間的六個面的位置,這六個面圍成一個長方體,我們稱其為視景體(Frustum)。只有在視景體內部(下圖中的灰色部分)的物體才可能顯示在屏幕上,而視景體外的物體會在顯示之前被裁減掉。
為了保持照相機的橫豎比例,需要保證(right - left)
與(top - bottom)
的比例與Canvas寬度與高度的比例一致。
near
與far
都是指到照相機位置在深度平面的位置,而照相機不應該拍攝到其后方的物體,因此這兩個值應該均為正值。為了保證場景中的物體不會因為太近或太遠而被照相機忽略,一般near
的值設置得較小,far
的值設置得較大,具體值視場景中物體的位置等決定。
實例說明
下面,我們通過一個具體的例子來解釋正交投影照相機的設置。
基本設置
設置照相機:
var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10); camera.position.set(0, 0, 5); scene.add(camera);
在原點處創建一個邊長為1
的正方體,為了和透視效果做對比,這里我們使用wireframe
而不是實心的材質,以便看到正方體后方的邊:
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true }) ); scene.add(cube);
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <script type="text/javascript" src="libs/three.js"></script> <script type="text/javascript"> function init() { var renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('mainCanvas') }); renderer.setClearColor(0x000000);
var scene = new THREE.Scene(); // camera // canvas size is 400x300 var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10); camera.position.set(0, 0, 5); //camera.lookAt(new THREE.Vector3(0, 0, 0))通過lookAt函數指定它看着原點方向,這樣我們就能過仰望正方體了;
//注意,lookAt函數接受的是一個THREE.Vector3的實例,因此千萬別寫成camera.lookAt(0, 0, 0),否則非但不能得到理想的效果,而且不會報錯,使你很難找到問題所在。
scene.add(camera); // a cube in the scene var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true }) ); scene.add(cube); // render renderer.render(scene, camera); } </script> </head> <body onload="init()"> <canvas id="mainCanvas" width="400px" height="300px" ></canvas> </body> </html>
得到的效果是:
我們看到正交投影的結果是一個正方形,后面的邊與前面完全重合了,這也就是正交投影與透視投影的區別所在。
長寬比例
這里,我們的Canvas寬度是400px
,高度是300px
,照相機水平方向距離4
,垂直方向距離3
,因此長寬比例保持不變。為了試驗長寬比例變化時的效果,我們將照相機水平方向的距離減小為2
:
var camera = new THREE.OrthographicCamera(-1, 1, 1.5, -1.5, 1, 10);
得到的結果是水平方向被拉長了:
照相機位置
接下來,我們來看看照相機位置對渲染結果的影響。在之前的例子中,我們將照相機設置在(0, 0, 5)
位置,而由於照相機默認是面向z軸負方向放置的,所以能看到在原點處的正方體。現在,如果我們將照相機向右移動1
個單位:
var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10); camera.position.set(1, 0, 5);
得到的效果是物體看上去向左移動了:
仔細想一下的話,這也不難理解。就好比你人往右站了,看起來物體就相對往左移動了。
那么,正交投影照相機在設置時,是否需要保證left
和right
是相反數呢?如果不是,那么會產生什么效果呢?下面,我們將原本的參數(-2, 2, 1.5, -1.5, 1, 10)
改為(-1, 3, 1.5, -1.5, 1, 10)
,即,將視景體設置得更靠右:
var camera = new THREE.OrthographicCamera(-1, 3, 1.5, -1.5, 1, 10); camera.position.set(0, 0, 5);
得到的結果是:
細心的讀者已經發現,這與之前向右移動照相機得到的效果是等價的。
換個角度看世界
到現在為止,我們使用照相機都是沿z軸負方向觀察的,因此看到的都是一個正方形。現在,我們想嘗試一下仰望這個正方體。我們已經學會設置照相機的位置,不妨將其設置在(4, -3, 5)
處:
camera.position.set(4, -3, 5);
但是現在照相機沿z軸負方向觀察的,因此觀察不到正方體,只看到一片黑。我們可以通過lookAt
函數指定它看着原點方向:
camera.lookAt(new THREE.Vector3(0, 0, 0));
這樣我們就能過仰望正方體啦:
不過一定要注意,lookAt
函數接受的是一個THREE.Vector3
的實例,因此千萬別寫成camera.lookAt(0, 0, 0)
,否則非但不能得到理想的效果,而且不會報錯,使你很難找到問題所在。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Three框架</title> <script src="libs/Three.js"></script> <style type="text/css"> div#canvas-frame { border: none; cursor: pointer; width: 100%; height: 600px; background-color: #EEEEEE; } </style> <script> var renderer; function initThree() { width = document.getElementById('canvas-frame').clientWidth; height = document.getElementById('canvas-frame').clientHeight; renderer = new THREE.WebGLRenderer({ antialias : true }); renderer.setSize(width, height); document.getElementById('canvas-frame').appendChild(renderer.domElement); renderer.setClearColor(0xFFFFFF, 1.0); } var camera; function initCamera() { camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000); //camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 10, 1000 ); camera.position.x = 0; camera.position.y = 0; camera.position.z = 600; camera.up.x = 0; camera.up.y = 1; camera.up.z = 0; camera.lookAt({ x : 0, y : 0, z : 0 }); } var scene; function initScene() { scene = new THREE.Scene(); } var light; function initLight() { light = new THREE.AmbientLight(0xFF0000); light.position.set(100, 100, 200); scene.add(light); light = new THREE.PointLight(0x00FF00); light.position.set(0, 0,300); scene.add(light); } var cube; function initObject() { var geometry = new THREE.CylinderGeometry( 70,100,200); var material = new THREE.MeshLambertMaterial( { color:0xFFFFFF} ); var mesh = new THREE.Mesh( geometry,material); mesh.position = new THREE.Vector3(0,0,0); scene.add(mesh); } function threeStart() { initThree(); initCamera(); initScene(); initLight(); initObject(); animation(); } function animation() { changeFov(); renderer.render(scene, camera); requestAnimationFrame(animation); } function setCameraFov(fov) { camera.fov = fov; camera.updateProjectionMatrix(); } function changeFov() { var txtFov = document.getElementById("txtFov").value; var val = parseFloat(txtFov); setCameraFov(val); } </script> </head> <body onload="threeStart();"> <div id="canvas-frame"></div> <div> Fov:<input type="text" value="45" id="txtFov"/>(0到180的值) </div> </body> </html>