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:
ChenMo
2022-11-07 13:12:06 +08:00
committed by GitHub
parent 70013030fe
commit 57ea0f8c8a
26 changed files with 327 additions and 295 deletions

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}
/**

View File

@@ -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);
}

View File

@@ -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);
}
/**

View File

@@ -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;
}

View File

@@ -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(

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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
);
}

View File

@@ -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. */

View File

@@ -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;
}

View File

@@ -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];

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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 );

View File

@@ -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

View File

@@ -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
);
}
}

View File

@@ -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];

View File

@@ -1,7 +1,7 @@
/**
* Determines which type of shadows should be used.
*/
export enum ShadowMode {
export enum ShadowType {
/** Disable Shadows. */
None,
/** Hard Shadows Only. */

View File

@@ -1,3 +1,3 @@
export { ShadowCascadesMode } from "./enum/ShadowCascadesMode";
export { ShadowResolution } from "./enum/ShadowResolution";
export { ShadowMode } from "./enum/ShadowMode";
export { ShadowType } from "./enum/ShadowType";

View File

@@ -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);
}
}

View File

@@ -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);
}
/**

View File

@@ -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();
}

View File

@@ -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;