我正在从头开发3D引擎,我正在尝试取消投影向量。我使用自己的数学库ALMath.js
。据我所知,要将2d屏幕坐标转换为3d世界坐标,我需要将画布中x和y坐标形成的矢量乘以ViewProjection Matrix的倒数。这是unproject的代码:
unproject : function (vector){
var viewMatrix = camera.viewMatrix;
var projectionMatrix = camera.projectionMatrix;
var viewProjection = viewMatrix.multiply(projectionMatrix);
var inverseViewProjection = viewProjection.getInverse();
var x = ((vector.x -0) / (AL3D.width)) *2 -1;
var y = ((vector.y -0) / (AL3D.height)) * 2 -1;
var z = 2*vector.z-1;
var w = 1;
var vec4 = new ALMath.Vector4(x,y,z,w);
var transformedVector = inverseViewProjection.multiplyByVector4(vec4);
var wordCoords = new ALMath.Vector3(transformedVector.x/transformedVector.w,transformedVector.y/transformedVector.w,transformedVector.z/transformedVector.w);
return wordCoords;
}
ALMath
库工作正常。我在所有引擎周围使用它(计算模型 - 视图 - 投影,创建投影矩阵,反向...)并且运行良好。实际上,我使用Octave
(替代matlab
)检查操作的结果,结果与ALMath
相同。
问题在于,如果我点击左上角:
canvas.addEventListener('click', function(event) {
var rect = canvas.getBoundingClientRect();
var x = event.pageX - rect.left,
y = event.pageY - rect.top;
var vector = camera.unproject(new ALMath.Vector3(x,y,0));
});
x = 0
和y = 2
我得到vector = (-0.12131, -0.25894, -0.79409)
,我知道这是错误的,因为如果我将立方体网格设置在那个位置,我看到这不是左上角
我在相机类
中编写了一个lookAt函数lookAt : function (eye, target, up)
作为示例,我使用x = 0
显示y = 2
和octave
的操作。
我将相机设置如下:
camera = new AL3D.PerspectiveCamera(40, window.innerWidth/window.innerHeight);
camera.lookAt(new ALMath.Vector3(), new ALMath.Vector3(0,-0.5,-2), new ALMath.Vector3(0,1,0));
这是octave
中与javascript代码结果匹配的逐步计算
viewMatrix =
1.00000 0.00000 0.00000 0.00000
0.00000 0.97014 0.24254 0.00000
0.00000 -0.24254 0.97014 0.00000
0.00000 0.00000 0.00000 1.00000
projectionMatrix =
1.37374 0.00000 0.00000 0.00000
0.00000 2.82610 0.00000 0.00000
0.00000 0.00000 -1.00020 -0.20002
0.00000 0.00000 -1.00000 0.00000
octave:7> viewProjectionMatrix = viewMatrix * projectionMatrix
viewProjectionMatrix =
1.37374 0.00000 0.00000 0.00000
0.00000 2.74171 -0.24258 -0.04851
0.00000 -0.68543 -0.97034 -0.19405
0.00000 0.00000 -1.00000 0.00000
octave:8> inverseViewProjectionMatrix = inv(viewProjectionMatrix)
inverseViewProjectionMatrix =
0.72794 0.00000 0.00000 -0.00000
0.00000 0.34328 -0.08582 0.00000
0.00000 0.00000 0.00000 -1.00000
0.00000 -1.21256 -4.85023 5.00050
AL3D.width = 1366
AL3D.height = 664
x = -1
y = -0.9939759036144579
z = -1
w = 1
octave:9> vector = [ -1 -0.9939759036144579 -1 1]
vector =
-1.00000 -0.99398 -1.00000 1.00000
octave:10> transformedVector = vector * inverseViewProjectionMatrix
transformedVector =
-0.72794 -1.55377 -4.76492 6.00050
// Perspective division
octave:12> result = [ transformedVector(1)/transformedVector(4) transformedVector(2)/transformedVector(4) transformedVector(3)/transformedVector(4)]
result =
-0.12131 -0.25894 -0.79409
也许我在伪造某些东西,但我不知道。我的逻辑有什么问题。感谢。
编辑:问题似乎是视图矩阵。这是我的视图矩阵的代码:
lookAt : function(eye, target, up){
var eye = eye || new ALMath.Vector3();
var up = up || new ALMath.Vector3();
var target = target || new ALMath.Vector3();
var c = this.components;
var z = target.sub(eye);
z = z.normalize();
var x = z.cross(up);
x = x.normalize();
var y = x.cross(z);
y = y.normalize();
c[0] = x.x; c[1] = x.y; c[2] = x.z;
c[4] = y.x; c[5] = y.y; c[6] = y.z;
c[8] = -z.x; c[9] = -z.y; c[10] = -z.z;
c[12] = -x.dot(eye); c[13] = -y.dot(eye); c[14] = z.dot(eye);
return this;
},
这是投影:
perspectiveProjection : function ( fov, aspect, zNear, zFar ) {
var a = aspect;
var tan=Math.tan(ALMath.degToRad(0.5*fov)),
A=-(zFar+zNear)/(zFar-zNear),
B=(-2*zFar*zNear)/(zFar-zNear);
var c = this.components;
c[ 0 ] = 0.5/tan; c[ 4 ] = 0; c[ 8 ] = 0; c[ 12 ] = 0;
c[ 1 ] = 0; c[ 5 ] = (0.5*a/tan); c[ 9 ] = 0; c[ 13 ] = 0;
c[ 2 ] = 0; c[ 6 ] = 0; c[ 10 ] = A; c[ 14 ] = B;
c[ 3 ] = 0; c[ 7 ] = 0; c[ 11 ] =-1; c[ 15 ] = 0;
return this;
},
我的投影矩阵很好。但是我的视图矩阵与gman库中的视图矩阵计算不同:m4.js中的https://github.com/greggman/twgl.js/blob/master/src/m4.js矩阵是计算的
c[0] = x.x; c[1] = x.y; c[2] = x.z; c[3] = 0;
c[4] = y.x; c[5] = y.y; c[6] = y.z; c[7] = 0;
c[8] = -z.x; c[9] = -z.y; c[10] = -z.z; c[11] = 0;
c[12] = eye.x; c[13] = eye.y; c[14] = eye.z; c[15] = 1;
而不是
c[0] = x.x; c[1] = x.y; c[2] = x.z;
c[4] = y.x; c[5] = y.y; c[6] = y.z;
c[8] = -z.x; c[9] = -z.y; c[10] = -z.z;
c[12] = -x.dot(eye); c[13] = -y.dot(eye); c[14] = z.dot(eye);
请注意,在我的数学库中,我用轴和眼睛之间的点计算它,如下所示:Calculating a LookAt matrix
那么,那个帖子错了?我应该直接用眼睛代替轴和眼睛之间的点积?
如果我运行gman发布的脚本,我会得到以下输出:
frustum points
0 -0.414 -0.207 -0.500
1 0.414 -0.207 -0.500
2 -0.414 0.207 -0.500
3 0.414 0.207 -0.500
4 -82.843 -41.421 -100.000
5 82.843 -41.421 -100.000
6 -82.843 41.421 -100.000
7 82.843 41.421 -100.000
camerafrustum points
0 1.666 2.120 3.080
1 1.080 2.120 3.666
2 1.497 2.458 2.911
3 0.911 2.458 3.497
4 134.224 25.915 19.067
5 17.067 25.915 136.224
6 100.403 93.555 -14.754
7 -16.754 93.555 102.403
screen points (should match width, height)
0 148.858 -47.653 4.029
1 111.806 -38.903 3.734
2 147.454 -72.303 4.217
3 108.845 -59.000 3.876
4 951.911 101.710 9.651
5 61.823 20.354 3.229
6 -833.522 732.104 -10.661
7 25.094 -97.340 4.035
unprojected (should match cameraFrustum points)
0 1.666 2.120 3.080
1 1.080 2.120 3.666
2 1.497 2.458 2.911
3 0.911 2.458 3.497
4 134.224 25.915 19.067
5 17.067 25.915 136.226
6 100.404 93.556 -14.754
7 -16.754 93.557 102.405
与gman发布的结果相同,但screen points (should match width, height)
部分的结果不同。
如果我使用此指令运行我的电脑中的gman脚本:<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
输出与gman发布的相同
screen points (should match width, height)
0 -0.000 -0.000 -1.000
1 300.000 -0.000 -1.000
2 -0.000 150.000 -1.000
3 300.000 150.000 -1.000
4 0.000 0.000 1.000
5 300.000 0.000 1.000
6 -0.000 150.000 1.000
7 300.000 150.000 1.000
但是如果我下载https://twgljs.org/dist/2.x/twgl-full.min.js
并存储在html文件所在的目录中并使用html文件中的指令<script src="twgl.js"></script>
,则输出就像我的数学库,这是:
screen points (should match width, height)
0 148.858 -47.653 4.029
1 111.806 -38.903 3.734
2 147.454 -72.303 4.217
3 108.845 -59.000 3.876
4 951.911 101.710 9.651
5 61.823 20.354 3.229
6 -833.522 732.104 -10.661
7 25.094 -97.340 4.035
适用于我的图书馆的脚本如下:
function main()
{
var width = 300;
var height = 150;
var aspect = width / height
var fieldOfView = Math.PI * 0.25; // 45 degrees
var zNear = 0.5;
var zFar = 100;
var projection = new ALMath.Matrix4();
projection = projection.perspectiveProjection(45, aspect, zNear, zFar);
var eye = new ALMath.Vector3(1, 2, 3);
var target = new ALMath.Vector3(4, 5, 6);
var up = new ALMath.Vector3(0, 1, 0);
var camera = new ALMath.Matrix4();
camera = camera.lookAt(eye, target, up);
var view = camera.getInverse();
var viewProjection = view.multiply(projection);
var inverseViewProjection = viewProjection.getInverse();
function getFrustumPoints(fieldOfView, aspect, zNear, zFar) {
var f = 1 / Math.tan(fieldOfView / 2);
var nearY = zNear / f;
var nearX = nearY * aspect;
var farY = zFar / f;
var farX = farY * aspect;
return [
new ALMath.Vector3(-nearX, -nearY, -zNear),
new ALMath.Vector3( nearX, -nearY, -zNear),
new ALMath.Vector3(-nearX, nearY, -zNear),
new ALMath.Vector3( nearX, nearY, -zNear),
new ALMath.Vector3(-farX, -farY, -zFar),
new ALMath.Vector3( farX, -farY, -zFar),
new ALMath.Vector3(-farX, farY, -zFar),
new ALMath.Vector3( farX, farY, -zFar),
];
}
function projectScreenPoint(width, height, projection, point) {
var c = projection.transformPoint(point);
return new ALMath.Vector3((c.x * 0.5 + 0.5) * width,(c.y * 0.5 + 0.5) * height, c.z);
}
function unproject(width, height, inverseViewProjection, p) {
return inverseViewProjection.transformPoint(new ALMath.Vector3(p.x / width * 2 - 1, p.y / height * 2 - 1, p.z));
}
function showPoints(label, points) {
log(label);
points.forEach((p, ndx) => log(ndx, p.x.toFixed(3), p.y.toFixed(3), p.z.toFixed(3)));
}
var frustumPoints = getFrustumPoints(fieldOfView, aspect, zNear, zFar);
showPoints("frustum points", frustumPoints);
var cameraFrustumPoints = frustumPoints.map(
p => camera.transformPoint(p));
showPoints("camerafrustum points", cameraFrustumPoints);
var screenPoints = cameraFrustumPoints.map(
p => projectScreenPoint(width, height, viewProjection, p));
showPoints("screen points (should match width, height)", screenPoints);
var unprojectedPoints = screenPoints.map(
p => unproject(width, height, inverseViewProjection, p));
showPoints("unprojected (should match cameraFrustum points)",
unprojectedPoints);
function log(...args) {
var elem = document.createElement("pre");
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
}
现在的问题是:
答案 0 :(得分:4)
如果我是你,我会写一些测试。你有一个视锥和一个相机。您应该能够轻松计算平截头体的角落。然后使用这些角落,您应该能够投影它们以获得屏幕坐标。然后检查你是否取消了那些你得到平截头积分的屏幕坐标。
由于您没有发布数学库,我将使用自己的
var m4 = twgl.m4;
// Plug in your math lib here
var m = {
multiply: (a, b) => m4.multiply(a, b),
inverse: (a) => m4.inverse(a),
identity: () => m4.identity(),
lookAt: (eye, target, up) => m4.lookAt(eye, target, up),
perspective: (fov, aspect, zNear, zFar) => m4.perspective(fov, aspect, zNear, zFar),
transformPoint: (m, p) => m4.transformPoint(m, p),
};
var width = 300;
var height = 150;
var aspect = width / height
var fieldOfView = Math.PI * 0.25; // 45 degrees
var zNear = 0.5;
var zFar = 100;
var projection = m.perspective(fieldOfView, aspect, zNear, zFar);
var eye = [1, 2, 3];
var target = [4, 5, 6];
var up = [0, 1, 0];
var camera = m.lookAt(eye, target, up);
var view = m.inverse(camera);
var viewProjection = m.multiply(projection, view);
var inverseViewProjection = m.inverse(viewProjection);
function getFrustumPoints(fieldOfView, aspect, zNear, zFar) {
var f = 1 / Math.tan(fieldOfView / 2);
var nearY = zNear / f;
var nearX = nearY * aspect;
var farY = zFar / f;
var farX = farY * aspect;
return [
[-nearX, -nearY, -zNear],
[ nearX, -nearY, -zNear],
[-nearX, nearY, -zNear],
[ nearX, nearY, -zNear],
[-farX, -farY, -zFar],
[ farX, -farY, -zFar],
[-farX, farY, -zFar],
[ farX, farY, -zFar],
];
}
function projectScreenPoint(width, height, projection, point) {
var c = m.transformPoint(projection, point);
return [
(c[0] * 0.5 + 0.5) * width,
(c[1] * 0.5 + 0.5) * height,
c[2],
];
}
function unproject(width, height, inverseViewProjection, p) {
return m.transformPoint(
inverseViewProjection,
[
p[0] / width * 2 - 1,
p[1] / height * 2 - 1,
p[2],
]);
}
function showPoints(label, points) {
log(label);
points.forEach((p, ndx) => log(ndx, p[0].toFixed(3), p[1].toFixed(3), p[2].toFixed(3)));
}
var frustumPoints = getFrustumPoints(fieldOfView, aspect, zNear, zFar);
showPoints("frustum points", frustumPoints);
var cameraFrustumPoints = frustumPoints.map(
p => m.transformPoint(camera, p));
showPoints("camerafrustum points", cameraFrustumPoints);
var screenPoints = cameraFrustumPoints.map(
p => projectScreenPoint(width, height, viewProjection, p));
showPoints("screen points (should match width, height)", screenPoints);
var unprojectedPoints = screenPoints.map(
p => unproject(width, height, inverseViewProjection, p));
showPoints("unprojected (should match cameraFrustum points)",
unprojectedPoints);
function log(...args) {
var elem = document.createElement("pre");
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
pre { margin: 0 };
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
注意:m4.transformPoint
为结果<{1}}除以w
将数学库插入上面?
以下是插入glMatrix
的示例
// Plug in your math lib here
var m = {
multiply: (a, b) => mat4.multiply(mat4.create(), a, b),
inverse: (a) => mat4.invert(mat4.create(), a),
identity: () => mat4.create(),
lookAt: (eye, target, up) => mat4.invert(
mat4.create(),
mat4.lookAt(mat4.create(), eye, target, up)),
perspective: (fov, aspect, zNear, zFar) => mat4.perspective(
mat4.create(), fov, aspect, zNear, zFar),
transformPoint: (m, p) => vec3.transformMat4(vec3.create(), p, m),
};
var width = 300;
var height = 150;
var aspect = width / height
var fieldOfView = Math.PI * 0.25; // 45 degrees
var zNear = 0.5;
var zFar = 100;
var projection = m.perspective(fieldOfView, aspect, zNear, zFar);
var eye = [1, 2, 3];
var target = [4, 5, 6];
var up = [0, 1, 0];
var camera = m.lookAt(eye, target, up);
var view = m.inverse(camera);
var viewProjection = m.multiply(projection, view);
var inverseViewProjection = m.inverse(viewProjection);
function getFrustumPoints(fieldOfView, aspect, zNear, zFar) {
var f = 1 / Math.tan(fieldOfView / 2);
var nearY = zNear / f;
var nearX = nearY * aspect;
var farY = zFar / f;
var farX = farY * aspect;
return [
[-nearX, -nearY, -zNear],
[ nearX, -nearY, -zNear],
[-nearX, nearY, -zNear],
[ nearX, nearY, -zNear],
[-farX, -farY, -zFar],
[ farX, -farY, -zFar],
[-farX, farY, -zFar],
[ farX, farY, -zFar],
];
}
function projectScreenPoint(width, height, projection, point) {
var c = m.transformPoint(projection, point);
return [
(c[0] * 0.5 + 0.5) * width,
(c[1] * 0.5 + 0.5) * height,
c[2],
];
}
function unproject(width, height, inverseViewProjection, p) {
return m.transformPoint(
inverseViewProjection,
[
p[0] / width * 2 - 1,
p[1] / height * 2 - 1,
p[2],
]);
}
function showPoints(label, points) {
log(label);
points.forEach((p, ndx) => log(ndx, p[0].toFixed(3), p[1].toFixed(3), p[2].toFixed(3)));
}
var frustumPoints = getFrustumPoints(fieldOfView, aspect, zNear, zFar);
showPoints("frustum points", frustumPoints);
var cameraFrustumPoints = frustumPoints.map(
p => m.transformPoint(camera, p));
showPoints("camerafrustum points", cameraFrustumPoints);
var screenPoints = cameraFrustumPoints.map(
p => projectScreenPoint(width, height, viewProjection, p));
showPoints("screen points (should match width, height)", screenPoints);
var unprojectedPoints = screenPoints.map(
p => unproject(width, height, inverseViewProjection, p));
showPoints("unprojected (should match cameraFrustum points)",
unprojectedPoints);
function log(...args) {
var elem = document.createElement("pre");
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
pre { margin: 0 };
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
至于你的例子,你传递0代表z,它位于截头体深度明智的中间。我也看到这样的代码
transformedVector(1)/transformedVector(4)
我不知道你的数学库,但我的基础是零基础索引,所以
transformedVector(0)/transformedVector(3)
以下是您添加到示例中的代码。它对我有用。我填写了缺少的数学函数。
const m4 = twgl.m4;
class Vector3 {
constructor(x, y, z) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
sub(v) {
return new Vector3(this.x - v.x, this.y - v.y, this.z - v.z);
}
cross(v) {
return new Vector3(
this.y * v.z - this.z * v.y,
this.z * v.x - this.x * v.z,
this.x * v.y - this.y * v.x);
}
dot(v) {
return (this.x * v.x) + (this.y * v.y) + (this.z * v.z);
}
normalize() {
var lenSq = this.x * this.x + this.y * this.y + this.z * this.z;
var len = Math.sqrt(lenSq);
if (len > 0.00001) {
return new Vector3(this.x / len, this.y / len, this.z / len);
} else {
return new Vector3();
}
}
}
class Vector4 {
constructor(x, y, z, w) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
this.w = w || 0;
}
}
class Matrix4 {
constructor(components) {
this.components = components || m4.identity();
}
multiply(m) {
return new Matrix4(m4.multiply(m.components, this.components));
}
getInverse() {
return new Matrix4(m4.inverse(this.components));
}
multiplyByVector4(v) {
const m = this.components;
const x = v.x * m[0 * 4 + 0] + v.y * m[1 * 4 + 0] + v.z * m[2 * 4 + 0] + v.w * m[3 * 4 + 0];
const y = v.x * m[0 * 4 + 1] + v.y * m[1 * 4 + 1] + v.z * m[2 * 4 + 1] + v.w * m[3 * 4 + 1];
const z = v.x * m[0 * 4 + 2] + v.y * m[1 * 4 + 2] + v.z * m[2 * 4 + 2] + v.w * m[3 * 4 + 2];
const w = v.x * m[0 * 4 + 3] + v.y * m[1 * 4 + 3] + v.z * m[2 * 4 + 3] + v.w * m[3 * 4 + 3];
return new Vector4(x, y, z, w);
}
transformPoint(v) {
const v4 = this.multiplyByVector4(new Vector4(v.x, v.y, v.z, 1));
return new Vector3(v4.x / v4.w, v4.y / v4.w, v4.z / v4.w);
}
lookAt(eye, target, up) {
var eye = eye || new ALMath.Vector3();
var up = up || new ALMath.Vector3();
var target = target || new ALMath.Vector3();
var c = this.components;
var z = target.sub(eye);
z = z.normalize();
var x = z.cross(up);
x = x.normalize();
var y = x.cross(z);
y = y.normalize();
c[0] = x.x; c[1] = x.y; c[2] = x.z;
c[4] = y.x; c[5] = y.y; c[6] = y.z;
c[8] = -z.x; c[9] = -z.y; c[10] = -z.z;
c[12] = -x.dot(eye); c[13] = -y.dot(eye); c[14] = z.dot(eye);
return this;
}
perspectiveProjection( fov, aspect, zNear, zFar ) {
var a = aspect;
var tan=Math.tan(ALMath.degToRad(0.5*fov)),
A=-(zFar+zNear)/(zFar-zNear),
B=(-2*zFar*zNear)/(zFar-zNear);
var c = this.components;
c[ 0 ] = 0.5/tan; c[ 4 ] = 0; c[ 8 ] = 0; c[ 12 ] = 0;
c[ 1 ] = 0; c[ 5 ] = (0.5*a/tan); c[ 9 ] = 0; c[ 13 ] = 0;
c[ 2 ] = 0; c[ 6 ] = 0; c[ 10 ] = A; c[ 14 ] = B;
c[ 3 ] = 0; c[ 7 ] = 0; c[ 11 ] =-1; c[ 15 ] = 0;
return this;
}
}
class PerspectiveCamera {
constructor(fieldOfViewDegrees, aspect, zNear, zFar) {
this.fieldOfViewDegrees = fieldOfViewDegrees || 45;
this.aspect = aspect || 1;
this.zNear = zNear || 0.5;
this.zFar = zFar || 100;
this.projectionMatrix = new Matrix4();
this.viewMatrix = new Matrix4();
this.updateProjection();
}
updateProjection() {
this.projectionMatrix.perspectiveProjection(
this.fieldOfViewDegrees, this.aspect, this.zNear, this.zFar);
}
lookAt(eye, target, up) {
//this.viewMatrix.lookAt(eye, target, up);
this.cameraMatrix = this.viewMatrix.getInverse();
}
transformPoint(v) {
// note this tranasforms by the camera matrix
// (which is the inverse view matrix)
// and not the perspective matrix
return this.cameraMatrix.transformPoint(v);
}
}
const ALMath = {
Vector3: Vector3,
Matrix4: Matrix4,
degToRad: d => d * Math.PI / 180,
};
const AL3D = {
width: 300,
height: 150,
PerspectiveCamera: PerspectiveCamera,
};
const camera = new AL3D.PerspectiveCamera(40, AL3D.width/AL3D.height);
camera.lookAt(
new ALMath.Vector3(),
new ALMath.Vector3(0,-0.5,-2),
new ALMath.Vector3(0,1,0));
function getFrustumPoints(fieldOfView, aspect, zNear, zFar) {
var f = 1 / Math.tan(ALMath.degToRad(fieldOfView) / 2);
var nearY = zNear / f;
var nearX = nearY * aspect;
var farY = zFar / f;
var farX = farY * aspect;
return [
new ALMath.Vector3(-nearX, -nearY, -zNear),
new ALMath.Vector3( nearX, -nearY, -zNear),
new ALMath.Vector3(-nearX, nearY, -zNear),
new ALMath.Vector3( nearX, nearY, -zNear),
new ALMath.Vector3(-farX, -farY, -zFar),
new ALMath.Vector3( farX, -farY, -zFar),
new ALMath.Vector3(-farX, farY, -zFar),
new ALMath.Vector3( farX, farY, -zFar),
];
}
const projectionMatrix = camera.projectionMatrix;
const viewMatrix = camera.viewMatrix;
const viewProjection = viewMatrix.multiply(projectionMatrix);
const inverseViewProjection = viewProjection.getInverse();
function projectScreenPoint(width, height, projection, point) {
var c = projectionMatrix.transformPoint(point);
return new ALMath.Vector3(
(c.x * 0.5 + 0.5) * width,
(c.y * 0.5 + 0.5) * height,
c.z);
}
function unproject(width, height, inverseViewProjection, p) {
return inverseViewProjection.transformPoint(new ALMath.Vector3(
p.x / width * 2 - 1,
p.y / height * 2 - 1,
p.z));
}
function showPoints(label, points) {
log(label);
points.forEach((p, ndx) => log(ndx, p.x.toFixed(3), p.y.toFixed(3), p.z.toFixed(3)));
}
var frustumPoints = getFrustumPoints(camera.fieldOfViewDegrees, camera.aspect, camera.zNear, camera.zFar);
showPoints("frustum points", frustumPoints);
var cameraFrustumPoints = frustumPoints.map(
p => camera.transformPoint(p));
showPoints("camerafrustum points", cameraFrustumPoints);
var screenPoints = cameraFrustumPoints.map(
p => projectScreenPoint(AL3D.width, AL3D.height, viewProjection, p));
showPoints("screen points (should match width, height)", screenPoints);
var unprojectedPoints = screenPoints.map(
p => unproject(AL3D.width, AL3D.height, inverseViewProjection, p));
showPoints("unprojected (should match cameraFrustum points)",
unprojectedPoints);
function log(...args) {
var elem = document.createElement("pre");
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
pre { margin: 0; }
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>