ivoyager / ivoyager_core

Godot Editor plugin that provides a solar system simulation.
Apache License 2.0
11 stars 1 forks source link

There are no shadows #1

Open charliewhitfield opened 4 years ago

charliewhitfield commented 4 years ago

I, Voyager platform All

I, Voyager version and (if applicable) Godot version v0.0.6 and master branch; Godot 3.2.1

OS/device Windows 10 (Chrome for HTML5 platforms)

Issue description: There are no shadows. There should be.

Steps to reproduce Visit Saturn. When rings are not edge-on they should cast a shadow on Saturn. Or visit Jupiter. It should be easy to catch Io's shadow, if it existed. (You can make Io 20x bigger in the moons.tsv table to make that more obvious.)

Minimal project N/A

Other helpful information I don't know why we don't have them! OmniLight.shadow_enabled = true All of our GeometryInstances (worlds and Saturn's rings) have default cast_shadow = 1. All of our SpatialMaterials (world & ring textures) have default flags_do_not_receive_shadows = false.

I played around with Light.shadow_bias. According to the tutorial, I ought to be able to create "self-shadow" artifacts by setting that too low. But no, I can't even create shadow artifacts...

AlanTheSane commented 1 month ago

Did some testing of shadow range in an empty project. It seems the upper limit for shadows cast by an OmniLight is ~10km from the light source before it completely gives out. Regardless the result is already very ugly with artifacting before hitting 10km.

A DirectionalLight could be an option, it isn't a point source so you would have to actively keep it oriented to the selected planet, but shadow cast range between objects is higher and gives a much cleaner result... unfortunately the range between objects tops out at ~1e6m which is still too low for moons. I was hoping the similarity to the camera range issue meant this could also be negated with double floating point precision, but in this case I get identical results on both builds.

As I understand it 64bit floats have a much bigger performance hit on GPUs than they do on CPUs so are rarely used, which is presumably why shadows cant utilize them here, even if you build the rest of the engine with double precision enabled.

A graphics wizard might be able to do some fakery and project a shadow from closer range while keeping everything else at a real planetary scale... Other option being to scale everything down to fit within the shadow cast range.

charliewhitfield commented 1 month ago

Thanks so much for looking into this! FYI, I'm going off-line from the 27th through August 25 (hiking the John Muir Trail where I won't even have cell phone coverage). But I'll try to engage with this and your discussion post over the next couple days...

Just a quick note: We can adjust the scale of the whole sim by changing const METER = 1.0 in a units.gd file. See here for the file used by Planetarium. As noted in the comments there, other artifacts constrain us on that setting, and I have to use very different values in HTML5 export versus Windows export or editor run. (I think it's 1e-10 in the current Web app.) This has varied greatly with different Godot versions too.

I've pondered more extreme trickery with scale, given that the sim uses its own physics so doesn't really care what Godot thinks the distances are.

AlanTheSane commented 1 month ago

The comments indicate const METER = 1e-3 works fine for windows builds and this is just low enough to get usable shadows from some of Jupiters moons with a DirectionalLight (not Callisto). Double precision allows for lower values without having objects vanish.

directional_shadow_max_distance controls the shadow view distance but by increasing this you are also effectively reducing the shadow resolution. It will quickly turn into a blurry mess if it is more than ~1e9 times the chosen METER scale. Therefore, if const METER = 1e-6 then use directional_shadow_max_distance = 1000

With this ratio the viewable distance of shadows is locked at 1e9m (Gannymead can just about see its own shadow). Decreasing the scale below 1e-3 (but maintaining the ratio as described above) will allow more distant bodies like Callisto to cast shadows on Jupiter even if you cant actually see the shadow at that range.

I'm not sure if this is the best solution or how any of it translates to HTML5, but here is my test setup:

Directional light is rotated to x=80, y=270. And I disabled the OmniLight during runtime. image

Io casting a shadow:

image

Shadow close up:

image

charliewhitfield commented 1 month ago

That's awesome! At least we have something to work from now. It won't be too hard to program a directional light that is camera aware (or multiple ones for a multi-star system). I'll do this for the next release for sure.

Just out of curiosity, are you seeing a shadow from Saturn's rings?

I guess we can do some dynamic trickery with directional_shadow_max_distance that is aware of the current view. I see a problem where we won't be able to have good shadows from astronomical objects (eg, rings on Saturn) at the same time the viewer is close-up to small bodies that might have a shadows.

AlanTheSane commented 1 month ago

Saturns rings are actually completely missing for me on my double precision build (even with default values), I haven't looked into why yet.

If I switch back to a standard build and keep within the limits Saturn casts a shadow on the ring... but no the ring isn't casting its own shadow.

charliewhitfield commented 1 month ago

I wouldn't expect rings.gdshader to be affected. Probably rings.gd is passing some bad data in the double-precision build. It's possible we are exposing a Godot bug that no one else has tested.

Also, I might have broken the rings shadow casting (specifically) in rings.gd code. I was messing around with shadow modes that I don't really understand.

AlanTheSane commented 1 month ago

I guess we can do some dynamic trickery with directional_shadow_max_distance that is aware of the current view. I see a problem where we won't be able to have good shadows from astronomical objects (eg, rings on Saturn) at the same time the viewer is close-up to small bodies that might have a shadows.

It seems you can have multiple light sources using different light_cull_mask layers and different shadow settings for each one. I first thought light would just "go through" an object if it wasn't on the same layer as the light source, but while it wont be lit by the light source it still casts a shadow from it.

A planet layer with a dynamically adjusted directional_shadow_max_distance can keep moon shadows crisp regardless of your view distance. Moons might be a bit trickier as it is possible to have multiple in view at the same time at wildly different ranges.

A reasonable scenario could have one nearby moon casting a shadow on the planet, and another on the far side in the planets own shadow. For the far side moon to be properly shadowed its light source must have a high shadow_max_distance, so if you want good shadow detail on the nearby moon you would need to further split them up onto more layers. Splitting every moon onto its own layer would likely be excessive, so if you do want more detail on a nearby moon maybe only split off the moon you currently have selected, while all others share a default moon layer / light source.

Follow a similar pattern for each size class, like a space station / space ship layer. A "default" layer with appropriately high shadow distance, and a "selected" layer for (dynamically adjusted) shadow detail if its needed.

charliewhitfield commented 1 month ago

OK, I think I understand this. So maybe we can get by with two layers: one fixed that handles large "astronomical" objects and one dynamic that handles smaller objects near the camera. We can try that first and see if we need a third intermediate layer.

The scaling artifacts I noted for const METER in units.gd were lighting artifacts. So that makes me think that these won't be limiting after we replace the OmniLight. Is there a conceivable fixed METER setting that will allow shadows at all scales we are talking about, given different light_cull_mask layers?

AlanTheSane commented 1 month ago

Kind of... other way round though. I suggest a dynamic light/shadow layer for the planet as you would mostly only ever have 1 body of that size in view at a time. Using the 4 split shadow mode is usable as shown in the screenshots above, but its a bit low resolution for such extreme viewing ranges and would benefit from a dynamically adjusted orthogonal mode rather than using splits.

Dynamic shadow distance is only really useful if you want to keep a single object "in focus", otherwise it can have weird effects on other objects that share the same layer. Using a fixed shadow distance should be the default when you have multiple objects in view, and due to the vast differences in scale you probably want a separate fixed/default layer for each size class of body. A planetary system on its own isn't a very complex scene with lots of geometry, so adding extra light/shadow layers shouldn't be very expensive (testing needed).

You can get away with not splitting the "in focus" moon onto a separate dynamic layer, but while testing some extreme shadow ranges there were artifacts when zoomed in close to a moon that was fully in Jupiters shadow. In this instance the value didn't need to be quite so high and was fine when lowered but if you do need even higher shadow distances for distant moons, or want some detail in the shadows cast on a moon, splitting it off might be necessary.

Shadow artifacting: image

The scaling artifacts I noted for const METER in units.gd were lighting artifacts.

Oh. I'm sure I tested this on its own and had objects fully vanishing. Tried again now and I get what you describe. Maybe I messed something up.

Is there a conceivable fixed METER setting that will allow shadows at all scales we are talking about,

The limiting factor that I have found is a moon in its planets shadow. If you zoom out beyond the shadow range (hard capped at 1e6 godot units) they suddenly light up. The moon might be so small it has effectively disappeared... but depending on screen resolution it could still be a visible pixel, or several pixels. Decide what range it is acceptable to have the moon suddenly light up and use that as a baseline for the METER scale.