I decided to use the feature I added to the engine a while ago that allows for custom screen space shaders to create a lighting shader for the game based on some simple raymarching! I’m going to work on getting it more polished and then implement it as a variable exposed by the main engine as something that can be used in any project.
The usage of the shader isn’t too complicated, it just requires data about the lights to be supplied to the shader via uniforms.
let lightPositions = [
new THREE.Vector2(-128, 128),
new THREE.Vector2(64, 128)
]
let lightScales = [
new THREE.Vector2(512, 512),
new THREE.Vector2(512, 512)
]
let lightColors = [
new THREE.Vector3(1,0.3,0),
new THREE.Vector3(0,1,0.3)
]
This would be supplied to the App’s options to override the shader.
shaderOverride: {
vertexShader: Phoenix.DefaultVertexShader,
fragmentShader: LitShader,
uniforms: {
uLightPositions: { value: lightPositions },
uLightScales: { value: lightScales },
uLightColors: { value: lightColors }
}
}
And then positions should be updated every frame to keep them in world space.
app.addFrameIntervalCallback(() => {
let lps = []
for (const lightPos of lightPositions) {
const p = new THREE.Vector3(lightPos.x, lightPos.y, 0);
p.project(app.camera);
const x = (p.x + 1) / 2
const y = (p.y + 1) / 2
lps.push(new THREE.Vector2(x, y))
}
app.screenSpaceShader.uniforms.uLightPositions!.value = lps
let scls = []
for (const lightScale of lightScales) {
const x = lightScale.x! / rScale.x
const y = lightScale.y! / rScale.y
scls.push(new THREE.Vector2(x, y))
}
app.screenSpaceShader.uniforms.uLightScales!.value = scls
})
Edit: I completely forgot about this but at some point I added support for animated sprites using the AnimatedSprite class which can be supplied an array of strings representing the paths to the image for each frame as well as a animation rate (number of game frames each animation frame is shown for)