<!doctype html>
<html>
<head>
<title>Frustum demo</title>
<style>
body { max-width: 100%; }
/* feel free to style the canvas any way you want. If you want it to
use the entire window, set width: 100% and height: 100%. */
canvas {
width: 80%;
height: 400px;
display: block;
margin: 10px auto;
}
</style>
</head>
<body>
<h1>Frustum demo</h1>
<div id="canvasParent"></div>
<p>This demo displays a <em>frustum</em> whose parameters can be adjusted with
the gui controls:
<ul>
<li> <code>fov</code> is the <em>field of view</em> that defines the angular
extent of the scene that is visible to the camera
<li> <code>aspectRatio</code> controls the ratio of the field of view
in the horizontal and vertical directions
<li> <code>near</code> and <code>far</code> define the range of distance
from the camera over which the scene content is visible
<ul>
<li>red and green lines are drawn around the near and far planes, respectively
</ul>
<li> <code>eyeX</code>, <code>eyeY</code>, and <code>eyeZ</code> specify the
position of the camera (EYE location, shown by the MAGENTA ball)
<li> <code>atX</code>, <code>atY</code>, and <code>atZ</code> specify the AT
point, where the camera is looking (shown by the CYAN ball)
<li> <code>upX</code>, <code>upY</code>, and <code>upZ</code> specify a
vector that indicates the UP direction for the camera (shown in yellow)
</ul>
<p> In this demo, the code calculates the exact geometry, given the <code>fov</code>,
<code>aspectRatio</code>, and other parameters.
<p>Here is the function that sets up this camera:
<pre data-code-jsfunction="setupCamera" class="prettyprint lang-js linenums">
</pre>
<div id="footer">
<p>© Scott D. Anderson<br>
This work is licensed under a <a rel="license"
href="http://creativecommons.org/licenses/by-nc-sa/1.0/">Creative Commons License</a>
</body>
</html>
// the "camera" that determines the frustum
var cameraParams = {
near: 10,
far: 30,
fov: 30, // degrees!!
aspectRatio: 400 / 300, // usually from the dimensions of the canvas
atX: 0,
atY: 0,
atZ: -20,
eyeX: 0,
eyeY: 0,
eyeZ: 1,
upX: 0,
upY: 1,
upZ: 0
};
// globals, modified from the above
var at = new THREE.Vector3();
var eye = new THREE.Vector3();
var up = new THREE.Vector3();
function setCameraView() {
at.set(cameraParams.atX, cameraParams.atY, cameraParams.atZ);
eye.set(cameraParams.eyeX, cameraParams.eyeY, cameraParams.eyeZ);
up.set(cameraParams.upX, cameraParams.upY, cameraParams.upZ);
}
setCameraView();
// This function isn't used, but it is displayed to the user.
function setupCamera() {
var cp = cameraParams; // just a shorthand for this function
frustumCamera = new THREE.PerspectiveCamera(
cp.fov,
cp.aspectRatio,
cp.near,
cp.far
);
// set location
frustumCamera.position.set(cp.eyeX, cp.eyeY, cp.eyeZ);
// Cameras inherit an "up" vector from Object3D.
frustumCamera.up.set(cp.upX, cp.upY, cp.upZ);
// The lookAt method computes the camera direction and orientation from its position
// and up parameters, and the input arguments specifying the location of the 'at' point
frustumCamera.lookAt(cp.atX, cp.atY, cp.atZ);
}
TW.createFrustumFOV = function(fov, aspectRatio, near, far) {
var top = near * Math.tan(TW.degrees2radians(fov) / 2);
var bottom = -top;
var right = aspectRatio * top;
var left = -right;
if (TW.debug) {
console.log("camera:", fov, aspectRatio, near, far);
console.log("frustum:", right, left, top, bottom, near, far);
}
return TW.createFrustum(right, left, top, bottom, near, far);
};
function createFrustumParams() {
var f = TW.createFrustumFOV(
cameraParams.fov,
cameraParams.aspectRatio,
cameraParams.near,
cameraParams.far
);
setCameraView();
f.position.copy(eye);
f.up.copy(up);
f.lookAt(at);
f.name = "frustum";
return f;
}
// Canvas, showing frustum
var renderer2 = new THREE.WebGLRenderer();
var scene2 = new THREE.Scene();
var frustum = null;
function recreateFrustum() {
if (frustum != null) {
scene2.remove(frustum);
}
frustum = createFrustumParams();
scene2.add(frustum);
}
recreateFrustum();
function makeMarker(point, color) {
var geom = new THREE.SphereGeometry(1, 5, 5);
var mat = new THREE.MeshBasicMaterial({ color: color });
var mesh = new THREE.Mesh(geom, mat);
mesh.position.copy(point);
return mesh;
}
var atMarker = makeMarker(at, TW.CYAN);
scene2.add(atMarker);
var eyeMarker = makeMarker(eye, TW.MAGENTA);
scene2.add(eyeMarker);
var upArrow = new THREE.ArrowHelper(up, eye, 5, TW.YELLOW);
upArrow.line.material.linewidth = 3;
scene2.add(upArrow);
function redo() {
recreateFrustum();
atMarker.position.copy(at);
eyeMarker.position.copy(eye);
upArrow.position.copy(eye);
up.normalize();
upArrow.setDirection(up);
TW.render();
}
var gui = new dat.GUI();
gui.add(cameraParams, "fov", 1, 179).onChange(redo);
gui.add(cameraParams, "aspectRatio", 0.1, 10).onChange(redo);
gui.add(cameraParams, "near", 1, 50).onChange(redo);
gui.add(cameraParams, "far", 1, 50).onChange(redo);
gui.add(cameraParams, "atX", -10, 10).onChange(redo);
gui.add(cameraParams, "atY", -10, 10).onChange(redo);
gui.add(cameraParams, "atZ", -30, 10).onChange(redo);
gui.add(cameraParams, "eyeX", -10, 10).onChange(redo);
gui.add(cameraParams, "eyeY", -10, 10).onChange(redo);
gui.add(cameraParams, "eyeZ", -30, 30).onChange(redo);
gui.add(cameraParams, "upX", -10, 10).onChange(redo);
gui.add(cameraParams, "upY", -10, 10).onChange(redo);
gui.add(cameraParams, "upZ", -10, 10).onChange(redo);
window.addEventListener("load", function(event) {
TW.mainInit(renderer2, scene2, { parentID: "canvasParent" });
var size = 20;
TW.cameraSetup(renderer2, scene2, {
minx: -size,
maxx: size,
miny: -size,
maxy: size,
minz: -size,
maxz: size
});
TW.toggleAxes("show");
TW.viewFromAboveFrontSide();
});
This Pen doesn't use any external CSS resources.