提问人:K. Russell Smith 提问时间:9/11/2020 更新时间:9/11/2020 访问量:243
计算 WebGL 的倾斜投影
Calculating Oblique Projection for WebGL
问:
我正在尝试在 WebGL 2.0 中创建一个门户渲染引擎(到目前为止,我正在尝试移植这个C++项目:https://github.com/HackerPoet/NonEuclidean),并尝试实现倾斜投影,以便将门户相机的剪裁平面与它们各自的门户对齐;但是,尽管在各种 OpenGL 平台上找到了多种实现,但我无法让它在我的 WebGL 项目中工作。以下是与之相关的代码:
const Mat4 = (() =>
{
const { mat3, mat4, vec2, vec3, vec4 } = glMatrix;
return {
...mat4,
zAxis(out, mat)
{
return vec3.set(out, mat[2], mat[6], mat[10]);
},
mulVec4(out, mat, vec)
{
const [x, y, z, w] = vec;
return vec3.set(out,
mat[ 0] * x + mat[ 1] * y + mat[ 2] * z + mat[ 3] * w,
mat[ 4] * x + mat[ 5] * y + mat[ 6] * z + mat[ 7] * w,
mat[ 8] * x + mat[ 9] * y + mat[10] * z + mat[11] * w,
mat[12] * x + mat[13] * y + mat[14] * z + mat[15] * w);
},
mulPoint(out, mat, vec)
{
const [x, y, z] = vec;
const w = mat[12] * x + mat[13] * y + mat[14] * z + mat[15];
return vec3.set(out,
(mat[0] * x + mat[1] * y + mat[ 2] * z + mat[ 3]) / w,
(mat[4] * x + mat[5] * y + mat[ 6] * z + mat[ 7]) / w,
(mat[8] * x + mat[9] * y + mat[10] * z + mat[11]) / w);
},
};
})();
const Camera = () =>
{
const { vec3, vec4 } = glMatrix;
const projection = Mat4.create();
const view = Mat4.create();
return {
width: 0,
height: 0,
aspect: 1,
near: 0.1,
far: 100.0,
fov: Math.PI / 3,
pos: [0, 0, 0],
euler: [0, 0, 0],
get view()
{
return view;
},
set view(matrix)
{
return Mat4.copy(view, matrix);
},
get matrix()
{
return Mat4.multiply(Mat4.create(), projection, view)
},
get projection()
{
return projection;
},
set transform([x = 0, y = 0, z = 0, rx = 0, ry = 0])
{
const matrix = Mat4.fromXRotation(Mat4.create(), rx);
Mat4.multiply(matrix, matrix, Mat4.fromYRotation(Mat4.create(), ry));
Mat4.multiply(matrix, matrix, Mat4.fromTranslation(Mat4.create(), [-x, -y, -z]));
Mat4.copy(view, matrix);
return view;
},
inverse()
{
const inv = Mat4.create();
const a = projection[0];
const b = projection[5];
const c = projection[10];
const d = projection[11];
const e = projection[14];
inv[0] = 1 / a;
inv[5] = 1 / b;
inv[11] = 1 / e;
inv[14] = 1 / d;
inv[15] = -c / (d * e)
return inv;
},
clipOblique(pos, norm)
{
const cpos = Mat4.mulPoint(vec3.create(), view, pos);
const cnorm = vec3.normalize(vec3.create(), Mat4.mulPoint(vec3.create(), view, norm));
const point = Mat4.mulPoint(vec3.create(), Mat4.invert(Mat4.create(), view), [0, 0, 0]);
cpos[1] -= point[1];
const cplane = vec4.set(vec4.create(), cnorm[0], cnorm[1], cnorm[2], -vec3.dot(cpos, cnorm));
const q = Mat4.mulVec4(vec4.create(), Mat4.invert(Mat4.create(), projection), [
(cplane[0] > 0 ? 1 : -1),
(cplane[1] > 0 ? 1 : -1),
1,
1]);
const c = cplane.map(x => x * 2 / vec4.dot(cplane, q));
projection[ 2] = c[0] - projection[ 3];
projection[ 6] = c[1] - projection[ 7];
projection[10] = c[2] - projection[11];
projection[14] = c[3] - projection[15];
return this;
},
copy(that)
{
this.setup(that.width, that.height, that.near, that.far, that.fov, that.aspect);
Mat4.copy(view, that.view);
Mat4.copy(projection, that.projection);
return this;
},
setup(width = this.width, height = this.height, near = this.near, far = this.far, fov = this.fov, aspect = height / width)
{
this.width = width;
this.height = height;
this.near = near;
this.far = far;
this.fov = fov;
this.aspect = aspect;
const f = 1.0 / Math.tan(fov / 2);
const range = 1.0 / (near - far);
projection[0] = f * aspect
projection[1] = 0.0;
projection[2] = 0.0;
projection[3] = 0.0;
projection[4] = 0.0;
projection[5] = f;
projection[6] = 0.0;
projection[7] = 0.0;
projection[8] = 0.0;
projection[9] = 0.0;
projection[10] = (near + far) * range;
projection[11] = -1.0;
projection[12] = 0.0;
projection[13] = 0.0;
projection[14] = 2 * near * far * range;
projection[15] = 0.0;
return this;
}
}
};
const GameObject = (gl, mesh) =>
{
const { vec3 } = glMatrix;
return {
pos: [0, 0, 0],
euler: [0, 0, 0],
scale: [1, 1, 1],
mesh,
forward()
{
const matrix = Mat4.fromZRotation(Mat4.create(), this.euler[2]);
Mat4.multiply(matrix, matrix, Mat4.fromXRotation(Mat4.create(), this.euler[0]));
Mat4.multiply(matrix, matrix, Mat4.fromYRotation(Mat4.create(), this.euler[1]));
return vec3.negate(vec3.create(), Mat4.zAxis(vec3.create(), matrix));
},
localToWorld()
{
const matrix = Mat4.fromTranslation(Mat4.create(), this.pos);
Mat4.multiply(matrix, matrix, Mat4.fromYRotation(Mat4.create(), this.euler[1]));
Mat4.multiply(matrix, matrix, Mat4.fromXRotation(Mat4.create(), this.euler[0]));
Mat4.multiply(matrix, matrix, Mat4.fromZRotation(Mat4.create(), this.euler[2]));
Mat4.multiply(matrix, matrix, Mat4.fromScaling(Mat4.create(), this.scale));
return matrix;
},
worldToLocal()
{
const matrix = Mat4.fromScaling(Mat4.create(), this.scale);
Mat4.invert(matrix, matrix);
Mat4.multiply(matrix, matrix, Mat4.fromZRotation(Mat4.create(), this.euler[2]));
Mat4.multiply(matrix, matrix, Mat4.fromXRotation(Mat4.create(), this.euler[0]));
Mat4.multiply(matrix, matrix, Mat4.fromYRotation(Mat4.create(), this.euler[1]));
Mat4.multiply(matrix, matrix, Mat4.fromTranslation(Mat4.create(), vec3.negate(vec3.create(), this.pos)));
return matrix;
},
render(camera, fbo = null)
{
const mv = Mat4.transpose(Mat4.create(), this.worldToLocal());
const mvp = Mat4.multiply(Mat4.create(), camera.matrix, this.localToWorld());
this.mesh.render(mvp, mv);
return this;
},
};
};
const Portal = (() =>
{
const { vec3 } = glMatrix;
const Warp = (fromPortal = {}) =>
{
const delta = Mat4.identity(Mat4.create());
const deltaInv = Mat4.identity(Mat4.create());
return {
fromPortal,
toPortal: null,
delta, deltaInv,
};
};
return {
connectWarp(a, b)
{
a.toPortal = b.fromPortal;
b.toPortal = a.toPortal;
a.delta = Mat4.multiply(Mat4.create(), a.fromPortal.localToWorld(), b.fromPortal.worldToLocal());
b.delta = Mat4.multiply(Mat4.create(), b.fromPortal.localToWorld(), a.fromPortal.worldToLocal());
a.deltaInv = b.delta;
b.deltaInv = a.delta;
return this;
},
connect(a, b)
{
this.connectWarp(a.front, b.back);
this.connectWarp(b.front, a.back);
return this;
},
create: async (gl) =>
{
const mesh = await Mesh.load('double_quad.obj');
const shader = await Shader(gl, '.', 'portal-shader');
const { a_position } = shader.attribute;
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const buffers = createBuffers(gl, new Array(1));
setupArrayBuffer(gl, buffers[0], new Float32Array(mesh.geometries[0].data.position), 3, a_position);
const portalCam = Camera();
return (self =>
{
self.front = Warp(self);
self.back = Warp(self);
return self;
})({
...GameObject(gl, null),
front: null,
back: null,
get cam()
{
return portalCam;
},
/*Override*/ render(camera, fbo, func)
{
console.assert(this.euler[0] === 0);
console.assert(this.euler[2] === 0);
const normal = this.forward();
const camPos = Mat4.getTranslation(vec3.create(), Mat4.invert(Mat4.create(), camera.view));
const isFront = vec3.dot(vec3.subtract(vec3.create(), camPos, this.pos), normal) > 0;
if (isFront)
{
vec3.negate(normal, normal);
}
const warp = isFront? this.front : this.back;
const mvp = Mat4.multiply(Mat4.create(), camera.matrix, this.localToWorld());
portalCam.copy(camera);
portalCam.clipOblique(vec3.add(vec3.create(), this.pos, normal.map(x => x * 0.1)), vec3.negate(vec3.create(), normal));
Mat4.multiply(portalCam.view, portalCam.view, warp.delta);
shader.use();
gl.bindVertexArray(vao);
const { u_mvp, u_texture } = shader.uniform;
gl.uniform1i(u_texture, 0);
gl.activeTexture(gl.TEXTURE0 + 0);
fbo.use();
gl.uniformMatrix4fv(u_mvp, false, mvp);
gl.drawArrays(gl.TRIANGLES, 0, mesh.geometries[0].data.position.length / 3);
return this;
},
});
},
};
})();
起初,传送门看起来很正常,但是当我试图走到它后面时,对象仍然会被渲染(如果我将自己定位为传送门的摄像机在它们后面),但以一种不寻常的方式。我不是很擅长矩阵数学或线性代数,希望得到一些帮助/见解;谢谢。
答: 暂无答案
上一个:C物理程序中的不规则行为
评论