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

optimization viewportToWorldPoint and screenToWorldPoint #604

Merged
merged 12 commits into from
Dec 6, 2021
50 changes: 26 additions & 24 deletions packages/core/src/Camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import { Transform } from "./Transform";
import { UpdateFlag } from "./UpdateFlag";

class MathTemp {
static tempRay = new Ray();
static tempMat4 = new Matrix();
static tempVec4 = new Vector4();
static tempVec3 = new Vector3();
static tempVec2 = new Vector2();
}

Expand Down Expand Up @@ -321,32 +321,33 @@ export class Camera extends Component {

/**
* Transform a point from viewport space to world space.
* @param point - Point in viewport space, X and Y are the viewport space coordinates, Z is the viewport depth. The near clipping plane is 0, and the far clipping plane is 1
* @param point - Point in viewport space, X and Y are the camera viewport space coordinates, Z is in world units from the camera
* @param out - Point in world space
* @returns Point in world space
*/
viewportToWorldPoint(point: Vector3, out: Vector3): Vector3 {
const invViewProjMat = this.invViewProjMat;
return this._innerViewportToWorldPoint(point, invViewProjMat, out);
const { tempVec2: pointXY, tempRay: ray } = MathTemp;
pointXY.setValue(point.x, point.y);
this.viewportPointToRay(pointXY, ray);
Vector3.scale(ray.direction, point.z, out);
Vector3.add(ray.origin, out, out);
return out;
}

/**
* Generate a ray by a point in viewport.
* @param point - Point in viewport space, which is represented by normalization
* @param point - Point in viewport space, X and Y are the camera viewport space coordinates
* @param out - Ray
* @returns Ray
*/
viewportPointToRay(point: Vector2, out: Ray): Ray {
const clipPoint = MathTemp.tempVec3;
const invViewProjMat = this._getInvViewProjMat();
// Use the intersection of the near clipping plane as the origin point.
clipPoint.setValue(point.x, point.y, 0);
const origin = this.viewportToWorldPoint(clipPoint, out.origin);
const origin = this._innerViewportToWorldPoint(point, 0.0, invViewProjMat, out.origin);
// Use the intersection of the far clipping plane as the origin point.
clipPoint.z = 1.0;
const farPoint: Vector3 = this._innerViewportToWorldPoint(clipPoint, this._invViewProjMat, clipPoint);
Vector3.subtract(farPoint, origin, out.direction);
out.direction.normalize();

const direction: Vector3 = this._innerViewportToWorldPoint(point, 1.0, invViewProjMat, out.direction);
Vector3.subtract(direction, origin, direction);
direction.normalize();
return out;
}

Expand All @@ -361,6 +362,7 @@ export class Camera extends Component {
const viewport = this.viewport;
out.x = (point.x / canvas.width - viewport.x) / viewport.z;
out.y = (point.y / canvas.height - viewport.y) / viewport.w;
(<Vector3>point).z !== undefined && ((<Vector3>out).z = (<Vector3>point).z);
return out;
}

Expand All @@ -375,6 +377,7 @@ export class Camera extends Component {
const viewport = this.viewport;
out.x = (viewport.x + point.x * viewport.z) * canvas.width;
out.y = (viewport.y + point.y * viewport.w) * canvas.height;
(<Vector3>point).z !== undefined && ((<Vector3>out).z = (<Vector3>point).z);
return out;
}

Expand All @@ -391,7 +394,8 @@ export class Camera extends Component {

/**
* Transform a point from screen space to world space.
* @param point - Screen space point
*
* @param point - Screen space point, the top-left of the screen is (0,0), the right-bottom is (pixelWidth,pixelHeight), The z position is in world units from the camera
* @param out - Point in world space
* @returns Point in world space
*/
Expand All @@ -402,7 +406,7 @@ export class Camera extends Component {

/**
* Generate a ray by a point in screen.
* @param point - Point in screen space, the unit is pixel
* @param point - Point in screen space, the top-left of the screen is (0,0), the right-bottom is (pixelWidth,pixelHeight)
* @param out - Ray
* @returns Ray
*/
Expand Down Expand Up @@ -478,11 +482,11 @@ export class Camera extends Component {
this._isInvViewProjDirty.flag = true;
}

private _innerViewportToWorldPoint(point: Vector3, invViewProjMat: Matrix, out: Vector3): Vector3 {
private _innerViewportToWorldPoint(pointXY: Vector2, pointZ: number, invViewProjMat: Matrix, out: Vector3): Vector3 {
// Depth is a normalized value, 0 is nearPlane, 1 is farClipPlane.
// Transform to clipping space matrix
const clipPoint = MathTemp.tempVec4;
clipPoint.setValue(point.x * 2 - 1, 1 - point.y * 2, point.z * 2 - 1, 1);
clipPoint.setValue(pointXY.x * 2 - 1, 1 - pointXY.y * 2, pointZ * 2 - 1, 1);
Vector4.transform(clipPoint, invViewProjMat, clipPoint);
const invW = 1.0 / clipPoint.w;
out.x = clipPoint.x * invW;
Expand All @@ -491,33 +495,31 @@ export class Camera extends Component {
return out;
}

private _updateShaderData(context: RenderContext) {
private _updateShaderData(context: RenderContext): void {
const shaderData = this.shaderData;
shaderData.setMatrix(Camera._viewMatrixProperty, this.viewMatrix);
shaderData.setMatrix(Camera._projectionMatrixProperty, this.projectionMatrix);
shaderData.setMatrix(Camera._vpMatrixProperty, context._viewProjectMatrix);
shaderData.setMatrix(Camera._inverseViewMatrixProperty, this._transform.worldMatrix);
shaderData.setMatrix(Camera._inverseProjectionMatrixProperty, this.inverseProjectionMatrix);
shaderData.setMatrix(Camera._inverseProjectionMatrixProperty, this._getInverseProjectionMatrix());
shaderData.setVector3(Camera._cameraPositionProperty, this._transform.worldPosition);
}

/**
* @private
* The inverse matrix of view projection matrix.
*/
get invViewProjMat(): Matrix {
private _getInvViewProjMat(): Matrix {
if (this._isInvViewProjDirty.flag) {
this._isInvViewProjDirty.flag = false;
Matrix.multiply(this._transform.worldMatrix, this.inverseProjectionMatrix, this._invViewProjMat);
Matrix.multiply(this._transform.worldMatrix, this._getInverseProjectionMatrix(), this._invViewProjMat);
}
return this._invViewProjMat;
}

/**
* @private
* The inverse of the projection matrix.
*/
get inverseProjectionMatrix(): Readonly<Matrix> {
private _getInverseProjectionMatrix(): Readonly<Matrix> {
if (this._isInvProjMatDirty) {
this._isInvProjMatDirty = false;
Matrix.invert(this.projectionMatrix, this._inverseProjectionMatrix);
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/RenderPipeline/BasicRenderPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export class BasicRenderPipeline {
program.uploadAll(program.materialUniformBlock, _backgroundTextureMaterial.shaderData);
program.uploadUngroupTextures();

_backgroundTextureMaterial.renderState._apply(engine);
_backgroundTextureMaterial.renderState._apply(engine, false);
rhi.drawPrimitive(mesh, mesh.subMesh, program);
}

Expand Down Expand Up @@ -263,7 +263,7 @@ export class BasicRenderPipeline {
program.uploadAll(program.materialUniformBlock, shaderData);
program.uploadUngroupTextures();

renderState._apply(engine);
renderState._apply(engine, false);
rhi.drawPrimitive(mesh, mesh.subMesh, program);
}
}
3 changes: 2 additions & 1 deletion packages/core/src/RenderPipeline/RenderQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ export class RenderQueue {
program.uploadUngroupTextures();
}
}
material.renderState._apply(camera.engine);
material.renderState._apply(camera.engine,element.component.entity.transform._isFrontFaceInvert());

rhi.drawPrimitive(element.mesh, element.subMesh, program);
} else {
const spriteElement = <SpriteElement>item;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/RenderPipeline/SpriteBatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class SpriteBatcher extends Basic2DBatcher {
program.uploadAll(program.rendererUniformBlock, renderer.shaderData);
program.uploadAll(program.materialUniformBlock, material.shaderData);

material.renderState._apply(engine);
material.renderState._apply(engine,false);

engine._hardwareRenderer.drawPrimitive(mesh, subMesh, program);

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/RenderPipeline/SpriteMaskBatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class SpriteMaskBatcher extends Basic2DBatcher {
program.uploadAll(program.rendererUniformBlock, renderer.shaderData);
program.uploadAll(program.materialUniformBlock, material.shaderData);

material.renderState._apply(engine);
material.renderState._apply(engine, false);

engine._hardwareRenderer.drawPrimitive(mesh, subMesh, program);
}
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/Transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,17 @@ export class Transform extends Component {
this._updateAllWorldFlag();
}

/**
* @internal
*/
_isFrontFaceInvert(): boolean {
const scale = this.lossyWorldScale;
let isInvert = scale.x < 0;
scale.y < 0 && (isInvert = !isInvert);
scale.z < 0 && (isInvert = !isInvert);
return isInvert;
}

/**
* Get worldMatrix: Will trigger the worldMatrix update of itself and all parent entities.
* Get worldPosition: Will trigger the worldMatrix, local position update of itself and the worldMatrix update of all parent entities.
Expand Down
17 changes: 14 additions & 3 deletions packages/core/src/shader/state/RasterState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ export class RasterState {

/** @internal */
_cullFaceEnable: boolean = true;
/** @internal */
_frontFaceInvert: boolean = false;

/**
* @internal
*/
_apply(hardwareRenderer: IHardwareRenderer, lastRenderState: RenderState): void {
this._platformApply(hardwareRenderer, lastRenderState.rasterState);
_apply(hardwareRenderer: IHardwareRenderer, lastRenderState: RenderState, frontFaceInvert: boolean): void {
this._platformApply(hardwareRenderer, lastRenderState.rasterState, frontFaceInvert);
}

private _platformApply(rhi: IHardwareRenderer, lastState: RasterState): void {
private _platformApply(rhi: IHardwareRenderer, lastState: RasterState, frontFaceInvert: boolean): void {
const gl = <WebGLRenderingContext>rhi.gl;
const { cullMode, depthBias, slopeScaledDepthBias } = this;

Expand All @@ -49,6 +51,15 @@ export class RasterState {
}
}

if (frontFaceInvert !== lastState._frontFaceInvert) {
if (frontFaceInvert) {
gl.frontFace(gl.CW);
} else {
gl.frontFace(gl.CCW);
}
lastState._frontFaceInvert == frontFaceInvert;
}

// apply polygonOffset.
if (depthBias !== lastState.depthBias || slopeScaledDepthBias !== lastState.slopeScaledDepthBias) {
if (depthBias !== 0 || slopeScaledDepthBias !== 0) {
Expand Down
8 changes: 5 additions & 3 deletions packages/core/src/shader/state/RenderState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ export class RenderState {
/** Raster state. */
readonly rasterState: RasterState = new RasterState();

/** @internal */
_apply(engine: Engine): void {
/**
* @internal
*/
_apply(engine: Engine, frontFaceInvert: boolean): void {
const hardwareRenderer = engine._hardwareRenderer;
const lastRenderState = engine._lastRenderState;
this.blendState._apply(hardwareRenderer, lastRenderState);
this.depthState._apply(hardwareRenderer, lastRenderState);
this.stencilState._apply(hardwareRenderer, lastRenderState);
this.rasterState._apply(hardwareRenderer, lastRenderState);
this.rasterState._apply(hardwareRenderer, lastRenderState, frontFaceInvert);
}
}
6 changes: 3 additions & 3 deletions packages/math/src/Matrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,15 +517,15 @@ export class Matrix implements IClone {

/**
* Calculate a perspective projection matrix.
* @param fovy - Field of view in the y direction, in radians
* @param fovY - Field of view in the y direction, in radians
* @param aspect - Aspect ratio, defined as view space width divided by height
* @param near - The depth of the near plane
* @param far - The depth of the far plane
* @param out - The calculated perspective projection matrix
*/
static perspective(fovy: number, aspect: number, near: number, far: number, out: Matrix): void {
static perspective(fovY: number, aspect: number, near: number, far: number, out: Matrix): void {
const oe = out.elements;
const f = 1.0 / Math.tan(fovy / 2);
const f = 1.0 / Math.tan(fovY / 2);
const nf = 1 / (near - far);

oe[0] = f / aspect;
Expand Down