Skip to content

Commit 770220d

Browse files
Cascading Shadows (#1036)
2 parents 3a20e92 + 9de49d7 commit 770220d

File tree

3 files changed

+99
-21
lines changed

3 files changed

+99
-21
lines changed

fission/src/mirabuf/MirabufInstance.ts

+2
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class MirabufInstance {
146146
material = new THREE.MeshPhongMaterial({
147147
color: hex,
148148
shininess: 0.0,
149+
shadowSide: THREE.DoubleSide,
149150
opacity: opacity,
150151
transparent: opacity < 1.0,
151152
})
@@ -156,6 +157,7 @@ class MirabufInstance {
156157
console.debug("Toon Material")
157158
}
158159

160+
World.SceneRenderer.SetupMaterial(material!)
159161
this._materials.set(appearanceId, material!)
160162
}
161163
)

fission/src/systems/scene/SceneRenderer.ts

+95-21
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import InputSystem from "../input/InputSystem"
1313
import Jolt from "@barclah/jolt-physics"
1414

1515
import { PixelSpaceCoord, SceneOverlayEvent, SceneOverlayEventKey } from "@/ui/components/SceneOverlayEvents"
16-
import {} from "@/ui/components/SceneOverlayEvents"
1716
import PreferencesSystem from "../preferences/PreferencesSystem"
17+
import { CSM } from "three/examples/jsm/csm/CSM.js"
1818

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

38+
private _light: THREE.DirectionalLight | CSM | undefined
39+
3840
public get sceneObjects() {
3941
return this._sceneObjects
4042
}
@@ -75,25 +77,10 @@ class SceneRenderer extends WorldSystem {
7577
this._renderer.shadowMap.type = THREE.PCFSoftShadowMap
7678
this._renderer.setSize(window.innerWidth, window.innerHeight)
7779

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

83-
const shadowMapSize = Math.min(4096, this._renderer.capabilities.maxTextureSize)
84-
const shadowCamSize = 15
85-
console.debug(`Shadow Map Size: ${shadowMapSize}`)
86-
87-
directionalLight.shadow.camera.top = shadowCamSize
88-
directionalLight.shadow.camera.bottom = -shadowCamSize
89-
directionalLight.shadow.camera.left = -shadowCamSize
90-
directionalLight.shadow.camera.right = shadowCamSize
91-
directionalLight.shadow.mapSize = new THREE.Vector2(shadowMapSize, shadowMapSize)
92-
directionalLight.shadow.blurSamples = 16
93-
directionalLight.shadow.normalBias = 0.01
94-
directionalLight.shadow.bias = 0.0
95-
96-
const ambientLight = new THREE.AmbientLight(0xffffff, 0.1)
83+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3)
9784
this._scene.add(ambientLight)
9885

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

135+
this._mainCamera.updateMatrixWorld()
136+
137+
// updating the CSM light if it is enabled
138+
if (this._light instanceof CSM) this._light.update()
139+
148140
this._skybox.position.copy(this._mainCamera.position)
149141

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

162+
/**
163+
* Changes the quality of lighting between cascading shadows and directional lights
164+
*
165+
* @param quality: string representing the quality of lighting - "Low", "Medium", "High"
166+
*/
167+
public ChangeLighting(quality: string): void {
168+
// removing the previous lighting method
169+
if (this._light instanceof THREE.DirectionalLight) {
170+
this._scene.remove(this._light)
171+
} else if (this._light instanceof CSM) {
172+
this._light.dispose()
173+
this._light.remove()
174+
}
175+
176+
// setting the shadow map size
177+
const shadowMapSize = Math.min(4096, this._renderer.capabilities.maxTextureSize)
178+
179+
// setting the light to a basic directional light
180+
if (quality === "Low" || quality === "Medium") {
181+
const shadowCamSize = 15
182+
183+
this._light = new THREE.DirectionalLight(0xffffff, 5.0)
184+
this._light.position.set(-1.0, 3.0, 2.0)
185+
this._light.castShadow = true
186+
this._light.shadow.camera.top = shadowCamSize
187+
this._light.shadow.camera.bottom = -shadowCamSize
188+
this._light.shadow.camera.left = -shadowCamSize
189+
this._light.shadow.camera.right = shadowCamSize
190+
this._light.shadow.mapSize = new THREE.Vector2(shadowMapSize, shadowMapSize)
191+
this._light.shadow.blurSamples = 16
192+
this._light.shadow.bias = 0.0
193+
this._light.shadow.normalBias = 0.01
194+
this._scene.add(this._light)
195+
} else if (quality === "High") {
196+
// setting light to cascading shadows
197+
this._light = new CSM({
198+
parent: this._scene,
199+
camera: this._mainCamera,
200+
cascades: 4,
201+
lightDirection: new THREE.Vector3(1.0, -3.0, -2.0).normalize(),
202+
lightIntensity: 5,
203+
shadowMapSize: shadowMapSize,
204+
mode: "custom",
205+
maxFar: 30,
206+
shadowBias: -0.00001,
207+
customSplitsCallback: (cascades: number, near: number, far: number, breaks: number[]) => {
208+
const blend = 0.7
209+
for (let i = 1; i < cascades; i++) {
210+
const uniformFactor = (near + ((far - near) * i) / cascades) / far
211+
const logarithmicFactor = (near * (far / near) ** (i / cascades)) / far
212+
const combinedFactor = uniformFactor * (1 - blend) + logarithmicFactor * blend
213+
214+
breaks.push(combinedFactor)
215+
}
216+
217+
breaks.push(1)
218+
},
219+
})
220+
221+
// setting up the materials for all objects in the scene
222+
this._light.fade = true
223+
this._scene.children.forEach(child => {
224+
if (child instanceof THREE.Mesh) {
225+
if (this._light instanceof CSM) this._light.setupMaterial(child.material)
226+
}
227+
})
228+
}
229+
}
230+
170231
public RegisterSceneObject<T extends SceneObject>(obj: T): number {
171232
const id = nextSceneObjectId++
172233
obj.id = id
@@ -190,6 +251,7 @@ class SceneRenderer extends WorldSystem {
190251
public CreateSphere(radius: number, material?: THREE.Material | undefined): THREE.Mesh {
191252
const geo = new THREE.SphereGeometry(radius)
192253
if (material) {
254+
if (this._light instanceof CSM) this._light.setupMaterial(material)
193255
return new THREE.Mesh(geo, material)
194256
} else {
195257
return new THREE.Mesh(geo, this.CreateToonMaterial())
@@ -213,10 +275,13 @@ class SceneRenderer extends WorldSystem {
213275
}
214276
const gradientMap = new THREE.DataTexture(colors, colors.length, 1, format)
215277
gradientMap.needsUpdate = true
216-
return new THREE.MeshToonMaterial({
278+
const material = new THREE.MeshToonMaterial({
217279
color: color,
280+
shadowSide: THREE.DoubleSide,
218281
gradientMap: gradientMap,
219282
})
283+
if (this._light instanceof CSM) this._light.setupMaterial(material)
284+
return material
220285
}
221286

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

252-
/**
317+
/**
253318
* Updates the skybox colors based on the current theme
254319
255320
* @param currentTheme: current theme from ThemeContext.useTheme()
@@ -359,6 +424,15 @@ class SceneRenderer extends WorldSystem {
359424
public RemoveObject(obj: THREE.Object3D) {
360425
this._scene.remove(obj)
361426
}
427+
428+
/**
429+
* Sets up the threejs material for cascading shadows if the CSM is enabled
430+
*
431+
* @param material
432+
*/
433+
public SetupMaterial(material: THREE.Material) {
434+
if (this._light instanceof CSM) this._light.setupMaterial(material)
435+
}
362436
}
363437

364438
export default SceneRenderer

fission/src/ui/modals/configuring/SettingsModal.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { SceneOverlayEvent, SceneOverlayEventKey } from "@/ui/components/SceneOv
1212
import { QualitySetting } from "@/systems/preferences/PreferenceTypes"
1313
import { Box } from "@mui/material"
1414
import { Spacer } from "@/ui/components/StyledComponents"
15+
import World from "@/systems/World"
1516

1617
const SettingsModal: React.FC<ModalPropsImpl> = ({ modalId }) => {
1718
const { openModal } = useModalControlContext()
@@ -73,6 +74,7 @@ const SettingsModal: React.FC<ModalPropsImpl> = ({ modalId }) => {
7374
defaultValue={PreferencesSystem.getGlobalPreference<QualitySetting>("QualitySettings")}
7475
onSelect={selected => {
7576
setQualitySettings(selected)
77+
World.SceneRenderer.ChangeLighting(selected)
7678
}}
7779
/>
7880
{Spacer(5)}

0 commit comments

Comments
 (0)