Rhino and shadows (again) wish

Hi guys, Rhino is an important tool for architects now and I need to bring shadows up again. This is a medium to large model in meters and we need better sun shadows.

I still can not understand Rhino’s memory usage slider for shadows, at least for the sun, this is the default:
@ 16 MB:


@ 64 MB:


@ 256 MB:


and @ 1 GB :


And I can not get my head around this.

I presume the shadow map is an 8 bit grayscale and at 4096x4096 this should result in a 16MB image. At 32 bit it should result in a 50MB image.

At 16K by 16K at 24 bit I get 768MB, and that is 256 Megapixels… so isn’t this strange? That should result in crisper shadows than any other software I have ever used. It is at least strange to me, and has always been.

Can you please add a separate option for the sun shadow @jeff ?
I imagine the shadow memory slider affects all shadow types equally and is a “max memory used container” as I see that when I drag the slider from 0 to 1GB the used vram also jumps up with 1GB so there is consistency there. And the skylight slider does not affect the vram.

This was tested in Rhino 8 and I got the same results in Rhino 7 when I tested there back in the days.

5 Likes

OK, so I did a test I want to share.

Here I have adjusted shadows to use 1GB and remove all softening and stuff to get it as crisp as possible.

The scene has a sculpture and a big plane, the floating view is a parallel view that simulates the angle of the sun and the sun shadows shall cover all objects. If I viewcapturetofile with 16000x16000 px I get an image that resembles the shadow quality.

Here is the file: (Rhino 8)
vram and shadows.3dm (1.3 MB)

And here is a snipet of the captured image from photoshop:
(You can see the resolution of the sculpture resembles the resolution of the shadow in the image above, if you count the pixels (lines) between the feet and the crotch of the shadow you get to 12 px, and in the image with the red figurine you’ll get to around 14, so pretty much the same)

So it makes me think that at 1GB the sun has something around a 16000x16000 shadowmap. Which is far more than I expected, and I see that I stand corrected regarding my previous assumptions.

So that brings me to the next part. Could the sun’s shadowmap be more view dependent? Where it uses a combination of where the camera target is centered and what the view actually sees to define the center of the shadow map? I also see that if I have one display mode with one shadow setting and another with another shadowsetting then these don’t seem to reuse the suns shadowmap (obviously) so could maybe the sun have it’s own value that is above any display mode?

All of this is centered around user need, not hardware resourses, software development nor display speed. It’s more of a "how can we get better local sun shadows on larger projects?

I know game engines like unreal operate with local oriented shadows that also uses two resolutions where one covers the center of the area around the “player” and then fades over to a lover resolution to cover the area around. In Rhino this is different since we don’t have a player, but we do have a camera target and a camera, so if we dig into it we should be able to find a good solution. I guess this isn’t high on the priority list since few users know how to wish for stuff like this, me included, but it would push Rhino in the right direction for architectural use in the years to come.

3 Likes

What I am trying to explain above is this:

The camera plane sees the yellow rectangle
(here easy to see as 2D since the world around the player is flat, in other models it would be more complex since it is in 3D, but that is solvable by shooting some rays to get an average or something)

And now the sun’s shadows only have to be calculated to cover that area, but from the suns perspective. I guess that’s kind of what the Camera Based Clipping Bubble is trying to solve, but it isn’t. The way I see the bubble is that it doesn’ affect the suns focus point and extent, but rather what objects the sun should take into consideration to figure out what extent it must have.

If I drag the Camera Based Clipping Bubble slider I don’t get a gradually better shadow, it jumps from “bad” (nothing) to “good” and then suddenly back to “bad” again when the bubble is big enough to cover the big rectangle on the ground.

So I propose a “CameraTarget Based Clipping Bubble” instead, when I think about it…

1 Like

I can’t say I follow all the technical dimensions you’re digging into here @holo but I do agree that Rhino’s shadow functionality has always been disappointing to me from a purely graphical standpoint.

For quick and dirty visualisation I export into Sketchup for its crisper shadows and sketchy line options.

1 Like

@Holo You really need to understand what “shadow mapping” is, what it does, and how it works before trying to reverse engineer it by counting pixels. There are a handful of real-time shadow mechanisms (tricks), all have their pros, all have their cons. The mechanism Rhino uses is called “Shadow Mapping”…which consists of a 32bit depth buffer AND a grayscale image.

The depth buffer is basically the ENTIRE scene rendered from the point of view of the light source… This produces depth values relative to a position somewhere out in space.

Some background…

In order to render any kind of 3D frame, you need 3 things…

  1. A point in space (the camera or eye)
  2. A direction (where the camera is looking)
  3. A frustum that defines the viewable area

A directional light source (i.e. The Sun) only supplies 1 of those… The direction. It has no point in space (and no, you cannot consider where the directional light object was placed in the scene). This means Rhino needs to cook up the other 2 requirements. So where does the camera get placed to encompass the entire scene? …and… How big does the frustum need to be?.. And yes, it MUST encompass the entire scene… Why? Because objects outside the view’s frustum can cast shadows down onto and into the field of view… So even though you can’t see objects in the view, those objects outside the view can and will cast shadows onto objects you can see in the view…depending on light position relative to camera direction.

Given that…

The depth map…
Rhino already has tools that automatically compute some of the unknowns…it’s basically a Zoom Extents…which calculates all the near/far clipping planes, camera target and camera position… So what about the frustum? That’s where the “size” value you discuss above comes into play. The frustum is basically an NxN field of view… where N is the specified size. So a 4k x 4k x 32bit depth map yields 16MB (rhino’s default) …and a 16k x 16k x 32bit map yields 1GB. … Since these are stored on the GPU as textures, most GPUs/drivers today don’t allow for texture sizes larger than 16k, so that’s the current cap.

So we now have a direction, a point in space, and a frustum… The “light” is now acting like a camera, and the scene is rendered from the point of view of the light…producing depth values relative to the computed point in space. Hopefully it’s obvious that the larger the scene, the further out in space that point needs to be placed…and when you move the point further away, it means objects get smaller, and take up less space in frustum…less space means fewer depth values (pixels) generated for those objects. Fewer depth values means lesser details (i.e. fidelity goes down).

The grayscale image…
It’s just a bitmap that’s the exact size of the viewport’s window.
Once a depth map exists for a given light, the scene you see in the viewport is then rendered. When an object is “hit” by a view ray (for a given pixel), another ray is shot back towards the light source. If that ray intersects the light’s frustum, the light’s depth value is obtained at that intersection. That depth value is then compared with the distance from the light to the hit object… If it’s less than the hit object’s distance, then the object (pixel) is “in shadow”, and a black pixel is produced…otherwise a white pixel is produced…resulting in a black and white image that’s the size of the viewport window. This is really the “shadow map”…because it maps over the top of the viewport frustum, showing where shadows occur and where they don’t. It ends up being a grayscale image due to softening, blurring and multi-sampling techniques that try to eliminate the pixelization that can occur along the edges of shadows.

Once the shadow map exists…it is used by the rendering shaders to determine if a given pixel is in-shadow, and the appropriate shading is applied.

So that’s basically shadow-mapping in a nutshell…

The Pros:

  • It’s fast
  • It’s easy
  • No massive triangle list needs to be created AND sorted
  • It’s easily adapted to support multiple (infinite) light sources.

The Cons:

  • It can be very memory intensive
  • It creates self-shadowing artifacts (the depth map value represents the same object/pixel that’s being hit, so it thinks that pixel is in-shadow, when it’s really looking at itself)
  • Its quality is scene and resolution dependent.

Having said all of that…

Your example above is only taking into account the object that you know (or want to) casts a shadow…but you’re forgetting about the plane it’s sitting on. Rhino has no way of knowing that you only want the red object to cast a shadow (well, it does, which I’ll mention in a bit)…so it has to take into account EVERYTHING in the scene when computing the light’s frustum, that includes the large planar object you’ve placed in the scene. Planes used as ground planes is just a bad idea all around…which is why the “Groundplane” object should be used…I know it has its own set of problems…so let’s not got there in this topic. Basically select EVERYTHING in your scene and run ZS (ZoomSelected)… And see how much screen real estate your red object takes up. Rhino’s shadow mapper does a better (tighter) job at this, but you get the idea. Here’s an example that I’ve done…

Red object on a large planar object…but I’ve zoomed in on the Red object…

Looks like crap! right?

That’s because the depth map really includes the plane and the red object…which would look something like this:

Everywhere you see red, is what gets used when determining “shadows”… and zooming in on the object has no impact.

That’s why these two object properties exist:

  • Casts shadows
  • Receives shadows

If you know that an object won’t be casting any shadows, or you simply do not want an object to cast shadows (or receive them), then you should turn OFF “Casts shadows”… What that does is it removes that object from the equation, which can/will allow for a much closer viewpoint to be used. Turning if OFF for the planar object in my example now yields this:

Much better, and I’m still at the default 16MB shadow memory usage.

This is exactly what the “Bubble” is supposed to do (although I’ll admit that I haven’t looked at that part of the code in many, many years, so I wouldn’t be surprised if it’s not working now)… But it basically discards all objects outside the bubble from the equation, and only objects inside the bubble are used… again, that’s how it’s supposed to work.

Lastly, the banding lines in your example look like “self-shadowing” artifacts… i.e. the plane is casting a shadow onto itself… This too can be controlled in the Shadows settings… which basically adds a bias in the comparison with the depth values mentioned above… It may be that we need to revisit that and change or allow for higher settings.

Sorry for such long winded reply… I will look what’s in store for V9, and see if we can do something about a single, dedicated light source (The Sun)… which can use a different mechanism altogether… Only worrying about 1 light source, for a single light type, is an order of magnitude easier than trying to support infinite multiple light types, all casting shadows…in different ways.

Hopefully that sheds some light on the topic :wink:

-J

17 Likes

Hi Jeff! As always thank you for your in depth and direct answers, they are food for thought and inspirational.

I could not agree more, and after watching “easy to understand” tutorials like these, I realized that I didn’t have enough background knowledge of programming to understand it:

So therefore I tried to understand as much as I still could by doing the “pixel counting” and it did make me understand more of what I don’t understand. But what i do understand is the principle and through there I like exploring approaches to get closer to what we as users desire and experience in other applications. Far greater minds than mine has spent countless hours during the last two decades on achieving our common goal: Better shadow-experiences at minimal cost.

When working on large landscapes and big buildings we have no dependent on perfect shadows for the entier scene at the same time, as that would just be too much data for both calculation and memory, but we would like shadows for the entier scene and better shadows for where we are looking at details. (Kind of how the eye works, if our entier field of view was in focus and at attention our brain would be overstimulated, thus focus-point and peripheral view.) So if something like this could be applied for the sun then we would be thrilled.

Our problem is that we want the terrain and the big buildings to cast shadow, we just don’t need them to cast “great shadows” all around, all the time… If that wasn’t the case I would have turned off shadow casting much more.

I see that spotlights aren’t affected by the size of the entire scene, but by the extent of the cone", is that right? So if the sun could take all objects into consideration but only generate shadows for what the camera sees, then I think we are onto something. If this is possible through standard shadowmapping, where the map operates with another one bounding box than the shadow casting objects, I do not know. And if this is possible on both OpenGL and Metal, on both apple, Intel, nVidia and AMD hardware, without causing crashes, is far beyond what I can understand. But I do know that we want it, and it has been solved elsewhere. So I keep on wishing until I get a “this will never happen in Rhino” message. That’s when I know the battle is lost, and I’ll promise to move on.

And the banding and other artifacts is something I learned from you back in 2011 when I got the shadow plugin for Rhino 4, I just turned all that off to see the raw shadow data. So I hope you understand I am not barking up this old three with less knowledge than before, maybe not more knowledge, but at least not less )

Thanks for all the info, I feel less like stumbling around in the dark now :wink:

1 Like

You’re forgetting that objects outside of the camera can still affect the shadows. Think of a large building that is not visible, but what you see would still be in its shadow…

2 Likes

Correct… That’s because spotlights provide all 3 of the rendering requirements I mentioned above… A point in space (the point of the cone), A direction, and a frustum (the extent of the cone)… So Rhino doesn’t have to cook up anything, because the placement and size of the spotlight is pretty much the same thing as placing a camera and a field of view. You can easily force the same problem using spotlights by making long, narrow cones… but spotlights are really supposed to be used and placed in the scene as if they were real world objects, so drawing long, narrow spotlights doesn’t make a lot of sense.

Aside from what Nathan already pointed out, you might be ok with some objects outside the camera not casting shadows (although most Sun studies I’ve seen, actually want this, and is exactly why they run the studies in the first place)… You’re still forgetting about the huge ground plane problem I mentioned. The camera can still see part of the ground plane, and thus, will include the entire plane in the calculations… Again, that’s what the “Casts shadows” property is all about.

Back to the “Bubble”… That feature is supposed to work exactly like what you’re explaining/wanting… The bubble not only defines what objects should be used, but also the frustum size… You’ll still suffer the problem about objects outside the camera view, but it sounds like you’re ok with that. I will try to take a look at why that’s not working and see if I can easily tune it up…if it looks like an easy fix, I’ll just do it…if it starts going down a deep rabbit hole, it’s going to have to wait… and I’m pretty sure we didn’t implement it at all on the Mac/Metal side of things, so that’ll be even a longer timeframe.

Thanks,
-J

1 Like

I agree, this is the price to pay for a target based shadow bubble, but those objects would still cast shadows into what the camera sees if they are visible from the lightsources and they thus cover the objects or the ground in front of the camera, even if they are not visible, would they not?

Sounds fantastic! And apparently what I have seen in other realtime engines (games and TwinMotion) seems to be called Cascaded Shadow Maps (CSMs) or Parallel-Split Shadow Maps (PSSMs). I bet you already know this, but on the odd chance that somebody else reads this they might find it interesting.

2 Likes

Hi @jeff,

These were my “go-to” settings for objects that only need to receive shadows to improve the quality of cast shadows in larger models. However there is a problem now that most of the time we use both Skylihght/AO shadows and direct light shadows. Once the “Cast Shadows” is turned off for, let’s lay’ large ground surfaces, they also stop “casting” the AO shadows which is highly undesirable and is no longer a good solution or help.
In order to make it work again, we should have an option to still be able to keep the AO shadow casting/receiving ON, while not counting towards cast shadows map. My understanding is tha AO shadows and direct shadows are 2 separate display “passes” that get combined, so it would be mostly a matter of adding some UI options. Do you think that would be achievable?

thanks,

–jarek

I don’t understand. A shadow is a shadow be it from direct light or indirect light…

I think he refer to the fact that AO light isn’t affected by the shadow memory usage and thus it would be nice to have AO cast shadow as a separate option from Sun (and other light source) cast shadows.

I second that wish if I understood it correctly :slight_smile: