Decomposition into several matrices

This commit is contained in:
Dennis Allerkamp 2025-05-08 19:54:30 +02:00
parent 115ab9c3c2
commit 9a05f717fc
3 changed files with 184 additions and 40 deletions

View File

@ -4,6 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CG1 MDI</title>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<script src="matrix-math.js"></script>
<script src="shader.js"></script>
<script src="script.js"></script>
@ -61,28 +62,18 @@
#controls {
left: 20px;
}
#interpolate {
width: 100%;
}
</style>
</head>
<body>
<div id="display">
<canvas id="cv"></canvas>
<div id="stats">
FPS: <span id="fps"></span> - <span id="ms"></span>
</div>
<div id="controls">
<div>
Click & Drag to move camera
</div>
<div>
Scroll to zoom
</div>
<div>
LookAt Matrix: <input type="range" min=0 max=1 step=0.01 value=0 id="interpolateLookAt" autocomplete="off">
</div>
<div>
Projection Matrix: <input type="range" min=0 max=1 step=0.01 value=0 id="interpolateProjection" autocomplete="off" disabled="yes">
<div style="margin-left: 20px;">Invert Z-Axis: <input type="checkbox" id="invertZAxis" checked autocomplete="off"></div>
FPS: <span id="fps"></span> - <span id="ms"></span>
</div>
<div>
Use virtual camera: <input type="checkbox" id="displayMatricesVirtually" checked autocomplete="off">
@ -91,6 +82,62 @@
Backface Culling: <input type="checkbox" id="backfaceCulling" autocomplete="off">
</div>
</div>
<div id="controls">
<div>
$$
\color{red}
\begin{pmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & -1 & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
\frac{2}{r-l} & 0 & 0 & 0 \\
0 & \frac{2}{t-b} & 0 & 0 \\
0 & 0 & \frac{2}{f-n} & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
1 & 0 & 0 & - \frac{r+l}{2} \\
0 & 1 & 0 & - \frac{t+b}{2} \\
0 & 0 & 1 & \frac{n+f}{2} \\
0 & 0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 + \frac{f}{n} & f \\
0 & 0 & - \frac{1}{n} & 0
\end{pmatrix}
\color{green}
\begin{pmatrix}
u_x & u_y & u_z & 0 \\
v_x & v_y & v_z & 0 \\
n_x & n_y & n_z & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
1 & 0 & 0 & -e_x \\
0 & 1 & 0 & -e_y \\
0 & 0 & 1 & -e_z \\
0 & 0 & 0 & 1
\end{pmatrix}
$$
<input id="interpolate" type="range" min="0" max="6" step="0.01" value="6" autocomplete="off" list="steplist">
<datalist id="steplist">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</datalist>
</div>
<div>
</div>
</div>
</div>
</body>
</html>

View File

@ -173,6 +173,32 @@ function mat4BuildLookAt(out, eye, look, up) {
out[2] = n[0]; out[6] = n[1]; out[10] = n[2]; out[14] = t[2];
out[3] = 0; out[7] = 0; out[11] = 0; out[15] = 1;
}
function mat4BuildLookAt1(out, eye, look, up) {
out[0] = 1; out[4] = 0; out[8] = 0; out[12] = -eye[0];
out[1] = 0; out[5] = 1; out[9] = 0; out[13] = -eye[1];
out[2] = 0; out[6] = 0; out[10] = 1; out[14] = -eye[2];
out[3] = 0; out[7] = 0; out[11] = 0; out[15] = 1;
}
function mat4BuildLookAt2(out, eye, look, up) {
let n = [];
let u = [];
let v = [];
vec3Subtract(n, eye, look);
vec3CrossProduct(u, up, n);
vec3CrossProduct(v, n, u);
vec3Normalise(n, n);
vec3Normalise(u, u);
vec3Normalise(v, v);
out[0] = u[0]; out[4] = u[1]; out[8] = u[2]; out[12] = 0;
out[1] = v[0]; out[5] = v[1]; out[9] = v[2]; out[13] = 0;
out[2] = n[0]; out[6] = n[1]; out[10] = n[2]; out[14] = 0;
out[3] = 0; out[7] = 0; out[11] = 0; out[15] = 1;
}
/**
* builds a projection matrix, overwriting out
@ -209,6 +235,43 @@ function mat4BuildPerspective(out, fovy, aspect, n, f) {
mat4BuildProjection(out, r, l, t, b, n, f);
}
function mat4BuildPerspective1(out, fovy, aspect, n, f) {
mat4Identity(out);
out[10] = 1.0 + f / n;
out[11] = -1.0 / n;
out[14] = f;
out[15] = 0.0;
}
function mat4BuildPerspective2(out, fovy, aspect, n, f) {
let t = n * Math.tan(0.5 * fovy);
let b = -t;
let r = aspect * t;
let l = -r;
mat4Identity(out);
out[12] = -(r + l) / 2.0;
out[13] = -(t + b) / 2.0;
out[14] = (f + n) / 2.0;
}
function mat4BuildPerspective3(out, fovy, aspect, n, f) {
let t = n * Math.tan(0.5 * fovy);
let b = -t;
let r = aspect * t;
let l = -r;
mat4Identity(out);
out[0] = 2.0 / (r - l);
out[5] = 2.0 / (t - b);
out[10] = 2.0 / (f - n);
}
function mat4BuildPerspective4(out, fovy, aspect, n, f) {
mat4Identity(out);
out[10] = -1.0;
}
/**
* linearly interpolates between a and b, storing the result in out

View File

@ -28,6 +28,8 @@ let lastFrame = Date.now();
let interpolateProjection = 0;
let interpolateLookAt = 0;
let interpolate = [0, 0, 0, 0, 0, 0];
let displayMatricesVirtually = true;
let virtualRealInterpolation = 1;
@ -64,34 +66,37 @@ async function init() {
// input handling
document.getElementById("interpolateProjection").addEventListener("input", (e) => {
interpolateProjection = e.target.value;
if (interpolateProjection <= 0) {
document.getElementById("interpolateLookAt").disabled = "";
} else {
document.getElementById("interpolateLookAt").disabled = "yes";
document.getElementById("interpolate").addEventListener("input", (e) => {
let t = 6.0 - e.target.value;
if (t <= 2.0) {
interpolateLookAt = t / 2.0;
interpolateProjection = 0.0;
invertZAxis = false;
}
});
document.getElementById("invertZAxis").addEventListener("input", (e) => {
invertZAxis = e.target.checked;
});
document.getElementById("interpolateLookAt").addEventListener("input", (e) => {
interpolateLookAt = e.target.value;
if (interpolateLookAt >= 1) {
document.getElementById("interpolateProjection").disabled = "";
} else {
document.getElementById("interpolateProjection").disabled = "yes";
else if (t <= 5.0) {
interpolateLookAt = 1.0;
interpolateProjection = (t - 2.0) / 3.0;
invertZAxis = false;
}
else {
interpolateLookAt = 1.0;
interpolateProjection = 1.0;
invertZAxis = true;
}
for (let i = 0; i < 6; ++i) {
if (i > t) {
interpolate[i] = 0.0;
}
else if (i+1 > t) {
interpolate[i] = t - Math.floor(t);
}
else {
interpolate[i] = 1.0;
}
}
});
document.getElementById("displayMatricesVirtually").addEventListener("input", (e) => {
displayMatricesVirtually = e.target.checked;
if (!displayMatricesVirtually) {
document.getElementById("invertZAxis").checked = true;
document.getElementById("invertZAxis").disabled = true;
} else {
document.getElementById("invertZAxis").checked = invertZAxis;
document.getElementById("invertZAxis").disabled = false;
}
});
document.getElementById("backfaceCulling").addEventListener("input", (e) => {
if (e.target.checked) {
@ -531,8 +536,37 @@ async function draw() {
let inverseVirtualViewMatrix = [];
mat4Inverse(inverseVirtualViewMatrix, virtualViewMatrix);
mat4Interpolate(virtualProjectionMatrix, identity, virtualProjectionMatrix, interpolateProjection);
mat4Interpolate(virtualViewMatrix, identity, virtualViewMatrix, interpolateLookAt);
// interpolated view matrix
let virtualViewMatrix1 = [];
mat4BuildLookAt1(virtualViewMatrix1, virtualCameraPosition, virtualCameraLookAt, virtualCameraUp);
mat4Interpolate(virtualViewMatrix1, identity, virtualViewMatrix1, interpolate[0]);
let virtualViewMatrix2 = [];
mat4BuildLookAt2(virtualViewMatrix2, virtualCameraPosition, virtualCameraLookAt, virtualCameraUp);
mat4Interpolate(virtualViewMatrix2, identity, virtualViewMatrix2, interpolate[1]);
mat4Multiply(virtualViewMatrix, virtualViewMatrix2, virtualViewMatrix1);
// interpolated projection matrix
let virtualProjectionMatrix1 = [];
mat4BuildPerspective1(virtualProjectionMatrix1, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.9, 5);
mat4Interpolate(virtualProjectionMatrix1, identity, virtualProjectionMatrix1, interpolate[2]);
let virtualProjectionMatrix2 = [];
mat4BuildPerspective2(virtualProjectionMatrix2, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.9, 5);
mat4Interpolate(virtualProjectionMatrix2, identity, virtualProjectionMatrix2, interpolate[3]);
let virtualProjectionMatrix3 = [];
mat4BuildPerspective3(virtualProjectionMatrix3, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.9, 5);
mat4Interpolate(virtualProjectionMatrix3, identity, virtualProjectionMatrix3, interpolate[4]);
let virtualProjectionMatrix4 = [];
mat4BuildPerspective4(virtualProjectionMatrix4, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.9, 5);
mat4Interpolate(virtualProjectionMatrix4, identity, virtualProjectionMatrix4, interpolate[5]);
mat4Multiply(virtualProjectionMatrix, virtualProjectionMatrix4, virtualProjectionMatrix3);
mat4Multiply(virtualProjectionMatrix, virtualProjectionMatrix, virtualProjectionMatrix2);
mat4Multiply(virtualProjectionMatrix, virtualProjectionMatrix, virtualProjectionMatrix1);
gl.uniformMatrix4fv(virtualProjectionLocation, gl.FALSE, new Float32Array(virtualProjectionMatrix));