Skip to content

Cascading Shadows #1036

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4f182f0
Adding basic Cascading Shadows implementation
Dhruv-0-Arora Jul 20, 2024
323386e
Formatting
Dhruv-0-Arora Jul 20, 2024
6213b25
Merge remote-tracking branch 'origin/dev' into dhruv/1694/shadow-casc…
Dhruv-0-Arora Jul 22, 2024
e716d0b
Changing light color and intensity and removing old lighting implemen…
Dhruv-0-Arora Jul 22, 2024
4bf919b
Merge remote-tracking branch 'origin/dev' into dhruv/1694/shadow-casc…
Dhruv-0-Arora Jul 24, 2024
c85ad83
Adding changes to using the implementation built in
Dhruv-0-Arora Jul 24, 2024
3b304aa
Merge remote-tracking branch 'origin/dev' into dhruv/1694/shadow-casc…
Dhruv-0-Arora Jul 24, 2024
28dab58
Adding quality changing functionality
Dhruv-0-Arora Jul 25, 2024
8b10576
Merge remote-tracking branch 'origin/dev' into dhruv/1694/shadow-casc…
Dhruv-0-Arora Jul 26, 2024
ddc0a55
Created working light switching for cascading shadow maps
Dhruv-0-Arora Jul 26, 2024
f36bbc7
Formatted + removed testing cube + updated documentation
Dhruv-0-Arora Jul 26, 2024
015ca51
Fixing setupMaterials
Dhruv-0-Arora Jul 29, 2024
404f7be
Working shadows
Dhruv-0-Arora Jul 30, 2024
22b5e8c
Merge remote-tracking branch 'origin/dev' into dhruv/1694/shadow-casc…
Dhruv-0-Arora Jul 30, 2024
b692eb7
Merge remote-tracking branch 'origin/dev' into dhruv/1694/shadow-casc…
Dhruv-0-Arora Aug 6, 2024
dcca428
Formatting
Dhruv-0-Arora Aug 6, 2024
1fdb767
Increased number of cascades
Dhruv-0-Arora Aug 8, 2024
9de49d7
Merge remote-tracking branch 'origin/dev' into dhruv/1694/shadow-casc…
Dhruv-0-Arora Aug 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions fission/src/mirabuf/MirabufInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class MirabufInstance {
material = new THREE.MeshPhongMaterial({
color: hex,
shininess: 0.0,
shadowSide: THREE.DoubleSide,
opacity: opacity,
transparent: opacity < 1.0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the shadowSide parameters to render against both the front and back faces when casting the shadow. This will remove the gap from the object to the shadow caused by the normal bias.

})
Expand All @@ -156,6 +157,7 @@ class MirabufInstance {
console.debug("Toon Material")
}

World.SceneRenderer.SetupMaterial(material!)
this._materials.set(appearanceId, material!)
}
)
Expand Down
116 changes: 95 additions & 21 deletions fission/src/systems/scene/SceneRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import InputSystem from "../input/InputSystem"
import Jolt from "@barclah/jolt-physics"

import { PixelSpaceCoord, SceneOverlayEvent, SceneOverlayEventKey } from "@/ui/components/SceneOverlayEvents"
import {} from "@/ui/components/SceneOverlayEvents"
import PreferencesSystem from "../preferences/PreferencesSystem"
import { CSM } from "three/examples/jsm/csm/CSM.js"

const CLEAR_COLOR = 0x121212
const GROUND_COLOR = 0x4066c7
Expand All @@ -35,6 +35,8 @@ class SceneRenderer extends WorldSystem {
private _orbitControls: OrbitControls
private _transformControls: Map<TransformControls, number> // maps all rendered transform controls to their size

private _light: THREE.DirectionalLight | CSM | undefined

public get sceneObjects() {
return this._sceneObjects
}
Expand Down Expand Up @@ -75,25 +77,10 @@ class SceneRenderer extends WorldSystem {
this._renderer.shadowMap.type = THREE.PCFSoftShadowMap
this._renderer.setSize(window.innerWidth, window.innerHeight)

const directionalLight = new THREE.DirectionalLight(0xffffff, 3.0)
directionalLight.position.set(-1.0, 3.0, 2.0)
directionalLight.castShadow = true
this._scene.add(directionalLight)
// Adding the lighting uisng quality preferences
this.ChangeLighting(PreferencesSystem.getGlobalPreference<string>("QualitySettings"))

const shadowMapSize = Math.min(4096, this._renderer.capabilities.maxTextureSize)
const shadowCamSize = 15
console.debug(`Shadow Map Size: ${shadowMapSize}`)

directionalLight.shadow.camera.top = shadowCamSize
directionalLight.shadow.camera.bottom = -shadowCamSize
directionalLight.shadow.camera.left = -shadowCamSize
directionalLight.shadow.camera.right = shadowCamSize
directionalLight.shadow.mapSize = new THREE.Vector2(shadowMapSize, shadowMapSize)
directionalLight.shadow.blurSamples = 16
directionalLight.shadow.normalBias = 0.01
directionalLight.shadow.bias = 0.0

const ambientLight = new THREE.AmbientLight(0xffffff, 0.1)
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3)
this._scene.add(ambientLight)

const ground = new THREE.Mesh(new THREE.BoxGeometry(10, 1, 10), this.CreateToonMaterial(GROUND_COLOR))
Expand Down Expand Up @@ -145,6 +132,11 @@ class SceneRenderer extends WorldSystem {
obj.Update()
})

this._mainCamera.updateMatrixWorld()

// updating the CSM light if it is enabled
if (this._light instanceof CSM) this._light.update()

this._skybox.position.copy(this._mainCamera.position)

const mainCameraFovRadians = (Math.PI * (this._mainCamera.fov * 0.5)) / 180
Expand All @@ -167,6 +159,75 @@ class SceneRenderer extends WorldSystem {
this.RemoveAllSceneObjects()
}

/**
* Changes the quality of lighting between cascading shadows and directional lights
*
* @param quality: string representing the quality of lighting - "Low", "Medium", "High"
*/
public ChangeLighting(quality: string): void {
// removing the previous lighting method
if (this._light instanceof THREE.DirectionalLight) {
this._scene.remove(this._light)
} else if (this._light instanceof CSM) {
this._light.dispose()
this._light.remove()
}

// setting the shadow map size
const shadowMapSize = Math.min(4096, this._renderer.capabilities.maxTextureSize)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe try using a smaller shadow map size for low quality to reduce memory usage. Cutting the max size from 4096 to 2048 can 1/4th the memory usage of the texture map.


// setting the light to a basic directional light
if (quality === "Low" || quality === "Medium") {
const shadowCamSize = 15

this._light = new THREE.DirectionalLight(0xffffff, 5.0)
this._light.position.set(-1.0, 3.0, 2.0)
this._light.castShadow = true
this._light.shadow.camera.top = shadowCamSize
this._light.shadow.camera.bottom = -shadowCamSize
this._light.shadow.camera.left = -shadowCamSize
this._light.shadow.camera.right = shadowCamSize
this._light.shadow.mapSize = new THREE.Vector2(shadowMapSize, shadowMapSize)
this._light.shadow.blurSamples = 16
this._light.shadow.bias = 0.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add in the normalBias again to fix the artifacts. Also see MirabufInstance about removing the offset of the shadow.

this._light.shadow.normalBias = 0.01
this._scene.add(this._light)
} else if (quality === "High") {
// setting light to cascading shadows
this._light = new CSM({
parent: this._scene,
camera: this._mainCamera,
cascades: 4,
lightDirection: new THREE.Vector3(1.0, -3.0, -2.0).normalize(),
lightIntensity: 5,
shadowMapSize: shadowMapSize,
mode: "custom",
maxFar: 30,
shadowBias: -0.00001,
customSplitsCallback: (cascades: number, near: number, far: number, breaks: number[]) => {
const blend = 0.7
for (let i = 1; i < cascades; i++) {
const uniformFactor = (near + ((far - near) * i) / cascades) / far
const logarithmicFactor = (near * (far / near) ** (i / cascades)) / far
const combinedFactor = uniformFactor * (1 - blend) + logarithmicFactor * blend

breaks.push(combinedFactor)
}

breaks.push(1)
},
})

// setting up the materials for all objects in the scene
this._light.fade = true
this._scene.children.forEach(child => {
if (child instanceof THREE.Mesh) {
if (this._light instanceof CSM) this._light.setupMaterial(child.material)
}
})
}
}

public RegisterSceneObject<T extends SceneObject>(obj: T): number {
const id = nextSceneObjectId++
obj.id = id
Expand All @@ -190,6 +251,7 @@ class SceneRenderer extends WorldSystem {
public CreateSphere(radius: number, material?: THREE.Material | undefined): THREE.Mesh {
const geo = new THREE.SphereGeometry(radius)
if (material) {
if (this._light instanceof CSM) this._light.setupMaterial(material)
return new THREE.Mesh(geo, material)
} else {
return new THREE.Mesh(geo, this.CreateToonMaterial())
Expand All @@ -213,10 +275,13 @@ class SceneRenderer extends WorldSystem {
}
const gradientMap = new THREE.DataTexture(colors, colors.length, 1, format)
gradientMap.needsUpdate = true
return new THREE.MeshToonMaterial({
const material = new THREE.MeshToonMaterial({
color: color,
shadowSide: THREE.DoubleSide,
gradientMap: gradientMap,
})
if (this._light instanceof CSM) this._light.setupMaterial(material)
return material
}

/**
Expand Down Expand Up @@ -249,7 +314,7 @@ class SceneRenderer extends WorldSystem {
return [(window.innerWidth * (screenSpace.x + 1.0)) / 2.0, (window.innerHeight * (1.0 - screenSpace.y)) / 2.0]
}

/**
/**
* Updates the skybox colors based on the current theme

* @param currentTheme: current theme from ThemeContext.useTheme()
Expand Down Expand Up @@ -359,6 +424,15 @@ class SceneRenderer extends WorldSystem {
public RemoveObject(obj: THREE.Object3D) {
this._scene.remove(obj)
}

/**
* Sets up the threejs material for cascading shadows if the CSM is enabled
*
* @param material
*/
public SetupMaterial(material: THREE.Material) {
if (this._light instanceof CSM) this._light.setupMaterial(material)
}
}

export default SceneRenderer
2 changes: 2 additions & 0 deletions fission/src/ui/modals/configuring/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SceneOverlayEvent, SceneOverlayEventKey } from "@/ui/components/SceneOv
import { QualitySetting } from "@/systems/preferences/PreferenceTypes"
import { Box } from "@mui/material"
import { Spacer } from "@/ui/components/StyledComponents"
import World from "@/systems/World"

const SettingsModal: React.FC<ModalPropsImpl> = ({ modalId }) => {
const { openModal } = useModalControlContext()
Expand Down Expand Up @@ -73,6 +74,7 @@ const SettingsModal: React.FC<ModalPropsImpl> = ({ modalId }) => {
defaultValue={PreferencesSystem.getGlobalPreference<QualitySetting>("QualitySettings")}
onSelect={selected => {
setQualitySettings(selected)
World.SceneRenderer.ChangeLighting(selected)
}}
/>
{Spacer(5)}
Expand Down
Loading