Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Projection plane tilt #10530

Merged
merged 7 commits into from Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions dist/preview release/what's new.md
Expand Up @@ -32,6 +32,7 @@
- spelling of function/variables `xxxByID` renamed to `xxxById` to be consistent over the project. Old `xxxByID` reamain as deprecated that forward to the correspondgin `xxxById` ([barroij](https://github.com/barroij))
- Added new reflector tool that enable remote inspection of scenes. ([bghgary](https://github.com/bghgary))
- Update `createPickingRay` and `createPickingRayToRef` matrix parameter to be nullable. ([jlivak](https://github.com/jlivak))
- Added `applyVerticalCorrection` and `projectionPlaneTilt` to perspective cameras to correct perspective projections ([CraigFeldspar](https://github.com/CraigFeldspar))

### Engine

Expand Down
26 changes: 23 additions & 3 deletions src/Cameras/camera.ts
Expand Up @@ -169,6 +169,14 @@ export class Camera extends Node {
@serialize()
public fov = 0.8;

/**
* Projection plane tilt around the X axis (horizontal), set in Radians. (default is 0)
* Can be used to make vertical lines in world space actually vertical on the screen.
* See https://forum.babylonjs.com/t/add-vertical-shift-to-3ds-max-exporter-babylon-cameras/17480
*/
@serialize()
public projectionPlaneTilt = 0;

/**
* Define the minimum distance the camera can see from.
* This is important to note that the depth buffer are not infinite and the closer it starts
Expand Down Expand Up @@ -404,6 +412,15 @@ export class Camera extends Node {
return ret;
}

/**
* Automatically tilts the projection plane, using `projectionPlaneTilt`, to correct the perspective effect on vertical lines.
*/
public applyVerticalCorrection() {
const rot = this.absoluteRotation.toEulerAngles();

this.projectionPlaneTilt = this._scene.useRightHandedSystem ? -rot.x : rot.x;
RaananW marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Gets the current world space position of the camera.
*/
Expand Down Expand Up @@ -508,7 +525,8 @@ export class Camera extends Node {
if (this.mode === Camera.PERSPECTIVE_CAMERA) {
check = this._cache.fov === this.fov
&& this._cache.fovMode === this.fovMode
&& this._cache.aspectRatio === engine.getAspectRatio(this);
&& this._cache.aspectRatio === engine.getAspectRatio(this)
&& this._cache.projectionPlaneTilt === this.projectionPlaneTilt;
}
else {
check = this._cache.orthoLeft === this.orthoLeft
Expand Down Expand Up @@ -771,13 +789,14 @@ export class Camera extends Node {
this._cache.fov = this.fov;
this._cache.fovMode = this.fovMode;
this._cache.aspectRatio = engine.getAspectRatio(this);
this._cache.projectionPlaneTilt = this.projectionPlaneTilt;

if (this.minZ <= 0) {
this.minZ = 0.1;
}

const reverseDepth = engine.useReverseDepthBuffer;
let getProjectionMatrix: (fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed: boolean, halfZRange: boolean) => void;
let getProjectionMatrix: (fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed: boolean, halfZRange: boolean, projectionPlaneTilt: number) => void;
if (scene.useRightHandedSystem) {
getProjectionMatrix = Matrix.PerspectiveFovRHToRef;
} else {
Expand All @@ -790,7 +809,8 @@ export class Camera extends Node {
reverseDepth ? this.minZ : this.maxZ,
this._projectionMatrix,
this.fovMode === Camera.FOVMODE_VERTICAL_FIXED,
engine.isNDCHalfZRange);
engine.isNDCHalfZRange,
this.projectionPlaneTilt);
} else {
var halfWidth = engine.getRenderWidth() / 2.0;
var halfHeight = engine.getRenderHeight() / 2.0;
Expand Down
49 changes: 33 additions & 16 deletions src/Maths/math.vector.ts
Expand Up @@ -5819,9 +5819,10 @@ export class Matrix {
* @param znear defines the near clip plane
* @param zfar defines the far clip plane
* @param halfZRange true to generate NDC coordinates between 0 and 1 instead of -1 and 1 (default: false)
* @param projectionPlaneTilt optional tilt angle of the projection plane around the X axis (horizontal)
* @returns a new matrix as a left-handed perspective projection matrix
*/
public static PerspectiveLH(width: number, height: number, znear: number, zfar: number, halfZRange?: boolean): Matrix {
public static PerspectiveLH(width: number, height: number, znear: number, zfar: number, halfZRange?: boolean, projectionPlaneTilt: number = 0): Matrix {
var matrix = new Matrix();

let n = znear;
Expand All @@ -5831,10 +5832,11 @@ export class Matrix {
let b = 2.0 * n / height;
let c = (f + n) / (f - n);
let d = -2.0 * f * n / (f - n);
let rot = Math.tan(projectionPlaneTilt);
Popov72 marked this conversation as resolved.
Show resolved Hide resolved

Matrix.FromValuesToRef(
a, 0.0, 0.0, 0.0,
0.0, b, 0.0, 0.0,
0.0, b, 0.0, rot,
0.0, 0.0, c, 1.0,
0.0, 0.0, d, 0.0,
matrix
Expand All @@ -5855,11 +5857,12 @@ export class Matrix {
* @param znear defines the near clip plane
* @param zfar defines the far clip plane
* @param halfZRange true to generate NDC coordinates between 0 and 1 instead of -1 and 1 (default: false)
* @param projectionPlaneTilt optional tilt angle of the projection plane around the X axis (horizontal)
* @returns a new matrix as a left-handed perspective projection matrix
*/
public static PerspectiveFovLH(fov: number, aspect: number, znear: number, zfar: number, halfZRange?: boolean): Matrix {
public static PerspectiveFovLH(fov: number, aspect: number, znear: number, zfar: number, halfZRange?: boolean, projectionPlaneTilt: number = 0): Matrix {
var matrix = new Matrix();
Matrix.PerspectiveFovLHToRef(fov, aspect, znear, zfar, matrix, true, halfZRange);
Matrix.PerspectiveFovLHToRef(fov, aspect, znear, zfar, matrix, true, halfZRange, projectionPlaneTilt);
return matrix;
}

Expand All @@ -5872,8 +5875,9 @@ export class Matrix {
* @param result defines the target matrix
* @param isVerticalFovFixed defines it the fov is vertically fixed (default) or horizontally
* @param halfZRange true to generate NDC coordinates between 0 and 1 instead of -1 and 1 (default: false)
* @param projectionPlaneTilt optional tilt angle of the projection plane around the X axis (horizontal)
*/
public static PerspectiveFovLHToRef(fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true, halfZRange?: boolean): void {
public static PerspectiveFovLHToRef(fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true, halfZRange?: boolean, projectionPlaneTilt: number = 0): void {
let n = znear;
let f = zfar;

Expand All @@ -5882,10 +5886,11 @@ export class Matrix {
let b = isVerticalFovFixed ? t : (t * aspect);
let c = f !== 0 ? (f + n) / (f - n) : 1;
let d = f !== 0 ? -2.0 * f * n / (f - n) : -2 * n;
let rot = Math.tan(projectionPlaneTilt);

Matrix.FromValuesToRef(
a, 0.0, 0.0, 0.0,
0.0, b, 0.0, 0.0,
0.0, b, 0.0, rot,
0.0, 0.0, c, 1.0,
0.0, 0.0, d, 0.0,
result
Expand All @@ -5907,14 +5912,17 @@ export class Matrix {
* @param result defines the target matrix
* @param isVerticalFovFixed defines it the fov is vertically fixed (default) or horizontally
* @param halfZRange true to generate NDC coordinates between 0 and 1 instead of -1 and 1 (default: false)
* @param projectionPlaneTilt optional tilt angle of the projection plane around the X axis (horizontal)
*/
public static PerspectiveFovReverseLHToRef(fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true, halfZRange?: boolean): void {
public static PerspectiveFovReverseLHToRef(fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true, halfZRange?: boolean, projectionPlaneTilt: number = 0): void {
let t = 1.0 / (Math.tan(fov * 0.5));
let a = isVerticalFovFixed ? (t / aspect) : t;
let b = isVerticalFovFixed ? t : (t * aspect);
let rot = Math.tan(projectionPlaneTilt);

Matrix.FromValuesToRef(
a, 0.0, 0.0, 0.0,
0.0, b, 0.0, 0.0,
0.0, b, 0.0, rot,
0.0, 0.0, -znear, 1.0,
0.0, 0.0, 1.0, 0.0,
result
Expand All @@ -5932,11 +5940,12 @@ export class Matrix {
* @param znear defines the near clip plane
* @param zfar defines the far clip plane
* @param halfZRange true to generate NDC coordinates between 0 and 1 instead of -1 and 1 (default: false)
* @param projectionPlaneTilt optional tilt angle of the projection plane around the X axis (horizontal)
* @returns a new matrix as a right-handed perspective projection matrix
*/
public static PerspectiveFovRH(fov: number, aspect: number, znear: number, zfar: number, halfZRange?: boolean): Matrix {
public static PerspectiveFovRH(fov: number, aspect: number, znear: number, zfar: number, halfZRange?: boolean, projectionPlaneTilt: number = 0): Matrix {
var matrix = new Matrix();
Matrix.PerspectiveFovRHToRef(fov, aspect, znear, zfar, matrix, true, halfZRange);
Matrix.PerspectiveFovRHToRef(fov, aspect, znear, zfar, matrix, true, halfZRange, projectionPlaneTilt);
return matrix;
}

Expand All @@ -5949,8 +5958,9 @@ export class Matrix {
* @param result defines the target matrix
* @param isVerticalFovFixed defines it the fov is vertically fixed (default) or horizontally
* @param halfZRange true to generate NDC coordinates between 0 and 1 instead of -1 and 1 (default: false)
* @param projectionPlaneTilt optional tilt angle of the projection plane around the X axis (horizontal)
*/
public static PerspectiveFovRHToRef(fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true, halfZRange?: boolean): void {
public static PerspectiveFovRHToRef(fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true, halfZRange?: boolean, projectionPlaneTilt: number = 0): void {
//alternatively this could be expressed as:
// m = PerspectiveFovLHToRef
// m[10] *= -1.0;
Expand All @@ -5964,10 +5974,11 @@ export class Matrix {
let b = isVerticalFovFixed ? t : (t * aspect);
let c = f !== 0 ? -(f + n) / (f - n) : -1;
let d = f !== 0 ? -2 * f * n / (f - n) : -2 * n;
let rot = Math.tan(projectionPlaneTilt);

Matrix.FromValuesToRef(
a, 0.0, 0.0, 0.0,
0.0, b, 0.0, 0.0,
0.0, b, 0.0, rot,
0.0, 0.0, c, -1.0,
0.0, 0.0, d, 0.0,
result
Expand All @@ -5989,15 +6000,17 @@ export class Matrix {
* @param result defines the target matrix
* @param isVerticalFovFixed defines it the fov is vertically fixed (default) or horizontally
* @param halfZRange true to generate NDC coordinates between 0 and 1 instead of -1 and 1 (default: false)
* @param projectionPlaneTilt optional tilt angle of the projection plane around the X axis (horizontal)
*/
public static PerspectiveFovReverseRHToRef(fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true, halfZRange?: boolean): void {
public static PerspectiveFovReverseRHToRef(fov: number, aspect: number, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true, halfZRange?: boolean, projectionPlaneTilt: number = 0): void {
let t = 1.0 / (Math.tan(fov * 0.5));
let a = isVerticalFovFixed ? (t / aspect) : t;
let b = isVerticalFovFixed ? t : (t * aspect);
let rot = Math.tan(projectionPlaneTilt);

Matrix.FromValuesToRef(
a, 0.0, 0.0, 0.0,
0.0, b, 0.0, 0.0,
0.0, b, 0.0, rot,
0.0, 0.0, -znear, -1.0,
0.0, 0.0, -1.0, 0.0,
result
Expand All @@ -6018,8 +6031,9 @@ export class Matrix {
* @param result defines the target matrix
* @param rightHanded defines if the matrix must be in right-handed mode (false by default)
* @param halfZRange true to generate NDC coordinates between 0 and 1 instead of -1 and 1 (default: false)
* @param projectionPlaneTilt optional tilt angle of the projection plane around the X axis (horizontal)
*/
public static PerspectiveFovWebVRToRef(fov: { upDegrees: number, downDegrees: number, leftDegrees: number, rightDegrees: number }, znear: number, zfar: number, result: Matrix, rightHanded = false, halfZRange?: boolean): void {
public static PerspectiveFovWebVRToRef(fov: { upDegrees: number, downDegrees: number, leftDegrees: number, rightDegrees: number }, znear: number, zfar: number, result: Matrix, rightHanded = false, halfZRange?: boolean, projectionPlaneTilt: number = 0): void {

var rightHandedFactor = rightHanded ? -1 : 1;

Expand All @@ -6029,11 +6043,14 @@ export class Matrix {
var rightTan = Math.tan(fov.rightDegrees * Math.PI / 180.0);
var xScale = 2.0 / (leftTan + rightTan);
var yScale = 2.0 / (upTan + downTan);
let rot = Math.tan(projectionPlaneTilt);

const m = result._m;
m[0] = xScale;
m[1] = m[2] = m[3] = m[4] = 0.0;
m[5] = yScale;
m[6] = m[7] = 0.0;
m[6] = 0.0;
m[7] = rot;
m[8] = ((leftTan - rightTan) * xScale * 0.5);
m[9] = -((upTan - downTan) * yScale * 0.5);
m[10] = -zfar / (znear - zfar);
Expand Down