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

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://s3.amazonaws.com/m.mr-pc.org/t/cisc3620/2020sp/three.min.js
  2. https://s3.amazonaws.com/m.mr-pc.org/t/cisc3620/2020sp/OrbitControls.js
  3. https://s3.amazonaws.com/m.mr-pc.org/t/cisc3620/2020sp/tw.js
  4. https://s3.amazonaws.com/m.mr-pc.org/t/cisc3620/2020sp/dat.gui.min.js