-
-
Notifications
You must be signed in to change notification settings - Fork 0
Basic Tutorials
A simple page and file set that allows you to test the basic operations of NAS6LIB in the console.
It also includes minimal integration samples with 3D rendering libraries such as Three.js and X3DOM.
Please enable comments and add test code to make use of it.
TestPage ZipFile (External Link)
Please scroll up a little to see the project template.
-
Quick Start
- 1. Load the Library
Include the necessary JavaScript files in your HTML. You'll typically need vector.js as core components.
Add other modules like matrix.js, quaternion.js, planet.js, etc., as needed for your specific use case.
HTML
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/vector.js"></script>
-
2. Minimum Sample (2D Vector Addition)
Here's a simple JavaScript example to get started:
JavaScript
var v1 = new N6LVector([1, 2]);
var v2 = new N6LVector([3, 4]);
var v3 = v1.Add(v2);
console.log(v3.x); // Result: [4, 6]
Loading the Library
HTML
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/vector.js"></script>
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/matrix.js"></script>
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/quaternion.js"></script>
elements order is w-x-y-z-...
JavaScript
//Fourth-order vector
var veca = new N6LVector(4);
//Homo fourth-order vector
var vecb = new N6LVector(4, true);
//Third-order vector
var vecc = new N6LVector(new Array(1, 2, 3));
//x-axis unit homo fourth-order vector
var vecd = new N6LVector(new Array(1, 1, 0, 0), true);
var vece = new N6LVector([1, 1, 0, 0], true);
var vecf = new N6LVector(4, true).UnitVec(1);
//zero homo fourth-order vector
var vecg = new N6LVector(new Array(1, 0, 0, 0), true);
var vech = new N6LVector([1, 0, 0, 0], true);
var veci = new N6LVector(4, true).ZeroVec();
//deep copy
var vecf = new N6LVector(veci);
//4 rows and 4 columns
var mata = new N6LMatrix(4);
//4 rows and 8 columns
var matb = new N6LMatrix(4, 8);
//4 rows and 4 columns unit matrix
var matc = new N6LMatrix(new Array(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), 4, 4);
var matd = new N6LMatrix(new Array(
new N6LVector(new Array(1, 0, 0, 0)),
new N6LVector(new Array(0, 1, 0, 0)),
new N6LVector(new Array(0, 0, 1, 0)),
new N6LVector(new Array(0, 0, 0, 1)) ));
var mate = new N6LMatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] );
//deep copy
var matf = new N6LMatrix(mate);
/*
note:Fourth-order or more is considered to be a homogeneous coordinates
If you want to make a fourth-order or more conventional coordinate N6LMatrix.SetHomo(false)**
Please continue to build declaration
*/* Addition, subtraction, multiplication (vectors and matrices, matrices and matrices), Division (for convenience)
JavaScript
//Vector
/*
When bHomo=true, the w element is skipped.
*/
//[1, 2] + [3, 4] = [4, 6]
var v1 = new N6LVector([1, 2]);
var v2 = new N6LVector([3, 4]);
var v3 = v1.Add(v2);
console.log(v3.x); // Result: [4, 6]
//[1, 2] + 3 = [4, 5]
var v4 = v1.Add(3);
console.log(v4.x); // Result: [4, 5]
//[3, 4] - [1, 2] = [2, 2]
var v5 = v2.Sub(v1);
console.log(v5.x); // Result: [2, 2]
//[3, 4] - 2 = [1, 2]
var v6 = v2.Sub(2);
console.log(v6.x); // Result: [1, 2]
//[1, 2] * [3, 4] = 11
var n1 = v1.Mul(v2);
console.log(n1); // Result: 11
//[1, 2] * [[1, 2], [3, 4]] = [7, 10]
var m1 = new N6LMatrix([[1, 2], [3, 4]]);
var v7 = v1.Mul(m1);
console.log(v7.x); // Result: [7, 10]
//[1, 2] * 2 = [2, 4]
var v8 = v1.Mul(2);
console.log(v8.x); // Result: [2, 4]
//[3, 4] / [1, 2] = 5
var n2 = v2.Div(v1);
console.log(n2); // Result: 5
//[3, 4] / [[1, 2], [3, 4]] = [4.333..., 2.5]
var v9 = v2.Div(m1);
console.log(v9.x); // Result: [4.333..., 2.5]
//[3, 4] / 2 = [1.5, 2]
var v10 = v2.Div(2);
console.log(v10.x); // Result: [1.5, 2]
//Matrix
//[[1, 2], [3, 4]] + [[2, 4], [6, 8]] = [[3, 6], [9, 12]]
var m2 = new N6LMatrix([[2, 4], [6, 8]]);
var m3 = m1.Add(m2);
console.log(m3.x); // Result: [[3, 6], [9, 12]]
//[[1, 2], [3, 4]] + 2 = [[3, 4], [5, 6]]
var m4 = m1.Add(2);
console.log(m4.x); // Result: [[3, 4], [5, 6]]
//[[2, 4], [6, 8]] - [[1, 2], [3, 4]] = [[1, 2], [3, 4]]
var m5 = m2.Sub(m1);
console.log(m5.x); // Result: [[1, 2], [3, 4]]
//[[2, 4], [6, 8]] - 1 = [[1, 3], [5, 7]]
var m6 = m2.Sub(1);
console.log(m6.x); // Result: [[1, 3], [5, 7]]
//[[1, 2], [3, 4]] * [[2, 4], [6, 8]] = [[14, 20], [30, 44]]
var m7 = m1.Mul(m2);
console.log(m7.x); // Result: [[14, 20], [30, 44]]
//[[1, 2], [3, 4]] * [1, 2] = [5, 11]
var v11 = m1.Mul(v1);
console.log(v11.x); // Result: [5, 11]
//[[1, 2], [3, 4]] * 2 = [[2, 4], [6, 8]]
var m8 = m1.Mul(2);
console.log(m8.x); // Result: [[2, 4], [6, 8]]
//[[2, 4], [6, 8]] / [[1, 2], [3, 4]] = [[3.333..., 2], [8.666..., 5]]
var m9 = m2.Div(m1);
console.log(m9.x); // Result: [[3.333..., 2], [8.666..., 5]]
//[[2, 4], [6, 8]] / [1, 2] = [4, 10]
var v12 = m2.Div(v1);
console.log(v12.x); // Result: [4, 10]
//[[2, 4], [6, 8]] / 2 = [[1, 2], [3, 4]]
var m10 = m2.Div(2);
console.log(m10.x); // Result: [[1, 2], [3, 4]] JavaScript
//Vector
//[3, 4].Abs() = 5
var v1 = new N6LVector([3, 4]);
var n1 = v1.Abs();
console.log(n1); // Result: 5
//[3, 4].Normal() = [3/5=0.6, 4/5=0.8]
var v2 = v1.NormalVec();
console.log(v2.x); // Result: [3/5=0.6, 4/5=0.8]
//[3, 4].Normal([6, 8]) = [3/5=0.6, 4/5=0.8]
var v3 = new N6LVector([6, 8]);
var v4 = v1.NormalVec(v3);
console.log(v4.x); // Result: [3/5=0.6, 4/5=0.8]
//Matrix
//[[2, 0], [0, 2]].Normal() = [[1, 0], [0, 1]]
var m1 = new N6LMatrix([[2, 0], [0, 2]]);
var m2 = m1.NormalMat();
console.log(m2.x); // Result: [[1, 0], [0, 1]]JavaScript
//Vector
//[3, 4].Dot([5, 6]) = 3*5+4*6=39
var v1 = new N6LVector([3, 4]);
var v2 = new N6LVector([5, 6]);
var n1 = v1.Dot(v2);
console.log(n1); // Result: 3*5+4*6=39
//[3, 4].Cross([5, 6]) = 3*6-4*5=-2
var n2 = v1.Cross(v2);
console.log(n2); // Result: 3*6-4*5=-2JavaScript
//Matrix
//[[1, 0, 0, 0], [2, 0, 0, 1], [3, 0, 1, 0], [4, -1, 0, 0]].Transpose() = [[1, 2, 3, 4], [0, 0, 0, -1], [0, 0, 1, 0], [0, 1, 0, 0]]
//[[1, 0, 0, 0], [2, 0, 0, 1], [3, 0, 1, 0], [4, -1, 0, 0]].Inverse() = [[1, 0, 0, 0], [2, 0, 0, -1], [3, 0, 1, 0], [4, 1, 0, 0]]
var m1 = new N6LMatrix([[1, 0, 0, 0], [2, 0, 0, 1], [3, 0, 1, 0], [4, -1, 0, 0]]);
var m2 = m1.TransposedMat();
console.log(m2.x); // Result: [[1, 2, 3, 4], [0, 0, 0, -1], [0, 0, 1, 0], [0, 1, 0, 0]]
var dt = [];
var m3 = m1.InverseMat(dt);
console.log(m3.x); // Result: [[1, 0, 0, 0], [2, 0, 0, -1], [3, 0, 1, 0], [4, 1, 0, 0]]
//[[0, 0, 1], [0, 1, 0], [-1, 0, 0]].Transpose() = [[0, 0, -1], [0, 1, 0], [1, 0, 0]]
//[[0, 0, 1], [0, 1, 0], [-1, 0, 0]].Inverse() = [[0, 0, -1], [0, 1, 0], [1, 0, 0]]
var m4 = new N6LMatrix([[0, 0, 1], [0, 1, 0], [-1, 0, 0]]);
var m5 = m4.TransposedMat();
console.log(m5.x); // Result: [[0, 0, -1], [0, 1, 0], [1, 0, 0]]
var m6 = m4.InverseMat(dt);
console.log(m6.x); // Result: [[0, 0, -1], [0, 1, 0], [1, 0, 0]]JavaScript
//Matrix
//[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]].Translate([1, 2, 3, 4]) = [[1, 0, 0, 0], [2, 1, 0, 0], [3, 0, 1, 0], [4, 0, 0, 1]]
var m1 = new N6LMatrix(4).UnitMat();
var v1 = new N6LVector([1, 2, 3, 4], true);
var m2 = m1.TranslatedMat(v1);
console.log(m2.x); // Result: [[1, 0, 0, 0], [2, 1, 0, 0], [3, 0, 1, 0], [4, 0, 0, 1]]JavaScript
//Matrix
var m1 = new N6LMatrix(4).UnitMat();
var v2 = new N6LVector([Math.PI / 2.0, 0, 1, 0], true);
var v3 = new N6LVector([1, 2, 2, 2],true);
var v4 = new N6LVector([1, 3, 4, 5],true);
var m3 = m1.AffineMat(v3, v2, v4);
console.log(m3.x); // Result: [[1, 0, 0, 0], [3, 0, 0, 2], [4, 0, 2, 0], [5, -2, 0, 0]]
var v5 = new N6LVector([1, 0, 1, 0], true);
var m4 = m3.RotAxis(v5, -Math.PI / 2.0);
console.log(m4.x); // Result: [[1, 0, 0, 0], [3, 2, 0, 0], [4, 0, 2, 0], [5, 0, 0, 2]]JavaScript
//Local & World
//Matrix
var um = new N6LMatrix(4).UnitMat();
var ay = new N6LVector(4, true).UnitVec(2);
var lm1 = um.RotAxis(ay, Math.PI / 2.0);
var tr = new N6LVector([1, 0, 0, 5], true);
var lm2 = lm1.TranslatedMat(tr);
console.log(lm2.x); // Result: Local[[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [5, -1, 0, 0]]
var lm3 = lm2.TranslatedMat(tr.Mul(-1));
var lm4 = lm3.RotAxis(ay, -Math.PI / 2.0);
console.log(lm4.x); // Result: World[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]This section explains how N6L handles homogeneous coordinates and the significance of the bHomo flag,
which dictates special behaviors within the library.
-
Coordinate System Expectation
N6L is fundamentally based on DirectX's conventions, therefore, it expects a row-major, left-handed coordinate system.
It's important to note that if you're interacting with other libraries or APIs that adopt a right-handed coordinate system,
you might need to perform transformations like transposition or Z-axis inversion (multiplication by -1) during input and output.
However, as long as you're performing calculations purely within N6L, these external conversions aren't strictly necessary,
provided you consistently align your internal conventions.
- N6L's Matrix Layout
While homogeneous transformation matrices are typically represented as:
M = |ROT T|
|0 1|
where ROT is the rotation component and T is the translation component, N6L adopts a slightly different,
though functionally equivalent, row-major layout for its internal representation. This specific arrangement does not cause any calculation issues.
N6L's expected matrix layout (row-major):
M = |1 0 0 0| // Row 0: W-component (Homogeneous scale)
|Tx M00 M01 M02| // Row 1: Translation X component, followed by Rotation/Scale X-axis components (X-axis of local basis)
|Ty M10 M11 M12| // Row 2: Translation Y component, followed by Rotation/Scale Y-axis components (Y-axis of local basis)
|Tz M20 M21 M22| // Row 3: Translation Z component, followed by Rotation/Scale Z-axis components (Z-axis of local basis)
Key Benefits of Homogeneous Coordinates
Using a homogeneous coordinate system and 4x4 matrices allows various 3D graphics transformations to be handled
as unified linear algebra operations. The benefits are immense:
-
Unified Transformation Representation:
-
Diverse transformations like translation, rotation, scaling, shearing, and even perspective projection
can all be expressed as a single 4x4 matrix multiplication. This simplifies complex transformation chains
(e.g., object rotation → translation → camera view transform) into straightforward matrix products,
significantly streamlining your code. Without homogeneous coordinates, different transformation types
would require distinct calculation methods, leading to much more complex code. -
Efficient Inverse Matrix Calculation (Especially for Rotation): You can extract the 3x3 rotation part from a 4x4 homogeneous matrix and leverage its orthogonal matrix properties
to find its inverse simply by transposing it. This optimization avoids computationally expensive general inverse matrix calculations
(like Gaussian elimination) and greatly contributes to real-time graphics performance. -
Perspective Projection Representation:
The w component of homogeneous coordinates is indispensable for representing perspective projection (depth perception).
As an object's distance changes, its w component varies, enabling correct perspective in the final 3D-to-2D projection. -
Distinguishing Points and Vectors (Transformation Characteristics):
In homogeneous coordinates, points (positions) are typically represented as (x, y, z, 1)
and direction vectors as (x, y, z, 0). This distinction automatically dictates their behavior
when a transformation matrix is applied: -
Points are affected by translation. Direction vectors are not affected by translation (only by rotation and scaling).
This characteristic is also achieved automatically through a single matrix operation.
-
-
The bHomo Flag: N6L's Magic Switch
The bHomo flag acts as a special switch within N6L, enabling unique behaviors when set to true.
When bHomo is true, N6L performs specific operations:
For arithmetic operations, transpositions, and other transformations, N6L extracts the 3x3 ROT component (omitting the w component),
performs the operation on this 3x3 sub-matrix, and then recombines the w component afterwards.
This behavior leverages the intrinsic properties of homogeneous coordinate calculations, allowing for a more intuitive
and streamlined way to describe transformations.
Important Note: There are cases where the bHomo flag must be false at the end of a transformation chain
(e.g., when converting back to non-homogeneous 3D coordinates for specific operations).
Forgetting to appropriately toggle or manage the bHomo flag can lead to unexpected visual errors and bugs.
Always ensure bHomo is set to true or false according to the intended use case of the matrix.
By using N6LXXX.SetHomo(rh) // setting the bHomo flag, N6LXXX.ToHomo() // adding the w element,
and N6LXXX.ToNormal() // removing the w element, etc,
you can manipulate the bHomo flag relatively safely.
The rotation matrix for rotating an object around the ZYZ axes by angles ϕ, θ, and ψ is known as the Euler angle rotation formula.
However, this method commonly leads to a problem called gimbal lock.
Gimbal lock occurs because Euler angles typically define rotations around a fixed set of global axes
(or sometimes, successive rotations around a mix of global and intermediate axes, which can still lead to similar issues).
When two of these axes align (e.g., after a 90-degree rotation around one axis, another axis becomes collinear with a third),
you lose a degree of rotational freedom. This prevents further rotation around the now-aligned axes in distinct directions,
leading to unexpected or uncontrollable rotations.
To avoid gimbal lock, you need a rotation representation that doesn't suffer from this axis alignment problem.
Quaternions (or axis-angle representations, like Rodrigues' rotation formula) provide a solution by representing rotations around
an arbitrary axis. Since quaternions directly encode an axis of rotation and an angle about that axis,
they inherently avoid the issue of axes collapsing, thus preventing gimbal lock.
When comparing quaternions to the equivalent Rodrigues' rotation formula,
a significant advantage of quaternions lies in their computational efficiency.
By using quaternions, we can omit two trigonometric function calculations (sine and cosine)
that are typically required by Rodrigues' formula when converting to a rotation matrix.
Consequently, this also eliminates the need for time-consuming Maclaurin series expansions
that computers use to approximate these trigonometric functions.
This direct bypass of complex calculations is precisely why quaternion-based rotations
are known for their higher computational speed and numerical stability.
JavaScript
var quta = new N6LQuaternion(1, 0, 0, 0);
var qutb = new N6LQuaternion(1, new Array(0, 0, 0));
var qutc = new N6LQuaternion(new Array(1, 0, 0, 0));
var qutd = new N6LQuaternion(new N6LVector([1, 0, 0, 0]));
var qute = new N6LQuaternion(1, new N6LVector([1, 2, 3, 4], true));
var qutf = new N6LQuaternion([1, 0, 0, 0]);
var qutg = new N6LQuaternion(quta); //deep copyJavaScript
//Quaternion
var q1 = new N6LQuaternion([1, 0, 0, 1]);
var q2 = new N6LQuaternion([1, 0, 1, 0]);
var q3 = q1.Mul(q2);
console.log(q3.q.x); // Result: [0.5, -0.5, 0.5, 0.5]
var q4 = q3.NormalQuat();
console.log(q4.q.x); // Result: [0.5, -0.5, 0.5, 0.5]JavaScript
//Vector←→Matrix←→Quaternion
var v1 = new N6LVector([Math.PI / 2.0, 0, 1, 0], true);
var m1 = v1.Matrix();
var q1 = m1.Quaternion();
var m2 = q1.Matrix();
var v2 = m2.Vector();
console.log(v2.x); // Result: [Math.PI / 2.0 = 1.571, 0, 1, 0]JavaScript
var q1 = new N6LQuaternion([1, 0, 0, 1]);
var q2 = new N6LQuaternion([1, 0, 1, 0]);
var q3 = q1.Lerp(q2, 0.5);
console.log(q3.q.x); // Result: [0.816, 0, 0.408, 0.408]
var q4 = q1.Lerp(q2, 1);
console.log(q4.q.x); // Result: [0.707, 0, 0.707, 0]
var q5 = q1.Slerp(q2, 0.5);
console.log(q5.q.x); // Result: [0.816, 0, 0.408, 0.408]
var q6 = q1.Slerp(q2, 1);
console.log(q6.q.x); // Result: [0.707, 0, 0.707, 0]
var q7 = q1.Slerp2(q2, 0.5);
console.log(q7.q.x); // Result: [0.816, 0, 0.408, 0.408]
var q8 = q1.Slerp2(q2, 1);
console.log(q8.q.x); // Result: [0.707, 0, 0.707, 0]This section explains the fundamental concepts of camera and projection in 3D graphics, which are essential
for rendering 3D scenes onto a 2D screen. We'll focus on the relevant functionalities provided by the N6LMatrix and N6LVector libraries.
Simple Perspective Projection Test Demo & Explanation (External Link)
This section outlines a basic demonstration of 3D wireframe rendering using only HTML Canvas and custom mathematical calculations,
without relying on external 3D libraries like Three.js or X3DOM. It serves as a fundamental example of how perspective projection works from scratch.
Core Concepts and Formulas
The demonstration revolves around two primary transformations and their application:
-
Rotation Formula (Rodrigues' Rotation Formula Equivalent):
This rot function handles rotations of a 3D point p around an arbitrary axis a by an angle th.
This custom implementation directly reflects the mathematical principles of rotation.
function rot(a, th, p) {
var c = Math.cos(th), s = Math.sin(th);
return [
(c + a[0] * a[0] * (1 - c)) * p[0] + (a[0] * a[1] * (1 - c) - a[2] * s) * p[1] + (a[0] * a[2] * (1 - c) + a[1] * s) * p[2],
(a[1] * a[0] * (1 - c) + a[2] * s) * p[0] + (c + a[1] * a[1] * (1 - c)) * p[1] + (a[1] * a[2] * (1 - c) - a[0] * s) * p[2],
(a[2] * a[0] * (1 - c) - a[1] * s) * p[0] + (a[2] * a[1] * (1 - c) + a[0] * s) * p[1] + (c + a[2] * a[2] * (1 - c)) * p[2]
];
}-
Near-Plane Perspective Projection Formula:
The frustum function implements a simple perspective projection onto a near plane.
A 3D point P = (Px, Py, Pz) is projected to a 2D point P' = (P'x, P'y) on the screen.
The formula used is:
P' = [ (n / Pz) * Px, (n / Pz) * Py ]
where n is the distance to the near plane.
function frustum(n, p) {
return [(n / p[2]) * p[0], (n / p[2]) * p[1]];
}Animation Loop Structure
The demonstration features an animation loop (GLoop) managed by a N6LTimerMan.
-
Entry Point and Main Loop Setup:
The enter3 function initializes the scene and sets up the main animation loop to run every 50 milliseconds.
function enter3() {
init(); // Initializes 3D points and time
TMan.add(); // Adds timer to manager
TMan.timer[0].setalerm(function() { GLoop(0); }, 50); // Set main loop alarm
return true;
}Main Loop (GLoop):
Each frame, the GLoop function performs a series of transformations on the 3D points:
1.Combined Rotation: Points are successively rotated around the Z, Y, and X axes at different speeds.
2.Z-axis Translation: Points are translated along the Z-axis (e.g., rp[i][2] += 40) to adjust their distance from the camera.
3.Perspective Projection: Each transformed 3D point is then projected onto the 2D near plane using the frustum function.
4.Wireframe Drawing: The projected 2D points are connected to form the wireframe, which is then drawn on the HTML Canvas.
function GLoop(id) {
// ... (time increment and variable declarations)
// Rotation, Translation & Perspective Transformation
for (i = 0; i < p.length; i++) rp[i] = rot([0, 0, 1], 1 * time, p[i]); // Z-axis rotation
for (i = 0; i < rp.length; i++) rp[i] = rot([0, 1, 0], 2 * time, rp[i]); // Y-axis rotation
for (i = 0; i < rp.length; i++) rp[i] = rot([1, 0, 0], 3 * time, rp[i]); // X-axis rotation
for (i = 0; i < rp.length; i++) rp[i][2] += 40; // Z-axis translation
for (i = 0; i < rp.length; i++) pp[i] = frustum(1, rp[i]); // Perspective projection
// ... (Wireframe drawing on Canvas)
}The page also explains the fundamental principle behind converting a 2D screen coordinate back to a 3D world coordinate,
often referred to as "picking" or "ray casting."
Given the perspective projection formulas:
x = (n / Z) * X
y = (n / Z) * Y
The inverse transformations can be derived as:
X = (Z / n) * x
Y = (Z / n) * y
And from these, we can express Z:
Z = (n / x) * X
Z = (n / y) * Y
Combining these leads to the relationship between X and Y on the projected plane:
(n / x) * X = (n / y) * Y
X = (x / y) * Y or Y = (y / x) * X
The core idea is to:
1.Assume a 3D object's Z-coordinate (e.g., Z = -5 for an XY plane at that depth, with n = 1).
2.Calculate the corresponding X and Y coordinates: X = (Z / n) * x = 5x Y = (Z / n) * y = 5y
3.Essentially, the picking process involves starting from a known Z-coordinate
(e.g., the near plane Z=n) and iterating outwards (by increasing Z) until a 3D object is found
that satisfies the derived relationships between its 3D coordinates (X, Y, Z)
and the 2D screen coordinates (x, y). The first object encountered along this "ray" is the one "picked."
The View Matrix is crucial for positioning the viewer in a 3D scene. It transforms objects from the world coordinate system to the view space,
which is essentially the camera's perspective.
To make the camera "look at" a specific point in 3D space, N6LMatrix provides dedicated LookAtMat methods:
-
N6LMatrix.LookAtMat(eye, lookat, up):
This method creates a view matrix based on the camera's position (eye) in world space, the target point (lookat)
it's facing, and the camera's upward direction (up). Think of this as precisely aiming a real-world camera at a subject. -
N6LMatrix.LookAtMat2(rh):
This is an overloaded version of LookAtMat. It uses the N6LMatrix instance itself (presumably representing the camera's current pose)
as the eye position and rh as the lookat target to calculate the view matrix. This is useful for adjusting an existing camera's gaze.
Controlling camera motion primarily involves translation (movement) and rotation (orientation changes).
The N6LMatrix library offers powerful tools to achieve this:
-
N6LMatrix.MoveMat(outmat, outv, d, pyr, v, a, vmin, vmax):
This comprehensive method handles both camera movement and rotation in a single call.
outmat[0] and outv[0] are output parameters that return the matrix and velocity after the movement.
d: Represents the displacement vector for translation.
pyr: A 4-dimensional vector defining the camera's pitch, yaw, and roll.
v: The current velocity.
a: Acceleration.
vmin, vmax: Velocity limits (a value of 987654321.0 signifies no limit).
This method allows for integrated simulation of physics-based camera motion, including acceleration, deceleration, and velocity capping. -
N6LMatrix.TranslatedMat():
For simpler translational movements, this method generates a basic translation matrix. -
N6LMatrix.RotAxis() (and other rotation-related methods):
These methods generate rotation matrices, used for changing the camera's orientation. -
N6LMatrix.InverseMat(dt, sw):
This method calculates the inverse of a matrix, also providing its determinant. It's incredibly useful for deriving the view matrix
from a camera's world matrix, or for reverting complex transformations. The sw parameter offers flexibility
in choosing the inverse calculation algorithm.
By combining these functionalities, you can implement anything from interactive camera controls to automated camera paths in your 3D scenes.
After objects are transformed into view space, they are finally projected onto the 2D screen using a Projection Matrix.
There are two main types of projections: Perspective Projection and Orthographic Projection.
Perspective projection simulates how the human eye perceives depth: distant objects appear smaller, and closer objects appear larger.
It's essential for creating realistic 3D scenes with a sense of depth and scale.
-
N6LMatrix.FrustumMat(left, right, top, bottom, near, far): This method constructs the perspective projection matrix. Its parameters define the view frustum?the truncated pyramid
that represents the visible volume of the 3D world.
left, right, top, bottom: Coordinates of the left, right, top, and bottom clipping planes of the frustum.
near, far: Distances to the near and far clipping planes. These parameters collectively determine the field of view and aspect ratio,
defining which part of the 3D space will be projected onto the screen. -
N6LVector.FromLogAxis(base, range, x) / N6LVector.ToLogAxis(base, range, x): These methods facilitate conversions between logarithmic and normal axes. This is particularly useful for improving the precision
of the depth buffer in scenes containing both very near and very distant objects, mitigating Z-fighting issues. -
N6LVector.FrustumInfVec(base, range, v) / N6LVector.InvFrustumInfVec(base, range, v, z):
These methods are used for infinity perspective projection, a specialized form where the far clipping plane is conceptually at infinity,
often used for optimizing depth range.
Orthographic projection provides a view without perspective. Objects maintain their original size regardless of their distance from the camera,
making it ideal for CAD applications, 2D games, or technical drawings where consistent scale is crucial.
-
N6LMatrix.OrthoMat(left, right, top, bottom, near, far):
This method constructs the orthographic projection matrix. Its parameters define a rectangular bounding box that represents the visible volume.
left, right, top, bottom: Coordinates of the left, right, top, and bottom boundaries of the projection box.
near, far: Distances to the near and far clipping planes. Unlike perspective projection, all objects within this defined box will be projected
with their actual size, regardless of their depth.
HTML
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/timer.js"></script>
JavaScript
// You do not usually need to wait for DOMContentLoaded to instantiate N6LTimerMan, but
// If there is processing that depends on other DOM elements, execute it in DOMContentLoaded.
var TMan = new N6LTimerMan(); // Assumes it is defined in NAS6LIB/javascripts/nas6lib/timer.js
var GLoopID = -1; //ID of Main Thread
//N6L initialization processing
function initializeN6L() {
// Write the main processing for N6L initialization here.
// For example, initializing calculations using N6LMatrix, obtaining Canvas and preparing drawing context.
console.log("DOM is fully loaded. Initializing N6L...");
GLoopID = TMan.add(); // Add a new timer to the timer manager
GLoop(GLoopID); // Start the main thread by running it for the first time
}
//Main thread
function GLoop(id){
// Write the main thread processing here.
// Finally, reset the main thread to continue the thread, but if you do not reset it with an end condition, the thread will stop.
TMan.timer[id].setalerm(function() { GLoop(id); }, 50); // Reset the main thread after 50 milliseconds.
}
// Add a DOMContentLoaded event listener
// It is standard to use window.document.addEventListener.
document.addEventListener('DOMContentLoaded', initializeN6L);
// Alternatively, you can use window.addEventListener('load', initializeN6L);
// DOMContentLoaded is faster and more suitable for DOM manipulation.
N6LTimerMan Multi-threaded construction Demo & Explanation (External Link)