mirror of
https://github.com/galacean/engine.git
synced 2026-06-09 17:23:29 +08:00
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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <common_vert>
|
||||
#include <blendShape_input>
|
||||
#include <normal_share>
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
@@ -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(<DirectLight>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(<DirectLight>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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Determines which type of shadows should be used.
|
||||
*/
|
||||
export enum ShadowMode {
|
||||
export enum ShadowType {
|
||||
/** Disable Shadows. */
|
||||
None,
|
||||
/** Hard Shadows Only. */
|
||||
@@ -1,3 +1,3 @@
|
||||
export { ShadowCascadesMode } from "./enum/ShadowCascadesMode";
|
||||
export { ShadowResolution } from "./enum/ShadowResolution";
|
||||
export { ShadowMode } from "./enum/ShadowMode";
|
||||
export { ShadowType } from "./enum/ShadowType";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<BoundingFrustum>, ICopy<BoundingF
|
||||
|
||||
// near
|
||||
const nearNormal = this.near.normal;
|
||||
nearNormal.set(-m14 - m13, -m24 - m23, -m34 - m33);
|
||||
this.near.distance = -m44 - m43;
|
||||
nearNormal.set(m14 + m13, m24 + m23, m34 + m33);
|
||||
this.near.distance = m44 + m43;
|
||||
this.near.normalize();
|
||||
|
||||
// far
|
||||
const farNormal = this.far.normal;
|
||||
farNormal.set(m13 - m14, m23 - m24, m33 - m34);
|
||||
this.far.distance = m43 - m44;
|
||||
farNormal.set(m14 - m13, m24 - m23, m34 - m33);
|
||||
this.far.distance = m44 - m43;
|
||||
|
||||
this.far.normalize();
|
||||
|
||||
// left
|
||||
const leftNormal = this.left.normal;
|
||||
leftNormal.set(-m14 - m11, -m24 - m21, -m34 - m31);
|
||||
this.left.distance = -m44 - m41;
|
||||
leftNormal.set(m14 + m11, m24 + m21, m34 + m31);
|
||||
this.left.distance = m44 + m41;
|
||||
this.left.normalize();
|
||||
|
||||
// right
|
||||
const rightNormal = this.right.normal;
|
||||
rightNormal.set(m11 - m14, m21 - m24, m31 - m34);
|
||||
this.right.distance = m41 - m44;
|
||||
rightNormal.set(m14 - m11, m24 - m21, m34 - m31);
|
||||
this.right.distance = m44 - m41;
|
||||
this.right.normalize();
|
||||
|
||||
// bottom
|
||||
const bottomNormal = this.bottom.normal;
|
||||
bottomNormal.set(-m14 - m12, -m24 - m22, -m34 - m32);
|
||||
this.bottom.distance = -m44 - m42;
|
||||
bottomNormal.set(m14 + m12, m24 + m22, m34 + m32);
|
||||
this.bottom.distance = m44 + m42;
|
||||
this.bottom.normalize();
|
||||
|
||||
// top
|
||||
const topNormal = this.top.normal;
|
||||
topNormal.set(m12 - m14, m22 - m24, m32 - m34);
|
||||
this.top.distance = m42 - m44;
|
||||
topNormal.set(m14 - m12, m24 - m22, m34 - m32);
|
||||
this.top.distance = m44 - m42;
|
||||
this.top.normalize();
|
||||
}
|
||||
|
||||
|
||||
@@ -346,14 +346,14 @@ export class CollisionUtil {
|
||||
*/
|
||||
static intersectsFrustumAndBox(frustum: BoundingFrustum, box: BoundingBox): boolean {
|
||||
const { min, max } = box;
|
||||
const back = CollisionUtil._tempVec30;
|
||||
const p = CollisionUtil._tempVec30;
|
||||
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
const plane = frustum.getPlane(i);
|
||||
const normal = plane.normal;
|
||||
|
||||
back.set(normal.x >= 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;
|
||||
|
||||
Reference in New Issue
Block a user