mirror of
https://github.com/galacean/engine.git
synced 2026-06-01 00:05:56 +08:00
56
e2e/case/shadow-basic.ts
Normal file
56
e2e/case/shadow-basic.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @title Shadow basic
|
||||
* @category Shadow
|
||||
*/
|
||||
import {
|
||||
Animator,
|
||||
Camera,
|
||||
DirectLight,
|
||||
GLTFResource,
|
||||
MeshRenderer,
|
||||
PBRMaterial,
|
||||
PrimitiveMesh,
|
||||
ShadowResolution,
|
||||
ShadowType,
|
||||
Vector3,
|
||||
WebGLEngine
|
||||
} from "@galacean/engine";
|
||||
|
||||
import { initScreenshot, updateForE2E } from "./.mockForE2E";
|
||||
|
||||
WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
|
||||
engine.canvas.resizeByClientSize();
|
||||
const scene = engine.sceneManager.activeScene;
|
||||
const rootEntity = scene.createRootEntity();
|
||||
scene.shadowResolution = ShadowResolution.Medium;
|
||||
scene.shadowDistance = 5;
|
||||
const cameraEntity = rootEntity.createChild("camera_node");
|
||||
cameraEntity.transform.setPosition(0, 2, 3);
|
||||
cameraEntity.transform.lookAt(new Vector3(0));
|
||||
const camera = cameraEntity.addComponent(Camera);
|
||||
const lightEntity = rootEntity.createChild("light_node");
|
||||
const light = lightEntity.addComponent(DirectLight);
|
||||
lightEntity.transform.setPosition(-10, 10, 10);
|
||||
lightEntity.transform.lookAt(new Vector3(0, 0, 0));
|
||||
|
||||
light.shadowType = ShadowType.SoftHigh;
|
||||
|
||||
const planeEntity = rootEntity.createChild("plane_node");
|
||||
const renderer = planeEntity.addComponent(MeshRenderer);
|
||||
renderer.mesh = PrimitiveMesh.createPlane(engine, 10, 10);
|
||||
const planeMaterial = new PBRMaterial(engine);
|
||||
renderer.setMaterial(planeMaterial);
|
||||
|
||||
engine.resourceManager
|
||||
.load<GLTFResource>("https://gw.alipayobjects.com/os/bmw-prod/5e3c1e4e-496e-45f8-8e05-f89f2bd5e4a4.glb")
|
||||
.then((asset) => {
|
||||
const { defaultSceneRoot } = asset;
|
||||
rootEntity.addChild(defaultSceneRoot);
|
||||
|
||||
const animator = defaultSceneRoot.getComponent(Animator);
|
||||
animator.play(asset.animations[0].name);
|
||||
|
||||
updateForE2E(engine, 500);
|
||||
initScreenshot(engine, camera);
|
||||
});
|
||||
});
|
||||
@@ -89,5 +89,12 @@ export const E2E_CONFIG = {
|
||||
caseFileName: "material-unlit",
|
||||
threshold: 0.2
|
||||
}
|
||||
},
|
||||
Shadow: {
|
||||
basic: {
|
||||
category: "Shadow",
|
||||
caseFileName: "shadow-basic",
|
||||
threshold: 0.2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
3
e2e/fixtures/originImage/Shadow_shadow-basic.jpg
Normal file
3
e2e/fixtures/originImage/Shadow_shadow-basic.jpg
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9cbb0915691b1eef33dad7a7dc3961d13af46a560645560a61c7903aba39b6a7
|
||||
size 113721
|
||||
@@ -68,5 +68,6 @@
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": "git@github.com:galacean/runtime.git"
|
||||
}
|
||||
@@ -42,6 +42,11 @@ export class Scene extends EngineObject {
|
||||
shadowFourCascadeSplits: Vector3 = new Vector3(1.0 / 15, 3.0 / 15.0, 7.0 / 15.0);
|
||||
/** Max Shadow distance. */
|
||||
shadowDistance: number = 50;
|
||||
/**
|
||||
* Last shadow fade distance in percentage, range [0,1].
|
||||
* @remarks Value 0 is used for no shadow fade.
|
||||
*/
|
||||
shadowFadeBorder: number = 0.1;
|
||||
|
||||
/* @internal */
|
||||
_lightManager: LightManager = new LightManager();
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
shadowAttenuation = 1.0;
|
||||
#ifdef SCENE_IS_CALCULATE_SHADOWS
|
||||
shadowAttenuation *= sampleShadowMap();
|
||||
// int sunIndex = int(scene_ShadowInfo.z);
|
||||
#endif
|
||||
|
||||
DirectLight directionalLight;
|
||||
|
||||
@@ -79,7 +79,6 @@ void addTotalDirectRadiance(Geometry geometry, Material material, inout Reflecte
|
||||
shadowAttenuation = 1.0;
|
||||
#ifdef SCENE_IS_CALCULATE_SHADOWS
|
||||
shadowAttenuation *= sampleShadowMap();
|
||||
// int sunIndex = int(scene_ShadowInfo.z);
|
||||
#endif
|
||||
|
||||
DirectLight directionalLight;
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#include <ShadowCoord>
|
||||
#endif
|
||||
|
||||
// intensity, resolution, sunIndex
|
||||
uniform vec3 scene_ShadowInfo;
|
||||
// intensity, null, fadeScale, fadeBias
|
||||
uniform vec4 scene_ShadowInfo;
|
||||
uniform vec4 scene_ShadowMapSize;
|
||||
|
||||
#ifdef GRAPHICS_API_WEBGL2
|
||||
@@ -73,6 +73,13 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
float getShadowFade(vec3 positionWS){
|
||||
vec3 camToPixel = positionWS - camera_Position;
|
||||
float distanceCamToPixel2 = dot(camToPixel, camToPixel);
|
||||
return saturate( distanceCamToPixel2 * scene_ShadowInfo.z + scene_ShadowInfo.w );
|
||||
}
|
||||
|
||||
float sampleShadowMap() {
|
||||
#if SCENE_SHADOW_CASCADED_COUNT == 1
|
||||
vec3 shadowCoord = v_shadowCoord;
|
||||
@@ -93,7 +100,9 @@
|
||||
#if SCENE_SHADOW_TYPE == 3
|
||||
attenuation = sampleShadowMapFiltered9(scene_ShadowMap, shadowCoord, scene_ShadowMapSize);
|
||||
#endif
|
||||
attenuation = mix(1.0, attenuation, scene_ShadowInfo.x);
|
||||
|
||||
float shadowFade = getShadowFade(v_pos);
|
||||
attenuation = mix(1.0, mix(attenuation, 1.0, shadowFade), scene_ShadowInfo.x);
|
||||
}
|
||||
return attenuation;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Color, MathUtil, Matrix, Vector2, Vector3, Vector4 } from "@galacean/engine-math";
|
||||
import { Camera } from "../Camera";
|
||||
import { Layer } from "../Layer";
|
||||
import { PipelineStage } from "../RenderPipeline/index";
|
||||
import { PipelinePass } from "../RenderPipeline/PipelinePass";
|
||||
import { PipelineUtils } from "../RenderPipeline/PipelineUtils";
|
||||
import { RenderContext } from "../RenderPipeline/RenderContext";
|
||||
import { RenderQueue } from "../RenderPipeline/RenderQueue";
|
||||
import { PipelineStage } from "../RenderPipeline/index";
|
||||
import { GLCapabilityType } from "../base/Constant";
|
||||
import { CameraClearFlags } from "../enums/CameraClearFlags";
|
||||
import { DirectLight } from "../lighting";
|
||||
@@ -17,7 +18,6 @@ import { TextureWrapMode } from "../texture/enums/TextureWrapMode";
|
||||
import { ShadowSliceData } from "./ShadowSliceData";
|
||||
import { ShadowUtils } from "./ShadowUtils";
|
||||
import { ShadowCascadesMode } from "./enum/ShadowCascadesMode";
|
||||
import { PipelinePass } from "../RenderPipeline/PipelinePass";
|
||||
|
||||
/**
|
||||
* Cascade shadow caster pass.
|
||||
@@ -52,13 +52,12 @@ export class CascadedShadowCasterPass extends PipelinePass {
|
||||
private _shadowSliceData: ShadowSliceData = new ShadowSliceData();
|
||||
private _lightUp: Vector3 = new Vector3();
|
||||
private _lightSide: Vector3 = new Vector3();
|
||||
private _existShadowMap: boolean = false;
|
||||
|
||||
private _splitBoundSpheres = new Float32Array(CascadedShadowCasterPass._maxCascades * 4);
|
||||
/** The end is project precision problem in shader. */
|
||||
private _shadowMatrices = new Float32Array((CascadedShadowCasterPass._maxCascades + 1) * 16);
|
||||
// strength, null, lightIndex
|
||||
private _shadowInfos = new Vector3();
|
||||
// intensity, null, fadeScale, fadeBias
|
||||
private _shadowInfos = new Vector4();
|
||||
private _depthTexture: Texture2D;
|
||||
private _renderTarget: RenderTarget;
|
||||
private _viewportOffsets: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()];
|
||||
@@ -75,16 +74,13 @@ export class CascadedShadowCasterPass extends PipelinePass {
|
||||
* @internal
|
||||
*/
|
||||
override onRender(context: RenderContext): void {
|
||||
const light = this._camera.scene._lightManager._sunlight;
|
||||
this._updateShadowSettings();
|
||||
this._existShadowMap = false;
|
||||
this._renderDirectShadowMap(context);
|
||||
|
||||
if (this._existShadowMap) {
|
||||
this._updateReceiversShaderData();
|
||||
}
|
||||
this._renderDirectShadowMap(context, light);
|
||||
this._updateReceiversShaderData(light);
|
||||
}
|
||||
|
||||
private _renderDirectShadowMap(context: RenderContext): void {
|
||||
private _renderDirectShadowMap(context: RenderContext, light: DirectLight): void {
|
||||
const {
|
||||
_engine: engine,
|
||||
_camera: camera,
|
||||
@@ -108,151 +104,147 @@ export class CascadedShadowCasterPass extends PipelinePass {
|
||||
const lightSide = this._lightSide;
|
||||
const lightForward = shadowSliceData.virtualCamera.forward;
|
||||
|
||||
const light = scene._lightManager._sunlight;
|
||||
if (light) {
|
||||
const shadowFar = Math.min(camera.scene.shadowDistance, camera.farClipPlane);
|
||||
this._getCascadesSplitDistance(shadowFar);
|
||||
// Prepare render target
|
||||
const { z: width, w: height } = this._shadowMapSize;
|
||||
const format = this._shadowMapFormat;
|
||||
let renderTarget: RenderTarget;
|
||||
let shadowTexture: Texture2D;
|
||||
if (this._supportDepthTexture) {
|
||||
renderTarget = PipelineUtils.recreateRenderTargetIfNeeded(
|
||||
engine,
|
||||
this._renderTarget,
|
||||
width,
|
||||
height,
|
||||
null,
|
||||
format,
|
||||
false
|
||||
);
|
||||
shadowTexture = <Texture2D>renderTarget.depthTexture;
|
||||
} else {
|
||||
renderTarget = PipelineUtils.recreateRenderTargetIfNeeded(
|
||||
engine,
|
||||
this._renderTarget,
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
null,
|
||||
false
|
||||
);
|
||||
shadowTexture = <Texture2D>renderTarget.getColorTexture(0);
|
||||
}
|
||||
|
||||
// Prepare render target
|
||||
const { z: width, w: height } = this._shadowMapSize;
|
||||
const format = this._shadowMapFormat;
|
||||
let renderTarget: RenderTarget;
|
||||
let shadowTexture: Texture2D;
|
||||
if (this._supportDepthTexture) {
|
||||
renderTarget = PipelineUtils.recreateRenderTargetIfNeeded(
|
||||
engine,
|
||||
this._renderTarget,
|
||||
width,
|
||||
height,
|
||||
null,
|
||||
format,
|
||||
false
|
||||
);
|
||||
shadowTexture = <Texture2D>renderTarget.depthTexture;
|
||||
} else {
|
||||
renderTarget = PipelineUtils.recreateRenderTargetIfNeeded(
|
||||
engine,
|
||||
this._renderTarget,
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
null,
|
||||
false
|
||||
);
|
||||
shadowTexture = <Texture2D>renderTarget.getColorTexture(0);
|
||||
}
|
||||
shadowTexture.wrapModeU = shadowTexture.wrapModeV = TextureWrapMode.Clamp;
|
||||
if (engine._hardwareRenderer._isWebGL2) {
|
||||
shadowTexture.depthCompareFunction = TextureDepthCompareFunction.Less;
|
||||
}
|
||||
|
||||
shadowTexture.wrapModeU = shadowTexture.wrapModeV = TextureWrapMode.Clamp;
|
||||
if (engine._hardwareRenderer._isWebGL2) {
|
||||
shadowTexture.depthCompareFunction = TextureDepthCompareFunction.Less;
|
||||
}
|
||||
this._renderTarget = renderTarget;
|
||||
this._depthTexture = shadowTexture;
|
||||
|
||||
this._renderTarget = renderTarget;
|
||||
this._depthTexture = shadowTexture;
|
||||
// @todo: shouldn't set viewport and scissor in activeRenderTarget
|
||||
rhi.activeRenderTarget(renderTarget, CascadedShadowCasterPass._viewport, 0);
|
||||
if (this._supportDepthTexture) {
|
||||
rhi.clearRenderTarget(engine, CameraClearFlags.Depth, null);
|
||||
} else {
|
||||
rhi.clearRenderTarget(engine, CameraClearFlags.All, CascadedShadowCasterPass._clearColor);
|
||||
}
|
||||
|
||||
// @todo: shouldn't set viewport and scissor in activeRenderTarget
|
||||
rhi.activeRenderTarget(renderTarget, CascadedShadowCasterPass._viewport, 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.z = 0; // @todo: sun light index always 0
|
||||
// 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]);
|
||||
const cameraForward = CascadedShadowCasterPass._tempVector;
|
||||
cameraForward.copyFrom(camera.entity.transform.worldForward);
|
||||
|
||||
// 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]);
|
||||
const cameraForward = CascadedShadowCasterPass._tempVector;
|
||||
cameraForward.copyFrom(camera.entity.transform.worldForward);
|
||||
const shadowTileResolution = this._shadowTileResolution;
|
||||
|
||||
const shadowTileResolution = this._shadowTileResolution;
|
||||
for (let j = 0; j < shadowCascades; j++) {
|
||||
ShadowUtils.getBoundSphereByFrustum(
|
||||
splitDistance[j],
|
||||
splitDistance[j + 1],
|
||||
camera,
|
||||
cameraForward,
|
||||
shadowSliceData
|
||||
);
|
||||
ShadowUtils.getDirectionLightShadowCullPlanes(
|
||||
camera._frustum,
|
||||
splitDistance[j],
|
||||
camera.nearClipPlane,
|
||||
lightForward,
|
||||
shadowSliceData
|
||||
);
|
||||
|
||||
for (let j = 0; j < shadowCascades; j++) {
|
||||
ShadowUtils.getBoundSphereByFrustum(
|
||||
splitDistance[j],
|
||||
splitDistance[j + 1],
|
||||
camera,
|
||||
cameraForward,
|
||||
shadowSliceData
|
||||
);
|
||||
ShadowUtils.getDirectionLightShadowCullPlanes(
|
||||
camera._frustum,
|
||||
splitDistance[j],
|
||||
camera.nearClipPlane,
|
||||
lightForward,
|
||||
shadowSliceData
|
||||
);
|
||||
|
||||
ShadowUtils.getDirectionalLightMatrices(
|
||||
lightUp,
|
||||
lightSide,
|
||||
lightForward,
|
||||
j,
|
||||
light.shadowNearPlane,
|
||||
ShadowUtils.getDirectionalLightMatrices(
|
||||
lightUp,
|
||||
lightSide,
|
||||
lightForward,
|
||||
j,
|
||||
light.shadowNearPlane,
|
||||
shadowTileResolution,
|
||||
shadowSliceData,
|
||||
shadowMatrices
|
||||
);
|
||||
if (shadowCascades > 1) {
|
||||
ShadowUtils.applySliceTransform(
|
||||
shadowTileResolution,
|
||||
shadowSliceData,
|
||||
width,
|
||||
height,
|
||||
j,
|
||||
this._viewportOffsets[j],
|
||||
shadowMatrices
|
||||
);
|
||||
if (shadowCascades > 1) {
|
||||
ShadowUtils.applySliceTransform(
|
||||
shadowTileResolution,
|
||||
width,
|
||||
height,
|
||||
j,
|
||||
this._viewportOffsets[j],
|
||||
shadowMatrices
|
||||
);
|
||||
}
|
||||
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;
|
||||
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, light, elements[k], shadowSliceData);
|
||||
}
|
||||
|
||||
if (opaqueQueue.elements.length || alphaTestQueue.elements.length) {
|
||||
opaqueQueue.sort(RenderQueue._compareFromNearToFar);
|
||||
alphaTestQueue.sort(RenderQueue._compareFromNearToFar);
|
||||
|
||||
const { x, y } = viewports[j];
|
||||
|
||||
rhi.setGlobalDepthBias(1.0, 1.0);
|
||||
|
||||
rhi.viewport(x, y, shadowTileResolution, shadowTileResolution);
|
||||
// for no cascade is for the edge,for cascade is for the beyond maxCascade pixel can use (0,0,0) trick sample the shadowMap
|
||||
rhi.scissor(x + 1, y + 1, shadowTileResolution - 2, shadowTileResolution - 2);
|
||||
engine._renderCount++;
|
||||
|
||||
opaqueQueue.render(camera, Layer.Everything, PipelineStage.ShadowCaster);
|
||||
alphaTestQueue.render(camera, Layer.Everything, PipelineStage.ShadowCaster);
|
||||
rhi.setGlobalDepthBias(0, 0);
|
||||
}
|
||||
}
|
||||
this._existShadowMap = true;
|
||||
this._updateSingleShadowCasterShaderData(light, shadowSliceData, context);
|
||||
|
||||
// upload pre-cascade infos.
|
||||
const center = boundSphere.center;
|
||||
const radius = boundSphere.radius;
|
||||
const offset = j * 4;
|
||||
splitBoundSpheres[offset] = center.x;
|
||||
splitBoundSpheres[offset + 1] = center.y;
|
||||
splitBoundSpheres[offset + 2] = center.z;
|
||||
splitBoundSpheres[offset + 3] = radius * radius;
|
||||
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, light, elements[k], shadowSliceData);
|
||||
}
|
||||
|
||||
if (opaqueQueue.elements.length || alphaTestQueue.elements.length) {
|
||||
opaqueQueue.sort(RenderQueue._compareFromNearToFar);
|
||||
alphaTestQueue.sort(RenderQueue._compareFromNearToFar);
|
||||
|
||||
const { x, y } = viewports[j];
|
||||
|
||||
rhi.setGlobalDepthBias(1.0, 1.0);
|
||||
|
||||
rhi.viewport(x, y, shadowTileResolution, shadowTileResolution);
|
||||
// for no cascade is for the edge,for cascade is for the beyond maxCascade pixel can use (0,0,0) trick sample the shadowMap
|
||||
rhi.scissor(x + 1, y + 1, shadowTileResolution - 2, shadowTileResolution - 2);
|
||||
engine._renderCount++;
|
||||
|
||||
opaqueQueue.render(camera, Layer.Everything, PipelineStage.ShadowCaster);
|
||||
alphaTestQueue.render(camera, Layer.Everything, PipelineStage.ShadowCaster);
|
||||
rhi.setGlobalDepthBias(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _updateReceiversShaderData(): void {
|
||||
const scene = this._camera.scene;
|
||||
private _updateReceiversShaderData(light: DirectLight): void {
|
||||
const camera = this._camera;
|
||||
const scene = camera.scene;
|
||||
const splitBoundSpheres = this._splitBoundSpheres;
|
||||
const shadowMatrices = this._shadowMatrices;
|
||||
const shadowCascades = scene.shadowCascades;
|
||||
|
||||
const shadowFar = Math.min(scene.shadowDistance, camera.farClipPlane);
|
||||
ShadowUtils.getScaleAndBiasForLinearDistanceFade(Math.pow(shadowFar, 2), scene.shadowFadeBorder, this._shadowInfos);
|
||||
this._shadowInfos.x = light.shadowStrength;
|
||||
|
||||
// set zero matrix to project the index out of max cascade
|
||||
if (shadowCascades > 1) {
|
||||
for (let i = shadowCascades * 4, n = splitBoundSpheres.length; i < n; i++) {
|
||||
@@ -267,7 +259,7 @@ export class CascadedShadowCasterPass extends PipelinePass {
|
||||
|
||||
const shaderData = scene.shaderData;
|
||||
shaderData.setFloatArray(CascadedShadowCasterPass._shadowMatricesProperty, this._shadowMatrices);
|
||||
shaderData.setVector3(CascadedShadowCasterPass._shadowInfosProperty, this._shadowInfos);
|
||||
shaderData.setVector4(CascadedShadowCasterPass._shadowInfosProperty, this._shadowInfos);
|
||||
shaderData.setTexture(CascadedShadowCasterPass._shadowMapsProperty, this._depthTexture);
|
||||
shaderData.setFloatArray(CascadedShadowCasterPass._shadowSplitSpheresProperty, this._splitBoundSpheres);
|
||||
shaderData.setVector4(CascadedShadowCasterPass._shadowMapSize, this._shadowMapSize);
|
||||
@@ -316,10 +308,14 @@ export class CascadedShadowCasterPass extends PipelinePass {
|
||||
}
|
||||
|
||||
private _updateShadowSettings(): void {
|
||||
const scene = this._camera.scene;
|
||||
const camera = this._camera;
|
||||
const scene = camera.scene;
|
||||
const shadowFormat = ShadowUtils.shadowDepthFormat(scene.shadowResolution, this._supportDepthTexture);
|
||||
const shadowResolution = ShadowUtils.shadowResolution(scene.shadowResolution);
|
||||
const shadowCascades = scene.shadowCascades;
|
||||
const shadowFar = Math.min(scene.shadowDistance, camera.farClipPlane);
|
||||
|
||||
this._getCascadesSplitDistance(shadowFar);
|
||||
|
||||
if (
|
||||
shadowFormat !== this._shadowMapFormat ||
|
||||
|
||||
@@ -7,17 +7,18 @@ import {
|
||||
Matrix,
|
||||
Plane,
|
||||
Vector2,
|
||||
Vector3
|
||||
Vector3,
|
||||
Vector4
|
||||
} from "@galacean/engine-math";
|
||||
import { Camera } from "../Camera";
|
||||
import { DirectLight, Light } from "../lighting";
|
||||
import { Renderer } from "../Renderer";
|
||||
import { RenderContext } from "../RenderPipeline/RenderContext";
|
||||
import { TextureFormat } from "../texture";
|
||||
import { Renderer } from "../Renderer";
|
||||
import { Utils } from "../Utils";
|
||||
import { DirectLight, Light } from "../lighting";
|
||||
import { TextureFormat } from "../texture";
|
||||
import { ShadowSliceData } from "./ShadowSliceData";
|
||||
import { ShadowResolution } from "./enum/ShadowResolution";
|
||||
import { ShadowType } from "./enum/ShadowType";
|
||||
import { ShadowSliceData } from "./ShadowSliceData";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -439,4 +440,29 @@ export class ShadowUtils {
|
||||
const offset = cascadeIndex * 16;
|
||||
Utils._floatMatrixMultiply(sliceMatrix, outShadowMatrices, offset, outShadowMatrices, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract scale and bias from a fade distance to achieve a linear fading of the fade distance.
|
||||
*/
|
||||
static getScaleAndBiasForLinearDistanceFade(fadeDistance: number, border: number, outInfo: Vector4): void {
|
||||
// (P^2-N^2)/(F^2-N^2)
|
||||
|
||||
// To avoid division from zero
|
||||
// This values ensure that fade within cascade will be 0 and outside 1
|
||||
if (border < 0.0001) {
|
||||
const multiplier = 1000; // To avoid blending if difference is in fractions
|
||||
outInfo.z = multiplier;
|
||||
outInfo.w = -fadeDistance * multiplier;
|
||||
return;
|
||||
}
|
||||
|
||||
border = 1 - border;
|
||||
border *= border;
|
||||
|
||||
// Fade with distance calculation is just a linear fade from 90% of fade distance to fade distance. 90% arbitrarily chosen but should work well enough.
|
||||
const distanceFadeNear = border * fadeDistance;
|
||||
const fadeRange = fadeDistance - distanceFadeNear;
|
||||
outInfo.z = 1.0 / fadeRange;
|
||||
outInfo.w = -distanceFadeNear / fadeRange;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user