Skip to content

Commit

Permalink
Cascading Shadows (#1036)
Browse files Browse the repository at this point in the history
  • Loading branch information
HunterBarclay authored Aug 13, 2024
2 parents 3a20e92 + 9de49d7 commit 770220d
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 21 deletions.
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,
})
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)

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

0 comments on commit 770220d

Please sign in to comment.