I’m working on implementing Portals™® © in PlayCanvas - PlayCanvas | HTML5 Game Engine (following Sebastian Lague’s “Coding Adventure: Portals”)
I’m now at “Oblique projection” (https://www.youtube.com/watch?v=cWpFZbjtSQg&t=735s) and trying to apply an oblique projection to the camera to crop out objects between the camera and the portal.
While it seems to work
but if the camera turns then items further away get clipped.
Any ideas how to fix this?
My calculateProjection function:
ObliqueProjection.prototype.applyObliqueProjection = function (mat) {
const cam = this.entity.camera.camera;
const clipPlane = this.clipPlane;
// Set projection matrix
if (cam.projection === pc.PROJECTION_PERSPECTIVE) {
mat.setPerspective(cam.fov, cam.aspectRatio, cam.nearClip, cam.farClip, cam.horizontalFov);
} else {
const x = cam.orthoHeight * cam.aspect;
mat.setOrtho(-x, x, -cam.orthoHeight, cam.orthoHeight, cam.nearClip, cam.farClip);
}
// Transform plane to view space
const viewMatrix = new pc.Mat4().setTRS(
cam.node.getPosition(),
cam.node.getRotation(),
pc.Vec3.ONE
).invert();
const planePosV = new pc.Vec3();
const planeNormalV = clipPlane.up.clone();
viewMatrix.transformPoint(clipPlane.getPosition(), planePosV);
viewMatrix.transformVector(planeNormalV, planeNormalV).normalize();
const planeV = new pc.Vec4(
planeNormalV.x,
planeNormalV.y,
planeNormalV.z,
-planePosV.dot(planeNormalV)
);
// Apply oblique projection
const projInv = new pc.Mat4().copy(mat).invert();
const planeQ = new pc.Vec4(Math.sign(planeV.x), Math.sign(planeV.y), 1.0, 1.0);
projInv.transformVec4(planeQ, planeQ);
planeV.scale(2.0 / planeV.dot(planeQ));
// Modify projection matrix (third row = clip plane - fourth row)
const p = mat.data;
p[2] = planeV.x - p[3];
p[6] = planeV.y - p[7];
p[10] = planeV.z - p[11];
p[14] = planeV.w - p[15];
};
For completeness, this seems to be the Unity C# implementation:
public static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 sourceProjection, Vector4 clipPlane)
{
var projection = sourceProjection;
var inversion = sourceProjection.inverse;
var cps = new Vector4(
Mathf.Sign(clipPlane.x),
Mathf.Sign(clipPlane.y),
1.0f,
1.0f);
var q = inversion * cps;
Vector4 M4 = new Vector4(projection[3], projection[7], projection[11], projection[15]);
var c = clipPlane * ((2.0f*Vector4.Dot(M4, q)) / Vector4.Dot(clipPlane, q));
projection[2] = c.x - M4.x;
projection[6] = c.y - M4.y;
projection[10] = c.z - M4.z;
projection[14] = c.w - M4.w;
return projection;
}