From 57ea0f8c8a9f37f753c29aff841260cc2a43e900 Mon Sep 17 00:00:00 2001 From: ChenMo Date: Mon, 7 Nov 2022 13:12:06 +0800 Subject: [PATCH] Enhanced shadows and fixed shadow and math related bugs (#1156) * fix: BoundingFrustum cull plane('calculateFromMatrix') inverse bug, the plane normal should towards the inside of the frustum, not outward * fix: CollisionUtil.intersectsFrustumAndBoxand CollisionUtil.frustumContainsBox, befor logic is strange with disjoint because of front, not back * feat: add shadowDistance property, shadow quality can be optimized in large scenes * fix: shadow cull planes generate bug due to the confusion of the plane rules above * fix: shadow near plane cull bug not clamp to min ndc z by add max(positionCS.z, -1.0) in shadow caster shader * fix: shadowMode set to None invalid error cause by not disable shadow macro * feat: move shadow config from engine to scene, i think shadow quality config should per scene * feat: modify receiveShadow and castShadow to true in Renderer, there is a master switch in the light * fix: shadow delay rendering 1 frame bug --- packages/core/src/2d/sprite/SpriteMask.ts | 6 +- packages/core/src/2d/sprite/SpriteRenderer.ts | 5 +- packages/core/src/2d/text/TextRenderer.ts | 6 +- packages/core/src/Camera.ts | 27 +- packages/core/src/Engine.ts | 10 - packages/core/src/EngineSettings.ts | 13 - .../src/RenderPipeline/BasicRenderPipeline.ts | 33 ++- .../core/src/RenderPipeline/RenderContext.ts | 29 ++- .../core/src/RenderPipeline/RenderQueue.ts | 11 +- packages/core/src/Renderer.ts | 13 +- packages/core/src/Scene.ts | 55 +++- packages/core/src/lighting/Light.ts | 7 +- packages/core/src/lighting/LightManager.ts | 5 +- packages/core/src/mesh/MeshRenderer.ts | 6 +- .../src/shaderlib/extra/shadow-map.vs.glsl | 8 +- .../src/shaderlib/mobile_blinnphong_frag.glsl | 8 +- .../pbr/direct_irradiance_frag_define.glsl | 4 - .../shaderlib/shadow/shadow_frag_share.glsl | 9 +- .../src/shadow/CascadedShadowCasterPass.ts | 245 +++++++++--------- packages/core/src/shadow/ShadowUtils.ts | 28 +- .../enum/{ShadowMode.ts => ShadowType.ts} | 2 +- packages/core/src/shadow/index.ts | 2 +- packages/core/src/sky/Sky.ts | 16 +- packages/core/src/trail/TrailRenderer.ts | 8 +- packages/math/src/BoundingFrustum.ts | 26 +- packages/math/src/CollisionUtil.ts | 40 +-- 26 files changed, 327 insertions(+), 295 deletions(-) rename packages/core/src/shadow/enum/{ShadowMode.ts => ShadowType.ts} (90%) diff --git a/packages/core/src/2d/sprite/SpriteMask.ts b/packages/core/src/2d/sprite/SpriteMask.ts index b1b8799ea..6fcc0acc4 100644 --- a/packages/core/src/2d/sprite/SpriteMask.ts +++ b/packages/core/src/2d/sprite/SpriteMask.ts @@ -1,9 +1,9 @@ import { BoundingBox } from "@oasis-engine/math"; -import { Camera } from "../../Camera"; import { assignmentClone, ignoreClone } from "../../clone/CloneManager"; import { ICustomClone } from "../../clone/ComponentCloner"; import { Entity } from "../../Entity"; import { Renderer, RendererUpdateFlags } from "../../Renderer"; +import { RenderContext } from "../../RenderPipeline/RenderContext"; import { SpriteMaskElement } from "../../RenderPipeline/SpriteMaskElement"; import { Shader } from "../../shader/Shader"; import { ShaderProperty } from "../../shader/ShaderProperty"; @@ -171,7 +171,7 @@ export class SpriteMask extends Renderer implements ICustomClone { * @override * @inheritdoc */ - _render(camera: Camera): void { + _render(context: RenderContext): void { if (!this.sprite?.texture || !this.width || !this.height) { return; } @@ -191,7 +191,7 @@ export class SpriteMask extends Renderer implements ICustomClone { const spriteMaskElementPool = this._engine._spriteMaskElementPool; const maskElement = spriteMaskElementPool.getFromPool(); maskElement.setValue(this, this._renderData, this.getMaterial()); - camera._renderPipeline._allSpriteMasks.add(this); + context.camera._renderPipeline._allSpriteMasks.add(this); this._maskElement = maskElement; } diff --git a/packages/core/src/2d/sprite/SpriteRenderer.ts b/packages/core/src/2d/sprite/SpriteRenderer.ts index 748398edd..8ccfd97e6 100644 --- a/packages/core/src/2d/sprite/SpriteRenderer.ts +++ b/packages/core/src/2d/sprite/SpriteRenderer.ts @@ -4,6 +4,7 @@ import { assignmentClone, deepClone, ignoreClone } from "../../clone/CloneManage import { ICustomClone } from "../../clone/ComponentCloner"; import { Entity } from "../../Entity"; import { Renderer, RendererUpdateFlags } from "../../Renderer"; +import { RenderContext } from "../../RenderPipeline/RenderContext"; import { CompareFunction } from "../../shader/enums/CompareFunction"; import { Shader } from "../../shader/Shader"; import { ShaderProperty } from "../../shader/ShaderProperty"; @@ -214,7 +215,7 @@ export class SpriteRenderer extends Renderer implements ICustomClone { /** * @internal */ - _render(camera: Camera): void { + _render(context: RenderContext): void { if (!this.sprite?.texture || !this.width || !this.height) { return; } @@ -239,7 +240,7 @@ export class SpriteRenderer extends Renderer implements ICustomClone { for (let i = 0, n = passes.length; i < n; i++) { const spriteElement = this._engine._spriteElementPool.getFromPool(); spriteElement.setValue(this, this._renderData, material, texture, renderStates[i], passes[i]); - camera._renderPipeline.pushPrimitive(spriteElement); + context.camera._renderPipeline.pushPrimitive(spriteElement); } } diff --git a/packages/core/src/2d/text/TextRenderer.ts b/packages/core/src/2d/text/TextRenderer.ts index 9b7d3fab7..ebf1b52aa 100644 --- a/packages/core/src/2d/text/TextRenderer.ts +++ b/packages/core/src/2d/text/TextRenderer.ts @@ -1,10 +1,10 @@ import { BoundingBox, Color, Vector3 } from "@oasis-engine/math"; -import { Camera } from "../../Camera"; import { assignmentClone, deepClone, ignoreClone } from "../../clone/CloneManager"; import { ICustomClone } from "../../clone/ComponentCloner"; import { Engine } from "../../Engine"; import { Entity } from "../../Entity"; import { Renderer } from "../../Renderer"; +import { RenderContext } from "../../RenderPipeline/RenderContext"; import { CompareFunction } from "../../shader/enums/CompareFunction"; import { TransformModifyFlags } from "../../Transform"; import { FontStyle } from "../enums/FontStyle"; @@ -286,7 +286,7 @@ export class TextRenderer extends Renderer implements ICustomClone { /** * @internal */ - _render(camera: Camera): void { + _render(context: RenderContext): void { if ( this._text === "" || (this.enableWrapping && this.width <= 0) || @@ -342,7 +342,7 @@ export class TextRenderer extends Renderer implements ICustomClone { ); charElements[i] = spriteElement; } - camera._renderPipeline.pushPrimitive(textElement); + context.camera._renderPipeline.pushPrimitive(textElement); } /** diff --git a/packages/core/src/Camera.ts b/packages/core/src/Camera.ts index 72baeb974..546eb37d7 100644 --- a/packages/core/src/Camera.ts +++ b/packages/core/src/Camera.ts @@ -8,7 +8,6 @@ import { Entity } from "./Entity"; import { CameraClearFlags } from "./enums/CameraClearFlags"; import { Layer } from "./Layer"; import { BasicRenderPipeline } from "./RenderPipeline/BasicRenderPipeline"; -import { RenderContext } from "./RenderPipeline/RenderContext"; import { ShaderDataGroup } from "./shader/enums/ShaderDataGroup"; import { Shader } from "./shader/Shader"; import { ShaderData } from "./shader/ShaderData"; @@ -30,12 +29,8 @@ class MathTemp { @dependentComponents(Transform) export class Camera extends Component { /** @internal */ - static _vpMatrixProperty = Shader.getPropertyByName("u_VPMat"); - - private static _viewMatrixProperty = Shader.getPropertyByName("u_viewMat"); - private static _projectionMatrixProperty = Shader.getPropertyByName("u_projMat"); private static _inverseViewMatrixProperty = Shader.getPropertyByName("u_viewInvMat"); - private static _inverseProjectionMatrixProperty = Shader.getPropertyByName("u_projInvMat"); + /** @internal */ private static _cameraPositionProperty = Shader.getPropertyByName("u_cameraPos"); /** Shader data. */ @@ -67,6 +62,9 @@ export class Camera extends Component { /** @internal */ @ignoreClone _renderPipeline: BasicRenderPipeline; + /** @internal */ + @deepClone + _viewProjectionMatrix: Matrix = new Matrix(); private _isOrthographic: boolean = false; private _isProjMatSetting = false; @@ -432,16 +430,19 @@ export class Camera extends Component { * @param mipLevel - Set mip level the data want to write, only take effect in webgl2.0 */ render(cubeFace?: TextureCubeFace, mipLevel: number = 0): void { - // compute cull frustum. const context = this.engine._renderContext; - context._setContext(this); + context.camera = this; + + Matrix.multiply(this.projectionMatrix, this.viewMatrix, this._viewProjectionMatrix); + + // compute cull frustum. if (this.enableFrustumCulling && (this._frustumViewChangeFlag.flag || this._isFrustumProjectDirty)) { - this._frustum.calculateFromMatrix(context._viewProjectMatrix); + this._frustum.calculateFromMatrix(this._viewProjectionMatrix); this._frustumViewChangeFlag.flag = false; this._isFrustumProjectDirty = false; } - this._updateShaderData(context); + this._updateShaderData(); // union scene and camera macro. ShaderMacroCollection.unionCollection( @@ -501,13 +502,9 @@ export class Camera extends Component { return out; } - private _updateShaderData(context: RenderContext): void { + private _updateShaderData(): 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._getInverseProjectionMatrix()); shaderData.setVector3(Camera._cameraPositionProperty, this._transform.worldPosition); } diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index 714171d4f..bdae9cb1a 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -1,4 +1,3 @@ -import { Vector3 } from "@oasis-engine/math"; import { Color } from "@oasis-engine/math/src/Color"; import { Font } from "./2d/text/Font"; import { ResourceManager } from "./asset/ResourceManager"; @@ -36,9 +35,6 @@ import { ShaderPass } from "./shader/ShaderPass"; import { ShaderPool } from "./shader/ShaderPool"; import { ShaderProgramPool } from "./shader/ShaderProgramPool"; import { RenderState } from "./shader/state/RenderState"; -import { ShadowCascadesMode } from "./shadow/enum/ShadowCascadesMode"; -import { ShadowMode } from "./shadow/enum/ShadowMode"; -import { ShadowResolution } from "./shadow/enum/ShadowResolution"; import { Texture2D, Texture2DArray, TextureCube, TextureCubeFace, TextureFormat } from "./texture"; ShaderPool.init(); @@ -271,12 +267,6 @@ export class Engine extends EventDispatcher { const colorSpace = settings?.colorSpace || ColorSpace.Linear; colorSpace === ColorSpace.Gamma && this._macroCollection.enable(Engine._gammaMacro); innerSettings.colorSpace = colorSpace; - innerSettings.shadowMode = settings?.shadowMode || ShadowMode.SoftLow; - innerSettings.shadowResolution = settings?.shadowResolution || ShadowResolution.Medium; - innerSettings.shadowCascades = settings?.shadowCascades || ShadowCascadesMode.NoCascades; - innerSettings.shadowTwoCascadeSplits = settings?.shadowTwoCascadeSplits || 1.0 / 3.0; - innerSettings.shadowFourCascadeSplits = - settings?.shadowFourCascadeSplits || new Vector3(1.0 / 15, 3.0 / 15.0, 7.0 / 15.0); } /** diff --git a/packages/core/src/EngineSettings.ts b/packages/core/src/EngineSettings.ts index 638ecd128..bd5a61b92 100644 --- a/packages/core/src/EngineSettings.ts +++ b/packages/core/src/EngineSettings.ts @@ -1,6 +1,4 @@ import { ColorSpace } from "./enums/ColorSpace"; -import { ShadowCascadesMode, ShadowMode, ShadowResolution } from "./shadow"; -import { Vector3 } from "@oasis-engine/math"; /** * Render settings. @@ -8,15 +6,4 @@ import { Vector3 } from "@oasis-engine/math"; export interface EngineSettings { /** Color space.*/ colorSpace?: ColorSpace; - - /** How this light casts shadows */ - shadowMode?: ShadowMode; - /** The resolution of the shadow maps. */ - shadowResolution?: ShadowResolution; - /** Number of cascades to use for directional light shadows. */ - shadowCascades?: ShadowCascadesMode; - /** The splits of two cascade distribution. */ - shadowTwoCascadeSplits?: number; - /** The splits of four cascade distribution. */ - shadowFourCascadeSplits?: Vector3; } diff --git a/packages/core/src/RenderPipeline/BasicRenderPipeline.ts b/packages/core/src/RenderPipeline/BasicRenderPipeline.ts index ff2e43678..becf74a4c 100644 --- a/packages/core/src/RenderPipeline/BasicRenderPipeline.ts +++ b/packages/core/src/RenderPipeline/BasicRenderPipeline.ts @@ -13,7 +13,7 @@ import { RenderQueueType } from "../shader/enums/RenderQueueType"; import { Shader } from "../shader/Shader"; import { ShaderMacroCollection } from "../shader/ShaderMacroCollection"; import { CascadedShadowCasterPass } from "../shadow/CascadedShadowCasterPass"; -import { ShadowMode } from "../shadow/enum/ShadowMode"; +import { ShadowType } from "../shadow/enum/ShadowType"; import { RenderTarget, TextureCubeFace } from "../texture"; import { RenderContext } from "./RenderContext"; import { RenderElement } from "./RenderElement"; @@ -141,31 +141,39 @@ export class BasicRenderPipeline { */ render(context: RenderContext, cubeFace?: TextureCubeFace, mipLevel?: number) { const camera = this._camera; + const scene = camera.scene; const opaqueQueue = this._opaqueQueue; const alphaTestQueue = this._alphaTestQueue; const transparentQueue = this._transparentQueue; camera.engine._spriteMaskManager.clear(); - if (camera.engine.settings.shadowMode !== ShadowMode.None) { - this._cascadedShadowCaster._render(); + if (scene.castShadows && scene._sunLight?.shadowType !== ShadowType.None) { + this._cascadedShadowCaster._render(context); } - opaqueQueue.clear(); alphaTestQueue.clear(); transparentQueue.clear(); this._allSpriteMasks.length = 0; + context.applyViewProjectMatrix(camera.viewMatrix, camera.projectionMatrix, camera._viewProjectionMatrix); + this._callRender(context); opaqueQueue.sort(RenderQueue._compareFromNearToFar); alphaTestQueue.sort(RenderQueue._compareFromNearToFar); transparentQueue.sort(RenderQueue._compareFromFarToNear); for (let i = 0, len = this._renderPassArray.length; i < len; i++) { - this._drawRenderPass(this._renderPassArray[i], camera, cubeFace, mipLevel); + this._drawRenderPass(context, this._renderPassArray[i], camera, cubeFace, mipLevel); } } - private _drawRenderPass(pass: RenderPass, camera: Camera, cubeFace?: TextureCubeFace, mipLevel?: number) { + private _drawRenderPass( + context: RenderContext, + pass: RenderPass, + camera: Camera, + cubeFace?: TextureCubeFace, + mipLevel?: number + ) { pass.preRender(camera, this._opaqueQueue, this._alphaTestQueue, this._transparentQueue); if (pass.enabled) { @@ -184,16 +192,16 @@ export class BasicRenderPipeline { if (pass.renderOverride) { pass.render(camera, this._opaqueQueue, this._alphaTestQueue, this._transparentQueue); } else { - this._opaqueQueue.render(camera, pass.replaceMaterial, pass.mask); - this._alphaTestQueue.render(camera, pass.replaceMaterial, pass.mask); + this._opaqueQueue.render(camera, pass.replaceMaterial, pass.mask, null); + this._alphaTestQueue.render(camera, pass.replaceMaterial, pass.mask, null); if (camera.clearFlags & CameraClearFlags.Color) { if (background.mode === BackgroundMode.Sky) { - background.sky._render(camera); + background.sky._render(context); } else if (background.mode === BackgroundMode.Texture && background.texture) { this._drawBackgroundTexture(engine, background); } } - this._transparentQueue.render(camera, pass.replaceMaterial, pass.mask); + this._transparentQueue.render(camera, pass.replaceMaterial, pass.mask, null); } renderTarget?._blitRenderTarget(); @@ -245,7 +253,7 @@ export class BasicRenderPipeline { private _callRender(context: RenderContext): void { const renderers = this._camera.engine._componentsManager._renderers; - const camera = context._camera; + const camera = context.camera; const elements = renderers._elements; for (let i = renderers.length - 1; i >= 0; --i) { const renderer = elements[i]; @@ -275,8 +283,7 @@ export class BasicRenderPipeline { } renderer._updateShaderData(context); - - renderer._render(camera); + renderer._render(context); // union camera global macro and renderer macro. ShaderMacroCollection.unionCollection( diff --git a/packages/core/src/RenderPipeline/RenderContext.ts b/packages/core/src/RenderPipeline/RenderContext.ts index 3e10bae04..cb481503c 100644 --- a/packages/core/src/RenderPipeline/RenderContext.ts +++ b/packages/core/src/RenderPipeline/RenderContext.ts @@ -1,20 +1,31 @@ import { Matrix } from "@oasis-engine/math"; import { Camera } from "../Camera"; +import { Shader } from "../shader"; /** + * @internal * Rendering context. */ export class RenderContext { /** @internal */ - _camera: Camera; - /** @internal */ - _viewProjectMatrix: Matrix = new Matrix(); + static _vpMatrixProperty = Shader.getPropertyByName("u_VPMat"); - /** - * @internal - */ - _setContext(camera: Camera): void { - this._camera = camera; - Matrix.multiply(camera.projectionMatrix, camera.viewMatrix, this._viewProjectMatrix); + private static _viewMatrixProperty = Shader.getPropertyByName("u_viewMat"); + private static _projectionMatrixProperty = Shader.getPropertyByName("u_projMat"); + + camera: Camera; + viewMatrix: Matrix; + projectionMatrix: Matrix; + viewProjectMatrix: Matrix; + + applyViewProjectMatrix(viewMatrix: Matrix, projectionMatrix: Matrix, viewProjectMatrix: Matrix): void { + const shaderData = this.camera.shaderData; + shaderData.setMatrix(RenderContext._viewMatrixProperty, viewMatrix); + shaderData.setMatrix(RenderContext._projectionMatrixProperty, projectionMatrix); + shaderData.setMatrix(RenderContext._vpMatrixProperty, viewProjectMatrix); + + this.viewMatrix = viewMatrix; + this.projectionMatrix = projectionMatrix; + this.viewProjectMatrix = viewProjectMatrix; } } diff --git a/packages/core/src/RenderPipeline/RenderQueue.ts b/packages/core/src/RenderPipeline/RenderQueue.ts index fb8ed9df1..27f742d07 100644 --- a/packages/core/src/RenderPipeline/RenderQueue.ts +++ b/packages/core/src/RenderPipeline/RenderQueue.ts @@ -41,7 +41,7 @@ export class RenderQueue { this.items.push(element); } - render(camera: Camera, replaceMaterial: Material, mask: Layer) { + render(camera: Camera, replaceMaterial: Material, mask: Layer, customShader: Shader): void { const items = this.items; if (items.length === 0) { return; @@ -82,10 +82,11 @@ export class RenderQueue { ); // @todo: temporary solution - const program = (replaceMaterial?.shader.passes[0] || element.shaderPass)._getShaderProgram( - engine, - compileMacros - ); + const program = ( + customShader?.passes[0] || + replaceMaterial?.shader.passes[0] || + element.shaderPass + )._getShaderProgram(engine, compileMacros); if (!program.isValid) { continue; diff --git a/packages/core/src/Renderer.ts b/packages/core/src/Renderer.ts index 39e3d55ab..c36a011e8 100644 --- a/packages/core/src/Renderer.ts +++ b/packages/core/src/Renderer.ts @@ -1,5 +1,4 @@ import { BoundingBox, Matrix } from "@oasis-engine/math"; -import { Camera } from "./Camera"; import { assignmentClone, deepClone, ignoreClone, shallowClone } from "./clone/CloneManager"; import { Component } from "./Component"; import { dependentComponents } from "./ComponentsDependencies"; @@ -69,7 +68,7 @@ export class Renderer extends Component { @ignoreClone private _priority: number = 0; @assignmentClone - private _receiveShadows: boolean = false; + private _receiveShadows: boolean = true; /** * Whether receive shadow. @@ -90,7 +89,7 @@ export class Renderer extends Component { } /** Whether cast shadow. */ - castShadows: boolean = false; + castShadows: boolean = true; /** * Material count. @@ -139,6 +138,8 @@ export class Renderer extends Component { this.shaderData._addRefCount(1); this._onTransformChanged = this._onTransformChanged.bind(this); this._registerEntityTransformListener(); + + this.shaderData.enableMacro(Renderer._receiveShadowMacro); } /** @@ -297,7 +298,7 @@ export class Renderer extends Component { /** * @internal */ - _render(camera: Camera): void { + _render(context: RenderContext): void { throw "not implement"; } @@ -323,8 +324,8 @@ export class Renderer extends Component { const mvInvMatrix = this._mvInvMatrix; const normalMatrix = this._normalMatrix; - Matrix.multiply(context._camera.viewMatrix, worldMatrix, mvMatrix); - Matrix.multiply(context._viewProjectMatrix, worldMatrix, mvpMatrix); + Matrix.multiply(context.viewMatrix, worldMatrix, mvMatrix); + Matrix.multiply(context.viewProjectMatrix, worldMatrix, mvpMatrix); Matrix.invert(mvMatrix, mvInvMatrix); Matrix.invert(worldMatrix, normalMatrix); normalMatrix.transpose(); diff --git a/packages/core/src/Scene.ts b/packages/core/src/Scene.ts index 25ff8ac79..b257dc9de 100644 --- a/packages/core/src/Scene.ts +++ b/packages/core/src/Scene.ts @@ -1,12 +1,17 @@ +import { Vector3 } from "@oasis-engine/math"; import { Background } from "./Background"; import { EngineObject, Logger } from "./base"; import { Camera } from "./Camera"; import { Engine } from "./Engine"; import { Entity } from "./Entity"; +import { Light } from "./lighting"; import { AmbientLight } from "./lighting/AmbientLight"; import { ShaderDataGroup } from "./shader/enums/ShaderDataGroup"; import { ShaderData } from "./shader/ShaderData"; import { ShaderMacroCollection } from "./shader/ShaderMacroCollection"; +import { ShadowCascadesMode } from "./shadow/enum/ShadowCascadesMode"; +import { ShadowType } from "./shadow/enum/ShadowType"; +import { ShadowResolution } from "./shadow/enum/ShadowResolution"; /** * Scene. @@ -17,10 +22,20 @@ export class Scene extends EngineObject { /** The background of the scene. */ readonly background: Background = new Background(this._engine); - /** Scene-related shader data. */ readonly shaderData: ShaderData = new ShaderData(ShaderDataGroup.Scene); + /** If cast shadows. */ + castShadows: boolean = true; + /** The resolution of the shadow maps. */ + shadowResolution: ShadowResolution = ShadowResolution.Medium; + /** The splits of two cascade distribution. */ + shadowTwoCascadeSplits: number = 1.0 / 3.0; + /** The splits of four cascade distribution. */ + shadowFourCascadeSplits: Vector3 = new Vector3(1.0 / 15, 3.0 / 15.0, 7.0 / 15.0); + /** Max Shadow distance. */ + shadowDistance: number = 50; + /** @internal */ _activeCameras: Camera[] = []; /** @internal */ @@ -29,9 +44,26 @@ export class Scene extends EngineObject { _globalShaderMacro: ShaderMacroCollection = new ShaderMacroCollection(); /** @internal */ _rootEntities: Entity[] = []; + /** @internal */ + _sunLight: Light; + private _shadowCascades: ShadowCascadesMode = ShadowCascadesMode.NoCascades; private _ambientLight: AmbientLight; + /** + * Number of cascades to use for directional light shadows. + */ + get shadowCascades(): ShadowCascadesMode { + return this._shadowCascades; + } + + set shadowCascades(value: ShadowCascadesMode) { + if (this._shadowCascades !== value) { + this.shaderData.enableMacro("CASCADED_COUNT", value.toString()); + this._shadowCascades = value; + } + } + /** * Ambient light. */ @@ -80,6 +112,8 @@ export class Scene extends EngineObject { shaderData._addRefCount(1); this.ambientLight = new AmbientLight(); engine.sceneManager._allScenes.push(this); + + this.shaderData.enableMacro("CASCADED_COUNT", this.shadowCascades.toString()); } /** @@ -261,11 +295,26 @@ export class Scene extends EngineObject { * @internal */ _updateShaderData(): void { - this._engine._lightManager._updateShaderData(this.shaderData); + const shaderData = this.shaderData; + const lightManager = this._engine._lightManager; + + lightManager._updateShaderData(this.shaderData); + const sunLightIndex = lightManager._getSunLightIndex(); + if (sunLightIndex !== -1) { + this._sunLight = lightManager._directLights.get(sunLightIndex); + } + + if (this.castShadows && this._sunLight && this._sunLight.shadowType !== ShadowType.None) { + shaderData.enableMacro("CASCADED_SHADOW_MAP"); + this.shaderData.enableMacro("SHADOW_MODE", this._sunLight.shadowType.toString()); + } else { + shaderData.disableMacro("CASCADED_SHADOW_MAP"); + } + // union scene and camera macro. ShaderMacroCollection.unionCollection( this.engine._macroCollection, - this.shaderData._macroCollection, + shaderData._macroCollection, this._globalShaderMacro ); } diff --git a/packages/core/src/lighting/Light.ts b/packages/core/src/lighting/Light.ts index e7c84bb82..175d16e5f 100644 --- a/packages/core/src/lighting/Light.ts +++ b/packages/core/src/lighting/Light.ts @@ -1,6 +1,7 @@ import { Color, Matrix } from "@oasis-engine/math"; import { ignoreClone } from "../clone/CloneManager"; import { Component } from "../Component"; +import { ShadowType } from "../shadow"; /** * Light base class. @@ -14,12 +15,12 @@ export abstract class Light extends Component { /** Light Intensity */ intensity: number = 1; - /** whether enable shadow */ - enableShadow: boolean = false; + /** How this light casts shadows. */ + shadowType: ShadowType = ShadowType.None; /** Shadow bias.*/ shadowBias: number = 1; /** Shadow mapping normal-based bias. */ - shadowNormalBias: number = 0; + shadowNormalBias: number = 1; /** Near plane value to use for shadow frustums. */ shadowNearPlane: number = 0.1; /** Shadow intensity, the larger the value, the clearer and darker the shadow. */ diff --git a/packages/core/src/lighting/LightManager.ts b/packages/core/src/lighting/LightManager.ts index ea41f1625..700c887b1 100644 --- a/packages/core/src/lighting/LightManager.ts +++ b/packages/core/src/lighting/LightManager.ts @@ -1,5 +1,6 @@ import { DisorderedArray } from "../DisorderedArray"; import { ShaderData } from "../shader"; +import { ShadowType } from "../shadow"; import { DirectLight } from "./DirectLight"; import { PointLight } from "./PointLight"; import { SpotLight } from "./SpotLight"; @@ -77,13 +78,13 @@ export class LightManager { let hasShadowLight = false; for (let i = 0, n = directLights.length; i < n; i++) { const directLight = directLights.get(i); - if (directLight.enableShadow && !hasShadowLight) { + if (directLight.shadowType !== ShadowType.None && !hasShadowLight) { maxIntensity = Number.NEGATIVE_INFINITY; hasShadowLight = true; } const intensity = directLight.intensity * directLight.color.getBrightness(); if (hasShadowLight) { - if (directLight.enableShadow && maxIntensity < intensity) { + if (directLight.shadowType !== ShadowType.None && maxIntensity < intensity) { maxIntensity = intensity; sunLightIndex = i; } diff --git a/packages/core/src/mesh/MeshRenderer.ts b/packages/core/src/mesh/MeshRenderer.ts index b47c8278e..a5d1a7cdb 100644 --- a/packages/core/src/mesh/MeshRenderer.ts +++ b/packages/core/src/mesh/MeshRenderer.ts @@ -1,11 +1,11 @@ import { BoundingBox } from "@oasis-engine/math"; import { Logger } from "../base/Logger"; -import { Camera } from "../Camera"; import { ignoreClone } from "../clone/CloneManager"; import { ICustomClone } from "../clone/ComponentCloner"; import { Entity } from "../Entity"; import { Mesh, MeshModifyFlags } from "../graphic/Mesh"; import { Renderer, RendererUpdateFlags } from "../Renderer"; +import { RenderContext } from "../RenderPipeline/RenderContext"; import { Shader } from "../shader/Shader"; /** @@ -47,7 +47,7 @@ export class MeshRenderer extends Renderer implements ICustomClone { * @internal * @override */ - _render(camera: Camera): void { + _render(context: RenderContext): void { const mesh = this._mesh; if (mesh) { if (this._dirtyUpdateFlag & MeshRendererUpdateFlags.VertexElementMacro) { @@ -83,7 +83,7 @@ export class MeshRenderer extends Renderer implements ICustomClone { } const subMeshes = mesh.subMeshes; - const renderPipeline = camera._renderPipeline; + const renderPipeline = context.camera._renderPipeline; const renderElementPool = this._engine._renderElementPool; for (let i = 0, n = subMeshes.length; i < n; i++) { const material = this._materials[i]; diff --git a/packages/core/src/shaderlib/extra/shadow-map.vs.glsl b/packages/core/src/shaderlib/extra/shadow-map.vs.glsl index 90b627764..d1a2f082a 100644 --- a/packages/core/src/shaderlib/extra/shadow-map.vs.glsl +++ b/packages/core/src/shaderlib/extra/shadow-map.vs.glsl @@ -2,7 +2,7 @@ #include #include #include -uniform mat4 u_lightViewProjMat; +uniform mat4 u_VPMat; uniform vec2 u_shadowBias; // x: depth bias, y: normal bias uniform vec3 u_lightDirection; @@ -35,6 +35,10 @@ void main() { #endif #endif - gl_Position = u_lightViewProjMat * vec4(v_pos, 1.0); + + vec4 positionCS = u_VPMat * vec4(v_pos, 1.0); + positionCS.z = max(positionCS.z, -1.0);// clamp to min ndc z + + gl_Position = positionCS; } diff --git a/packages/core/src/shaderlib/mobile_blinnphong_frag.glsl b/packages/core/src/shaderlib/mobile_blinnphong_frag.glsl index d6112bcc1..012c76588 100644 --- a/packages/core/src/shaderlib/mobile_blinnphong_frag.glsl +++ b/packages/core/src/shaderlib/mobile_blinnphong_frag.glsl @@ -11,23 +11,19 @@ #ifdef O3_DIRECT_LIGHT_COUNT shadowAttenuation = 1.0; -#ifdef OASIS_CALCULATE_SHADOWS - #ifdef CASCADED_SHADOW_MAP + #ifdef OASIS_CALCULATE_SHADOWS shadowAttenuation *= sampleShadowMap(); int sunIndex = int(u_shadowInfo.z); #endif -#endif DirectLight directionalLight; for( int i = 0; i < O3_DIRECT_LIGHT_COUNT; i++ ) { directionalLight.color = u_directLightColor[i]; -#ifdef OASIS_CALCULATE_SHADOWS - #ifdef CASCADED_SHADOW_MAP + #ifdef OASIS_CALCULATE_SHADOWS if (i == sunIndex) { directionalLight.color *= shadowAttenuation; } #endif -#endif directionalLight.direction = u_directLightDirection[i]; float d = max(dot(N, -directionalLight.direction), 0.0); diff --git a/packages/core/src/shaderlib/pbr/direct_irradiance_frag_define.glsl b/packages/core/src/shaderlib/pbr/direct_irradiance_frag_define.glsl index 65dbc8fe7..3a73fe47c 100644 --- a/packages/core/src/shaderlib/pbr/direct_irradiance_frag_define.glsl +++ b/packages/core/src/shaderlib/pbr/direct_irradiance_frag_define.glsl @@ -78,21 +78,17 @@ void addTotalDirectRadiance(Geometry geometry, Material material, inout Reflecte #ifdef O3_DIRECT_LIGHT_COUNT shadowAttenuation = 1.0; #ifdef OASIS_CALCULATE_SHADOWS - #ifdef CASCADED_SHADOW_MAP shadowAttenuation *= sampleShadowMap(); int sunIndex = int(u_shadowInfo.z); - #endif #endif DirectLight directionalLight; for ( int i = 0; i < O3_DIRECT_LIGHT_COUNT; i ++ ) { directionalLight.color = u_directLightColor[i]; #ifdef OASIS_CALCULATE_SHADOWS - #ifdef CASCADED_SHADOW_MAP if (i == sunIndex) { directionalLight.color *= shadowAttenuation; } - #endif #endif directionalLight.direction = u_directLightDirection[i]; addDirectionalDirectLightRadiance( directionalLight, geometry, material, reflectedLight ); diff --git a/packages/core/src/shaderlib/shadow/shadow_frag_share.glsl b/packages/core/src/shaderlib/shadow/shadow_frag_share.glsl index 1dddfca58..75bb83397 100644 --- a/packages/core/src/shaderlib/shadow/shadow_frag_share.glsl +++ b/packages/core/src/shaderlib/shadow/shadow_frag_share.glsl @@ -1,6 +1,8 @@ -#ifdef CASCADED_SHADOW_MAP - #ifdef OASIS_RECEIVE_SHADOWS - #define OASIS_CALCULATE_SHADOWS +#if defined(CASCADED_SHADOW_MAP)&&defined(OASIS_RECEIVE_SHADOWS) + #define OASIS_CALCULATE_SHADOWS +#endif + +#ifdef OASIS_CALCULATE_SHADOWS // intensity, resolution, sunIndex uniform vec3 u_shadowInfo; @@ -164,5 +166,4 @@ } return attenuation; } - #endif #endif \ No newline at end of file diff --git a/packages/core/src/shadow/CascadedShadowCasterPass.ts b/packages/core/src/shadow/CascadedShadowCasterPass.ts index 1dbbe2529..81febfab7 100644 --- a/packages/core/src/shadow/CascadedShadowCasterPass.ts +++ b/packages/core/src/shadow/CascadedShadowCasterPass.ts @@ -1,11 +1,12 @@ import { Color, MathUtil, Matrix, Vector3 } from "@oasis-engine/math"; import { Vector2 } from "@oasis-engine/math/src"; +import { GLCapabilityType } from "../base/Constant"; import { Camera } from "../Camera"; import { Engine } from "../Engine"; import { CameraClearFlags } from "../enums/CameraClearFlags"; import { Layer } from "../Layer"; import { DirectLight } from "../lighting"; -import { Material } from "../material"; +import { RenderContext } from "../RenderPipeline/RenderContext"; import { RenderQueue } from "../RenderPipeline/RenderQueue"; import { Shader } from "../shader"; import { TextureDepthCompareFunction } from "../texture/enums/TextureDepthCompareFunction"; @@ -14,16 +15,13 @@ import { TextureWrapMode } from "../texture/enums/TextureWrapMode"; import { RenderTarget } from "../texture/RenderTarget"; import { Texture2D } from "../texture/Texture2D"; import { ShadowCascadesMode } from "./enum/ShadowCascadesMode"; -import { ShadowMode } from "./enum/ShadowMode"; import { ShadowSliceData } from "./ShadowSliceData"; import { ShadowUtils } from "./ShadowUtils"; -import { GLCapabilityType } from "../base/Constant"; /** * Cascade shadow caster. */ export class CascadedShadowCasterPass { - private static _lightViewProjMatProperty = Shader.getPropertyByName("u_lightViewProjMat"); private static _lightShadowBiasProperty = Shader.getPropertyByName("u_shadowBias"); private static _lightDirectionProperty = Shader.getPropertyByName("u_lightDirection"); @@ -41,10 +39,9 @@ export class CascadedShadowCasterPass { private readonly _camera: Camera; private readonly _engine: Engine; - private readonly _shadowMapMaterial: Material; + private readonly _shadowCasterShader: Shader; private readonly _supportDepthTexture: boolean; - private _shadowMode: ShadowMode; private _shadowMapResolution: number; private _shadowMapSize: Vector2 = new Vector2(); private _shadowTileResolution: number; @@ -71,30 +68,27 @@ export class CascadedShadowCasterPass { this._engine = camera.engine; this._supportDepthTexture = camera.engine._hardwareRenderer.canIUse(GLCapabilityType.depthTexture); - this._shadowMapMaterial = new Material(this._engine, Shader.find("shadow-map")); + this._shadowCasterShader = Shader.find("shadow-map"); } /** * @internal */ - _render(): void { + _render(context: RenderContext): void { this._updateShadowSettings(); this._existShadowMap = false; - this._renderDirectShadowMap(); + this._renderDirectShadowMap(context); if (this._existShadowMap) { this._updateReceiversShaderData(); - this._camera.scene.shaderData.enableMacro("CASCADED_SHADOW_MAP"); - } else { - this._camera.scene.shaderData.disableMacro("CASCADED_SHADOW_MAP"); } } - private _renderDirectShadowMap(): void { + private _renderDirectShadowMap(context: RenderContext): void { const { _engine: engine, _camera: camera, - _shadowMapMaterial: shadowMapMaterial, + _shadowCasterShader: shadowCasterShader, _viewportOffsets: viewports, _shadowSliceData: shadowSliceData, _splitBoundSpheres: splitBoundSpheres, @@ -109,7 +103,7 @@ export class CascadedShadowCasterPass { const componentsManager = engine._componentsManager; const rhi = engine._hardwareRenderer; - const shadowCascades = engine.settings.shadowCascades; + const shadowCascades = camera.scene.shadowCascades; const splitDistance = CascadedShadowCasterPass._cascadesSplitDistance; const boundSphere = shadowSliceData.splitBoundSphere; const lightWorld = CascadedShadowCasterPass._tempMatrix0; @@ -118,141 +112,136 @@ export class CascadedShadowCasterPass { const lightSide = this._lightSide; const lightForward = this._lightForward; - const lights = engine._lightManager._directLights; const sunLightIndex = engine._lightManager._getSunLightIndex(); - this._getCascadesSplitDistance(); if (sunLightIndex !== -1) { - const light = lights.get(sunLightIndex); - if (light.enableShadow) { - // prepare render target - const renderTarget = this._getAvailableRenderTarget(); - rhi.activeRenderTarget(renderTarget, null, 0); - if (this._supportDepthTexture) { - rhi.clearRenderTarget(engine, CameraClearFlags.Depth, null); - } else { - rhi.clearRenderTarget(engine, CameraClearFlags.All, CascadedShadowCasterPass._clearColor); - } - this._shadowInfos.x = light.shadowStrength; - this._shadowInfos.y = this._shadowTileResolution; - this._shadowInfos.z = sunLightIndex; - - // prepare light and camera direction - Matrix.rotationQuaternion(light.entity.transform.worldRotationQuaternion, lightWorld); - lightSide.set(lightWorldE[0], lightWorldE[1], lightWorldE[2]); - lightUp.set(lightWorldE[4], lightWorldE[5], lightWorldE[6]); - lightForward.set(-lightWorldE[8], -lightWorldE[9], -lightWorldE[10]); - camera.entity.transform.getWorldForward(CascadedShadowCasterPass._tempVector); - - for (let j = 0; j < shadowCascades; j++) { - ShadowUtils.getBoundSphereByFrustum( - splitDistance[j], - splitDistance[j + 1], - camera, - CascadedShadowCasterPass._tempVector.normalize(), - shadowSliceData - ); - ShadowUtils.getDirectionLightShadowCullPlanes( - camera._frustum, - splitDistance[j], - camera.nearClipPlane, - lightForward, - shadowSliceData - ); - ShadowUtils.getDirectionalLightMatrices( - lightUp, - lightSide, - lightForward, - j, - light.shadowNearPlane, - this._shadowTileResolution, - shadowSliceData - ); - this._updateSingleShadowCasterShaderData(light, shadowSliceData); - - // upload pre-cascade infos. - const center = boundSphere.center; - const radius = boundSphere.radius; - const offset = j * 4; - splitBoundSpheres[offset] = center.x; - splitBoundSpheres[offset + 1] = center.y; - splitBoundSpheres[offset + 2] = center.z; - splitBoundSpheres[offset + 3] = radius * radius; - vpMatrix.set(shadowSliceData.viewProjectMatrix.elements, 16 * j); - - opaqueQueue.clear(); - alphaTestQueue.clear(); - transparentQueue.clear(); - const renderers = componentsManager._renderers; - const elements = renderers._elements; - for (let k = renderers.length - 1; k >= 0; --k) { - ShadowUtils.shadowCullFrustum(camera, elements[k], shadowSliceData); - } - opaqueQueue.sort(RenderQueue._compareFromNearToFar); - alphaTestQueue.sort(RenderQueue._compareFromNearToFar); - - const viewport = viewports[j]; - rhi.viewport(viewport.x, viewport.y, this._shadowTileResolution, this._shadowTileResolution); - engine._renderCount++; - - opaqueQueue.render(camera, shadowMapMaterial, Layer.Everything); - alphaTestQueue.render(camera, shadowMapMaterial, Layer.Everything); - } - this._existShadowMap = true; + const light = camera.scene._sunLight; + const shadowFar = Math.min(camera.scene.shadowDistance, camera.farClipPlane); + this._getCascadesSplitDistance(shadowFar); + // prepare render target + const renderTarget = this._getAvailableRenderTarget(); + rhi.activeRenderTarget(renderTarget, null, 0); + if (this._supportDepthTexture) { + rhi.clearRenderTarget(engine, CameraClearFlags.Depth, null); + } else { + rhi.clearRenderTarget(engine, CameraClearFlags.All, CascadedShadowCasterPass._clearColor); } + this._shadowInfos.x = light.shadowStrength; + this._shadowInfos.y = this._shadowTileResolution; + this._shadowInfos.z = sunLightIndex; + + // prepare light and camera direction + Matrix.rotationQuaternion(light.entity.transform.worldRotationQuaternion, lightWorld); + lightSide.set(lightWorldE[0], lightWorldE[1], lightWorldE[2]); + lightUp.set(lightWorldE[4], lightWorldE[5], lightWorldE[6]); + lightForward.set(-lightWorldE[8], -lightWorldE[9], -lightWorldE[10]); + camera.entity.transform.getWorldForward(CascadedShadowCasterPass._tempVector); + + for (let j = 0; j < shadowCascades; j++) { + ShadowUtils.getBoundSphereByFrustum( + splitDistance[j], + splitDistance[j + 1], + camera, + CascadedShadowCasterPass._tempVector.normalize(), + shadowSliceData + ); + ShadowUtils.getDirectionLightShadowCullPlanes( + camera._frustum, + splitDistance[j], + camera.nearClipPlane, + lightForward, + shadowSliceData + ); + + ShadowUtils.getDirectionalLightMatrices( + lightUp, + lightSide, + lightForward, + j, + light.shadowNearPlane, + this._shadowTileResolution, + shadowSliceData + ); + this._updateSingleShadowCasterShaderData(light, shadowSliceData, context); + + // upload pre-cascade infos. + const center = boundSphere.center; + const radius = boundSphere.radius; + const offset = j * 4; + splitBoundSpheres[offset] = center.x; + splitBoundSpheres[offset + 1] = center.y; + splitBoundSpheres[offset + 2] = center.z; + splitBoundSpheres[offset + 3] = radius * radius; + vpMatrix.set(shadowSliceData.viewProjectMatrix.elements, 16 * j); + + opaqueQueue.clear(); + alphaTestQueue.clear(); + transparentQueue.clear(); + const renderers = componentsManager._renderers; + const elements = renderers._elements; + for (let k = renderers.length - 1; k >= 0; --k) { + ShadowUtils.shadowCullFrustum(context, elements[k], shadowSliceData); + } + opaqueQueue.sort(RenderQueue._compareFromNearToFar); + alphaTestQueue.sort(RenderQueue._compareFromNearToFar); + + const viewport = viewports[j]; + rhi.viewport(viewport.x, viewport.y, this._shadowTileResolution, this._shadowTileResolution); + engine._renderCount++; + + opaqueQueue.render(camera, null, Layer.Everything, shadowCasterShader); + alphaTestQueue.render(camera, null, Layer.Everything, shadowCasterShader); + } + this._existShadowMap = true; } } private _updateReceiversShaderData(): void { const splitBoundSpheres = this._splitBoundSpheres; - const shadowCascades = this._engine.settings.shadowCascades; + const scene = this._camera.scene; + const shadowCascades = scene.shadowCascades; for (let i = shadowCascades * 4, n = 4 * 4; i < n; i++) { splitBoundSpheres[i] = 0.0; } - const shaderData = this._camera.scene.shaderData; + const shaderData = scene.shaderData; shaderData.setFloatArray(CascadedShadowCasterPass._viewProjMatFromLightProperty, this._vpMatrix); shaderData.setVector3(CascadedShadowCasterPass._shadowInfosProperty, this._shadowInfos); shaderData.setTexture(CascadedShadowCasterPass._shadowMapsProperty, this._depthTexture); shaderData.setFloatArray(CascadedShadowCasterPass._shadowSplitSpheresProperty, this._splitBoundSpheres); } - private _getCascadesSplitDistance(): void { - const { shadowTwoCascadeSplits, shadowFourCascadeSplits, shadowCascades } = this._engine.settings; - const camera = this._camera; - const { nearClipPlane, farClipPlane, aspectRatio, fieldOfView } = camera; + private _getCascadesSplitDistance(shadowFar: number): void { + const cascadesSplitDistance = CascadedShadowCasterPass._cascadesSplitDistance; + const { shadowTwoCascadeSplits, shadowFourCascadeSplits, shadowCascades } = this._camera.scene; + const { nearClipPlane, aspectRatio, fieldOfView } = this._camera; - CascadedShadowCasterPass._cascadesSplitDistance[0] = nearClipPlane; - const range = farClipPlane - nearClipPlane; + cascadesSplitDistance[0] = nearClipPlane; + const range = shadowFar - nearClipPlane; const tFov = Math.tan(MathUtil.degreeToRadian(fieldOfView) * 0.5); const denominator = 1.0 + tFov * tFov * (aspectRatio * aspectRatio + 1.0); switch (shadowCascades) { case ShadowCascadesMode.NoCascades: - CascadedShadowCasterPass._cascadesSplitDistance[1] = this._getFarWithRadius(farClipPlane, denominator); + cascadesSplitDistance[1] = this._getFarWithRadius(shadowFar, denominator); break; - case ShadowCascadesMode.TwoCascades: - CascadedShadowCasterPass._cascadesSplitDistance[1] = this._getFarWithRadius( - nearClipPlane + range * shadowTwoCascadeSplits, - denominator - ); - CascadedShadowCasterPass._cascadesSplitDistance[2] = this._getFarWithRadius(farClipPlane, denominator); + cascadesSplitDistance[1] = this._getFarWithRadius(nearClipPlane + range * shadowTwoCascadeSplits, denominator); + cascadesSplitDistance[2] = this._getFarWithRadius(shadowFar, denominator); break; - case ShadowCascadesMode.FourCascades: - CascadedShadowCasterPass._cascadesSplitDistance[1] = this._getFarWithRadius( + cascadesSplitDistance[1] = this._getFarWithRadius( nearClipPlane + range * shadowFourCascadeSplits.x, denominator ); - CascadedShadowCasterPass._cascadesSplitDistance[2] = this._getFarWithRadius( + cascadesSplitDistance[2] = this._getFarWithRadius( nearClipPlane + range * shadowFourCascadeSplits.y, denominator ); - CascadedShadowCasterPass._cascadesSplitDistance[3] = this._getFarWithRadius( + cascadesSplitDistance[3] = this._getFarWithRadius( nearClipPlane + range * shadowFourCascadeSplits.z, denominator ); - CascadedShadowCasterPass._cascadesSplitDistance[4] = this._getFarWithRadius(farClipPlane, denominator); + cascadesSplitDistance[4] = this._getFarWithRadius(shadowFar, denominator); break; } } @@ -292,19 +281,10 @@ export class CascadedShadowCasterPass { } private _updateShadowSettings(): void { - const sceneShaderData = this._camera.scene.shaderData; - const settings = this._engine.settings; - const shadowFormat = ShadowUtils.shadowDepthFormat(settings.shadowResolution, this._supportDepthTexture); - const shadowResolution = ShadowUtils.shadowResolution(settings.shadowResolution); - const shadowCascades = settings.shadowCascades; - if (shadowCascades !== this._shadowCascadeMode) { - sceneShaderData.enableMacro("CASCADED_COUNT", shadowCascades.toString()); - } - const shadowMode = settings.shadowMode; - if (shadowMode !== this._shadowMode) { - sceneShaderData.enableMacro("SHADOW_MODE", shadowMode.toString()); - this._shadowMode = shadowMode; - } + const scene = this._camera.scene; + const shadowFormat = ShadowUtils.shadowDepthFormat(scene.shadowResolution, this._supportDepthTexture); + const shadowResolution = ShadowUtils.shadowResolution(scene.shadowResolution); + const shadowCascades = scene.shadowCascades; if ( shadowFormat !== this._shadowMapFormat || @@ -352,7 +332,11 @@ export class CascadedShadowCasterPass { } } - private _updateSingleShadowCasterShaderData(light: DirectLight, shadowSliceData: ShadowSliceData): void { + private _updateSingleShadowCasterShaderData( + light: DirectLight, + shadowSliceData: ShadowSliceData, + context: RenderContext + ): void { // Frustum size is guaranteed to be a cube as we wrap shadow frustum around a sphere // elements[0] = 2.0 / (right - left) const frustumSize = 2.0 / shadowSliceData.projectionMatrix.elements[0]; @@ -363,6 +347,11 @@ export class CascadedShadowCasterPass { const sceneShaderData = this._camera.scene.shaderData; sceneShaderData.setVector2(CascadedShadowCasterPass._lightShadowBiasProperty, this._shadowBias); sceneShaderData.setVector3(CascadedShadowCasterPass._lightDirectionProperty, light.direction); - sceneShaderData.setMatrix(CascadedShadowCasterPass._lightViewProjMatProperty, shadowSliceData.viewProjectMatrix); + + context.applyViewProjectMatrix( + shadowSliceData.viewMatrix, + shadowSliceData.projectionMatrix, + shadowSliceData.viewProjectMatrix + ); } } diff --git a/packages/core/src/shadow/ShadowUtils.ts b/packages/core/src/shadow/ShadowUtils.ts index c3e5d55e5..9272bb414 100644 --- a/packages/core/src/shadow/ShadowUtils.ts +++ b/packages/core/src/shadow/ShadowUtils.ts @@ -10,6 +10,7 @@ import { } from "@oasis-engine/math"; import { Camera } from "../Camera"; import { Renderer } from "../Renderer"; +import { RenderContext } from "../RenderPipeline/RenderContext"; import { TextureFormat } from "../texture"; import { ShadowResolution } from "./enum/ShadowResolution"; import { ShadowSliceData } from "./ShadowSliceData"; @@ -176,24 +177,22 @@ export class ShadowUtils { static cullingRenderBounds(bounds: BoundingBox, cullPlaneCount: number, cullPlanes: Plane[]): boolean { const { min, max } = bounds; - let pass = true; for (let i = 0; i < cullPlaneCount; i++) { const plane = cullPlanes[i]; const normal = plane.normal; if ( - normal.x * (normal.x > 0.0 ? min.x : max.x) + - normal.y * (normal.y > 0.0 ? min.y : max.y) + - normal.z * (normal.z > 0.0 ? min.z : max.z) > + normal.x * (normal.x >= 0.0 ? max.x : min.x) + + normal.y * (normal.y >= 0.0 ? max.y : min.y) + + normal.z * (normal.z >= 0.0 ? max.z : min.z) < -plane.distance ) { - pass = false; - break; + return false; } } - return pass; + return true; } - static shadowCullFrustum(camera: Camera, renderer: Renderer, shadowSliceData: ShadowSliceData) { + static shadowCullFrustum(context: RenderContext, renderer: Renderer, shadowSliceData: ShadowSliceData): void { const center = ShadowUtils._edgePlanePoint2; if ( renderer.castShadows && @@ -201,7 +200,8 @@ export class ShadowUtils { ) { renderer.bounds.getCenter(center); renderer._distanceForSort = Vector3.distance(center, shadowSliceData.position); - renderer._render(camera); + renderer._updateShaderData(context); + renderer._render(context); } } @@ -266,10 +266,10 @@ export class ShadowUtils { const splitFar = ShadowUtils._adjustFarPlane; splitNear.normal.copyFrom(near.normal); splitFar.normal.copyFrom(far.normal); - splitNear.distance = near.distance + splitNearDistance; - //do a clamp is the sphere is out of range the far plane - splitFar.distance = Math.max( - -near.distance - shadowSliceData.sphereCenterZ - shadowSliceData.splitBoundSphere.radius, + splitNear.distance = near.distance - splitNearDistance; + // do a clamp if the sphere is out of range the far plane + splitFar.distance = Math.min( + -near.distance + shadowSliceData.sphereCenterZ + shadowSliceData.splitBoundSphere.radius, far.distance ); @@ -304,7 +304,7 @@ export class ShadowUtils { } } - let edgeIndex: number = backIndex; + let edgeIndex = backIndex; for (let i = 0; i < backIndex; i++) { const backFace = backPlaneFaces[i]; const neighborFaces = planeNeighbors[backFace]; diff --git a/packages/core/src/shadow/enum/ShadowMode.ts b/packages/core/src/shadow/enum/ShadowType.ts similarity index 90% rename from packages/core/src/shadow/enum/ShadowMode.ts rename to packages/core/src/shadow/enum/ShadowType.ts index 8bb5f9f37..6269dc8e4 100644 --- a/packages/core/src/shadow/enum/ShadowMode.ts +++ b/packages/core/src/shadow/enum/ShadowType.ts @@ -1,7 +1,7 @@ /** * Determines which type of shadows should be used. */ -export enum ShadowMode { +export enum ShadowType { /** Disable Shadows. */ None, /** Hard Shadows Only. */ diff --git a/packages/core/src/shadow/index.ts b/packages/core/src/shadow/index.ts index 077e299ee..cb09a192e 100644 --- a/packages/core/src/shadow/index.ts +++ b/packages/core/src/shadow/index.ts @@ -1,3 +1,3 @@ export { ShadowCascadesMode } from "./enum/ShadowCascadesMode"; export { ShadowResolution } from "./enum/ShadowResolution"; -export { ShadowMode } from "./enum/ShadowMode"; +export { ShadowType } from "./enum/ShadowType"; diff --git a/packages/core/src/sky/Sky.ts b/packages/core/src/sky/Sky.ts index cdb02dad2..0a4e0ce27 100644 --- a/packages/core/src/sky/Sky.ts +++ b/packages/core/src/sky/Sky.ts @@ -1,8 +1,8 @@ import { MathUtil, Matrix } from "@oasis-engine/math"; +import { Logger } from "../base/Logger"; import { Mesh } from "../graphic/Mesh"; import { Material } from "../material"; -import { Camera } from "../Camera"; -import { Logger } from "../base/Logger"; +import { RenderContext } from "../RenderPipeline/RenderContext"; import { Shader } from "../shader/Shader"; import { ShaderMacroCollection } from "../shader/ShaderMacroCollection"; @@ -22,7 +22,7 @@ export class Sky { /** * @internal */ - _render(camera: Camera): void { + _render(context: RenderContext): void { const { material, mesh } = this; if (!material) { Logger.warn("The material of sky is not defined."); @@ -33,7 +33,7 @@ export class Sky { return; } - const { engine, aspectRatio, fieldOfView, viewMatrix, shaderData: cameraShaderData } = camera; + const { engine, aspectRatio, fieldOfView, viewMatrix, shaderData: cameraShaderData } = context.camera; const { _viewProjMatrix: viewProjMatrix, _projectionMatrix: projectionMatrix } = Sky; const rhi = engine._hardwareRenderer; const { shaderData: materialShaderData, shader, renderState } = material; @@ -50,12 +50,12 @@ export class Sky { // view-proj matrix Matrix.multiply(projectionMatrix, viewProjMatrix, viewProjMatrix); - const originViewProjMatrix = cameraShaderData.getMatrix(Camera._vpMatrixProperty); - cameraShaderData.setMatrix(Camera._vpMatrixProperty, viewProjMatrix); + const originViewProjMatrix = cameraShaderData.getMatrix(RenderContext._vpMatrixProperty); + cameraShaderData.setMatrix(RenderContext._vpMatrixProperty, viewProjMatrix); const compileMacros = Shader._compileMacros; ShaderMacroCollection.unionCollection( - camera._globalShaderMacro, + context.camera._globalShaderMacro, materialShaderData._macroCollection, compileMacros ); @@ -68,6 +68,6 @@ export class Sky { renderState._apply(engine, false); rhi.drawPrimitive(mesh, mesh.subMesh, program); - cameraShaderData.setMatrix(Camera._vpMatrixProperty, originViewProjMatrix); + cameraShaderData.setMatrix(RenderContext._vpMatrixProperty, originViewProjMatrix); } } diff --git a/packages/core/src/trail/TrailRenderer.ts b/packages/core/src/trail/TrailRenderer.ts index ecf98f23c..a6bede5fe 100644 --- a/packages/core/src/trail/TrailRenderer.ts +++ b/packages/core/src/trail/TrailRenderer.ts @@ -1,5 +1,4 @@ import { Matrix, Quaternion, Vector3 } from "@oasis-engine/math"; -import { Camera } from "../Camera"; import { Entity } from "../Entity"; import { Buffer } from "../graphic/Buffer"; import { BufferUsage } from "../graphic/enums/BufferUsage"; @@ -8,6 +7,7 @@ import { VertexElementFormat } from "../graphic/enums/VertexElementFormat"; import { VertexElement } from "../graphic/VertexElement"; import { BufferMesh } from "../mesh/BufferMesh"; import { MeshRenderer } from "../mesh/MeshRenderer"; +import { RenderContext } from "../RenderPipeline/RenderContext"; import { Texture2D } from "../texture"; import { TrailMaterial } from "./TrailMaterial"; @@ -104,12 +104,12 @@ export class TrailRenderer extends MeshRenderer { /** * @internal */ - _render(camera: Camera): void { - this._updateStrapVertices(camera, this._points); + _render(context: RenderContext): void { + this._updateStrapVertices(context.camera, this._points); this._updateStrapCoords(); this._vertexBuffer.setData(this._vertices); - super._render(camera); + super._render(context); } /** diff --git a/packages/math/src/BoundingFrustum.ts b/packages/math/src/BoundingFrustum.ts index e8ea11fea..0014b7c58 100644 --- a/packages/math/src/BoundingFrustum.ts +++ b/packages/math/src/BoundingFrustum.ts @@ -2,11 +2,11 @@ import { BoundingBox } from "./BoundingBox"; import { BoundingSphere } from "./BoundingSphere"; import { CollisionUtil } from "./CollisionUtil"; import { ContainmentType } from "./enums/ContainmentType"; +import { FrustumFace } from "./enums/FrustumFace"; import { IClone } from "./IClone"; import { ICopy } from "./ICopy"; import { Matrix } from "./Matrix"; import { Plane } from "./Plane"; -import { FrustumFace } from "./enums/FrustumFace"; /** * A bounding frustum. @@ -89,39 +89,39 @@ export class BoundingFrustum implements IClone, ICopy= 0 ? min.x : max.x, normal.y >= 0 ? min.y : max.y, normal.z >= 0 ? min.z : max.z); - if (Vector3.dot(normal, back) > -plane.distance) { + p.set(normal.x >= 0 ? max.x : min.x, normal.y >= 0 ? max.y : min.y, normal.z >= 0 ? max.z : min.z); + if (Vector3.dot(normal, p) < -plane.distance) { return false; } } @@ -369,8 +369,8 @@ export class CollisionUtil { */ static frustumContainsBox(frustum: BoundingFrustum, box: BoundingBox): ContainmentType { const { min, max } = box; - const front = CollisionUtil._tempVec30; - const back = CollisionUtil._tempVec31; + const p = CollisionUtil._tempVec30; + const n = CollisionUtil._tempVec31; let result = ContainmentType.Contains; for (let i = 0; i < 6; ++i) { @@ -378,32 +378,32 @@ export class CollisionUtil { const normal = plane.normal; if (normal.x >= 0) { - front.x = max.x; - back.x = min.x; + p.x = max.x; + n.x = min.x; } else { - front.x = min.x; - back.x = max.x; + p.x = min.x; + n.x = max.x; } if (normal.y >= 0) { - front.y = max.y; - back.y = min.y; + p.y = max.y; + n.y = min.y; } else { - front.y = min.y; - back.y = max.y; + p.y = min.y; + n.y = max.y; } if (normal.z >= 0) { - front.z = max.z; - back.z = min.z; + p.z = max.z; + n.z = min.z; } else { - front.z = min.z; - back.z = max.z; + p.z = min.z; + n.z = max.z; } - if (CollisionUtil.intersectsPlaneAndPoint(plane, back) === PlaneIntersectionType.Front) { + if (CollisionUtil.intersectsPlaneAndPoint(plane, p) === PlaneIntersectionType.Back) { return ContainmentType.Disjoint; } - if (CollisionUtil.intersectsPlaneAndPoint(plane, front) === PlaneIntersectionType.Front) { + if (CollisionUtil.intersectsPlaneAndPoint(plane, n) === PlaneIntersectionType.Back) { result = ContainmentType.Intersects; } } @@ -423,7 +423,7 @@ export class CollisionUtil { for (let i = 0; i < 6; ++i) { const plane = frustum.getPlane(i); const intersectionType = CollisionUtil.intersectsPlaneAndSphere(plane, sphere); - if (intersectionType === PlaneIntersectionType.Front) { + if (intersectionType === PlaneIntersectionType.Back) { return ContainmentType.Disjoint; } else if (intersectionType === PlaneIntersectionType.Intersecting) { result = ContainmentType.Intersects;