Shadow maps in 2025?

In 2023 you mentioned that I might use shadow maps but it would be difficult. I am been trying to figure out how to use those in hopes they perform nicely with many trees. It is confusing disentangling the documentation for shadow maps vs shadow volumes. It seems like they overlap with some things? Like shadows boolean? It seems like the docs for shadow maps are more at the x3d level? Has anything changed on shadow maps? I switched to DirectionalLight instead of Pointlight, but still see no shadows. My objects are all built in pascal, nothing comes from blender or file models. I guess I need to add the shadowmap to map terrain triangle sets?

I tried the castle_with_trees_process in the shadow_maps demo models… but it won’t build, it can’t find FdProjectionNear in a TAbstractLightNode.

Making shadow maps really comfortable is still a TODO, see roadmap section about shadow maps. I did a progress there (to make cross-scene), but it’s all still work-in-progress I’m afraid.

That is, shadow maps work, but are not as comfortable as I’d like, you need to have everything in one X3D scene, which pretty much prevents the normal engine usage (when you have many scenes, and I recommend it). Which is likely making it not really useful in your case, sorry – it’s one of tasks blocking 7.0, so I really want to clean it up + improve.

Docs:

  • Shadow maps docs are on Shadow Maps | Castle Game Engine .

  • They are separate from shadow volumes Shadow Volumes | Manual | Castle Game Engine .

    But I know some bits are confusing because they suggest that some things may be general (and they are planned to be general!) but are not at this moment.

    • E.g. TCastlePoint/Spot/DirectionalLight.Shadows boolean controls only shadow volumes now (but the plan is: let it control both, additional property should just the algorithm).
    • In contrast, on X3D nodes, TPoint/Spot/DirectionalNode.Shadows boolean controls only shadow maps (but the plan is: make it general, let it control both, again additional field should control the algorithm).

Sorry, cleaning up the mess above is part of our roadmap item :slight_smile: I know that shadow maps are at this point hard to use.

We moved some properties to TAbstractPunctualLightNode. I fixed that tool (see Fix compilation of castle_with_trees_process.lpr ¡ castle-engine/demo-models@4bc94f7 ¡ GitHub ) and made sure it produces the output as before.

But you should not need to use it, in practice. That tool converts VRML generated by old Blender->VRML exporter (the current Blender X3D exporter is likely quite different). The whole example should be, frankly, remade to use glTF (model format) | Creating Game Data | Castle Game Engine now which has excellent Blender->glTF converter and we recommend it.

I can easily put the terrain and the trees into the same x3d rootnode. Then the trees can cast shadows without the huge performance expense they do with the volume shadows. It already seems beneficial to have all the trees for a tile in 1 x3d rootnode. I have to have the light in the same scene too? Perhaps if I duplicated the light per tile (ie per scene) for just shadow use?

Various answers:

  • Currently, the shadow receiver and the light casting shadows must be in the same scene. It doesn’t matter where the shadow caster is, it can be in different scene.

  • There is no concept of “light just for shadow use” for shadow maps. “Shadow” means (in a simulation of shadow maps and in reality :slight_smile: ) that a light doesn’t reach the object. So the light must really shine to make the shadow sensible.

  • I don’t really recommend to merge many things together in 1 scene with 1 X3D root node. We want to optimize world with many scenes, not with 1 big scene, though some things work better this or that way. In practice, a “healthy mix” works best, see Optimization and profiling | Manual | Castle Game Engine . If you have a case where just combining 100 TCastleScene instances into one TCastleScene instance with one X3D graph makes it much faster → submit a bug with a testcase to reproduce, because usually it should not make a big difference (unless you do something more at merging).

  • It would be bad if you are forced to merge into one scene by our unoptimal shadow maps implementation. I feel that our problems in the shadow maps area really should not guide this design decision how you use CGE, as it will affect many things :slight_smile: We should just improve shadow maps on the CGE side.

All in all, I’m afraid it will be complicated to setup for you, and the gains are not that obvious. Shadow maps also have a performance hit, just in a different place. And they look much different.

It seems like having lots of trees in one scene is helpful. In this case 1000 trees in a scene. I feel like shadowmaps is ideal since trees don’t move so they would only need to be calculated every few minutes as the sun moves. But I can wait til it is more ready. I have been trying to get the core basics nailed down before moving on to other things (revisiting roads). If I have just 100 trees per tile, even with shadows, performance is ok. I can live with that for now. If you do have time to revisit shadow maps, a demo that applies them in wyrdforest or to the terrain demo would be cool.

You are of course welcome to try the repo. Right now it is set to 1000 trees per tile, all in their own scene.

Right, this would indeed be a gain, shadow maps are updated only as often as you want. In frames when shadow maps don’t need to be updated, their cost is merely an additional texture fetch per light source, so rather trivial.

For shadow volumes, they have to be rendered (shadow quads, and 2nd pass with light) every frame.

I think I indeed advise it (to wait) in this situation. I would hate to see you redesign your approach (as merging/splitting TCastleScene has an effect on how you do other things you can do in CGE, so it’s not something trivial to undo in general) to address our problems with shadow maps, which will hopefully just disappear in 2025.

Thank you! I added this to TODO.

1 Like

From what I see shadow volumes also scale quite poorly on modern monitors with very high resolution, since the same scene need to be rendered 2 times on the same display buffer.

Yeah, that is why I am hoping shadow maps will be the ultimate solution for trees and immobile stuff like buildings.

So the summary is Shadow Maps work now with shadow caster and shadow receiver in the same x3drootnode. But I also have to add a light in the x3d (not cge lights whose shadow settings are all for volume shadows) that has a shadowmap x3d node. But the light can be outside of the caster/receiver’s x3drootnode. And then I do something when I want to trigger the shadows to be recalculated? Am I missing anything?

That sounds pretty doable. I already do more in x3dnodes than I do with the higher level cge classes. Shadows do a lot to give the terrain visual depth and topography. Especially when terrain casts shadow on itself in low sun.

No, that’s not what I wrote in Shadow maps in 2025? - #5 by michalis :slight_smile: To recap, I wrote:

Currently, the shadow receiver and the light casting shadows must be in the same scene. It doesn’t matter where the shadow caster is, it can be in different scene.

Ok, thanks, I misunderstood.

Potentially useful example of setting up shadow maps is castle-engine/examples/research_special_rendering_methods/test_rendering_opengl_capabilities at master ¡ castle-engine/castle-engine ¡ GitHub .

Note that it tests a few things, also shadow volumes in the same world as shadow maps. This is where shadow maps are visible:

You can experiment in editor to confirm what I claim above,

Currently, the shadow receiver and the light casting shadows must be in the same scene. It doesn’t matter where the shadow caster is, it can be in different scene.

  • So the light source and shadow receiver must be in 1 scene (castle-data:/primitives_with_shadow_maps.x3dv in that example).
  • Shadow caster can be anything in that world. Below I added TCastleCone, and it casts shadows using shadow map too.

1 Like

Cool, I see the shadowmaps in that demo with the Sometext. I see in the x3dv file the

DEF Light SpotLight {
  location 0 2 0
  direction 0 -1 0
  shadows TRUE
  cutOffAngle 0.6
  defaultShadowMap GeneratedShadowMap { update "ALWAYS" size 1024 }
}

How would I do that with the x3d nodes in code, or is there a way to parse snippets of x3d code when building the x3drootnode? I see the definition for the SpotLight X3DLightNode and shadows boolean but I don’t see a field for the defaultShadowMap.
…
Ok, found Light.FdDefaultShadowMap. So I can set that to a GeneratedShadowMap and should be able to maybe see something. Or maybe I need to set something in the terrain and in the trees still.

Now I have my Spotlight defined in the same x3drootnode (scene) as the terrain tile it is in. I can see its ring. Here is the code added where it is building the tile…

   Shape := TShapeNode.Create;
   Shape.Geometry := Triangles;

   Light := TSpotLightNode.Create;
   Light.Attenuation := vector3(0,0,0);
   Light.Radius := 200;
   Light.Location := vector3( 0, 10, 0 );
   Light.direction := vector3( 0, -1, 0 );
   Light.Shadows := true;
   Light.Intensity := 20;
   Light.CutOffAngle := 1;

   Appearance := InitAppearance;
   Appearance.ShadowCaster := true;
   Appearance.SetReceiveShadows([Light]);
   Shape.Appearance := Appearance;

   shadowmap := TGeneratedShadowMapNode.Create;
   shadowmap.Update := upNextFrameOnly;
   shadowmap.Size := 1024;
   Light.DefaultShadowMap := shadowmap;

   Root := TX3DRootNode.Create;
   Root.AddChildren( Shape );
   Root.AddChildren( Light );          

Then in the tree builder I add shape.Appearance.ShadowCaster := true ;
But I still don’t see them. Am I missing a step?

Maybe try upAlways? In the PostUpdate method, the upNextFrame thing is set to upNone. So it may happen that your trees aren’t ready yet

There are some parameters, like projection near/far, that need to be adjusted for shadow maps to look right – see Shadow Maps | Castle Game Engine . We try to auto-calculate them, but it’s not fool-proof, and it likely goes wrong for non-trivial large worlds. Which is again something we plan to improve, likely by resigning from auto-calculation and just giving you more intuitive options to control them, see that roadmap item.

In general, I would say to make a test on a simple isolated scene. One piece of terrain, one tree, just one X3D model, that can be investigated e.g. in castle-model-viewer on even text editor.

  • It will either work (and you will have a starting point),
  • or you will have a testcase to submit here so we can investigate :slight_smile:

Remember you can dump your X3D node graph using SaveNode.

Note that there’s no point in doing both Appearance.SetReceiveShadows([Light]); and Light.Shadows := true;. See Shadow Maps | Castle Game Engine ,

This is equivalent to adding this light source to every shape’s receiveShadows field. Read on to know more details..

This is also not necessary, things are shadow casters by default, see Shadow Maps | Castle Game Engine ,

By default, every Shape in the scene casts a shadow.

1 Like

It is working. It was working but the light direction seems to have insured no shadows. I turned off other lights and angled the light at 45deg toward the 0,0 of the tile (here just 1)… and I see some shadows. I should use direction light because this is ‘sun’


I like the softer edges of these shadows compared to volumetric shadows.

Hmm. That is casting the shadows on the water, but not the terrain. The water and terrain are related classes. But the water uses texcoords and a texture image, while the terrain uses an effect that is based on the terrain.fs used by tcastleterrain. Would this affect shadow map behavior?

If I disable the effect so the terrain is just white material then I do see the shadows. How can I made shadow maps work with the glsl shader? Maybe I need to pass the shadow map into the shader and mix it in?