mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.3k stars 35.36k forks source link

Add light source radius to point and spot lights to enable accurate soft shadows? #7672

Closed bhouston closed 6 years ago

bhouston commented 8 years ago

@MasterJames has proposed that we implement Percent Closer Soft Shadows that is used in GTA V. It looks amazing, even though it was invented about a decade ago:

http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf

I was looking at this quickly. The main equation for PCSS is the calculation of an accurate penumbra width. That equation (see step 2 on page 1) is this:

widthPenumbra = ( distanceToReceiver – distanceToBlocker) * radiusLight / distanceToBlocker;

This equation needs to be modified for directional lights that do not have positions. I would suggest it becomes something like this (which is what I believe how it is implemented in V-Ray and UE4):

widthPenumbra = radiusLight;

The blocker for implementing both of these equations in ThreeJS is that our lights, both point, spot and directional, do not have light radii. This means that if we hope to calculate correct penumbra, we need to add radius to these lights.

Can I make a PR that does this? I can make use of the lightRadius parameter in the lighting equations that I made in this PR here: https://github.com/mrdoob/three.js/pull/7609

What should the name be?

/ping @erichlof, @benaadams

bhouston commented 8 years ago

@WestLangley does this make sense to you? I think that it would further improve our physically based lighting intensities and allow us to extend that accuracy to penumbra.

WestLangley commented 8 years ago

Sure. : - )

Our DirectionalLight is essentially Sun. What would the light size be in that case? Maybe the paper is more about area lights?

Regarding the terminology, "light radius" sounds correct. It should be "cutoff distance", IMO.

Maybe "light diameter" would be more clear than "light radius".

bhouston commented 8 years ago

Our DirectionalLight is essentially Sun. What would the light size be in that case? Maybe the paper is more about area lights?

I may be incorrect about the equation for directional lights, but the sun does have a size, it is just huge but very far away. I know that the shadow from a sun/directional light should be crisp at the point of contact between the object and the surface on which it is casting a shadow but it should then increase in width (image from V-Ray):

sunandsky_24

In this equation:

widthPenumbra = ( distanceToReceiver – distanceToBlocker) * radiusLight / distanceToBlocker;

I think we can estimate the radiusLight / distanceToBlock for a sun with 1/215, which is the ratio of the sun's radius over the distance from the sun to the earth (source). Thus for the directional light one could then use radiusLight as a multiplier on the sun's radius. Thus it would become:

const float cSunRadiusDividedByDistanceFromEarth = 1.0 / 215.0;
widthPenumbra = ( distanceToReceiver – distanceToBlocker) * radiusLight * cSunRadiusDividedByDistanceFromEarth;

V-Ray uses a sun radius multiplier for its sun lights. Although it also offers on its directional lights a light radius, which may be something like this in order to get the crisp shadows at the contact point:

widthPenumbra = ( distanceToReceiver – distanceToBlocker) * radiusLight;

I can leave out a penumbra calculation for the directional lights though because it isn't clear what to use.

BTW area lights do not need a light radius because they have width/height that define their emission area.

WestLangley commented 8 years ago

BTW area lights do not need a light radius

Area lights can be circular.

I prefer "diameter" in that case, as it is akin to "width". Just my opinion, of course.

bhouston commented 8 years ago

Ah, sorry, you are correct, I often thing of Area as rectangular but you are right there are a few different types.

I went with the simple name radius for now as that mirrors what we do with SphereGeometry, CircleGeometry. Should I add a property alias to diameter as well? I have not seen any renderers use diameter instead of radius, although a lot of them qualify radius in some way, like UE4 uses the term sourceRadius, while Frostbite uses the term sphereRadius. V-Ray and Arnold just use the term radius.

Some day it would be awesome if ThreeJS supported the following:

mrdoob commented 8 years ago

Maybe instead of adding radius to PointLight, we should just add SphereLight?

bhouston commented 8 years ago

Maybe instead of adding radius to PointLight, we should just add SphereLight?

Good point. I guess SphereLight is a PointLight when you set the radius to 0. Not sure if we should rename the light or make a new one or just keep the name and add parameters.

One could follow UE4 as a guide. It only has a PointLight type but its point light supports both a "source radius" and a "source length" so it can do both Point (radius = 0, length =0), Sphere (length = 0) and Capsule light types: https://docs.unrealengine.com/latest/INT/Engine/Rendering/LightingAndShadows/LightTypes/Point/index.html

mrdoob commented 8 years ago

SphereLight could extend PointLight. Same glsl for both, but more intuitive API.

bhouston commented 8 years ago

SphereLight could extend PointLight. Same glsl for both, but more intuitive API.

Will do.

WestLangley commented 8 years ago

Radius is fine with me.

SphereLight could extend PointLight.

Ultimately, SphereLight will be a type of AreaLight if we follow the Frostbite convention. So looking ahead, I would do that, instead of extending PointLight. AreaLights all have the same intensity units, which may or may not be the same as PointLight units.

CapsuleLight vs TubeLight nomenclature could depend on whether the end-caps are rounded or not.

bhouston commented 8 years ago

Ultimately, SphereLight will be a type of AreaLight if we follow the Frostbite convention. So looking ahead, I would do that, instead of extending PointLight. AreaLights all have the same intensity units, which may or may not be the same as PointLight units.

In Frostbite both punctual and area lights have the same intensity units: lumens and power. We can start with a SphereLight derived from PointLight and later switch if it need be? Deriving from PointLight for now is the least amount of new code.

CapsuleLight vs TubeLight nomenclature could depend on whether the end-caps are rounded or not.

Tube light in Frostbite has rounded ends, so they are the same as UE4's Capsule lights, see page 40 of http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf

WestLangley commented 8 years ago

In Frostbite both punctual and area lights have the same intensity units: lumens and power.

Actually, Frostbite also supports luminance units for AreaLights according to the PDF.

But I think it would be OK to use the same units (lumens) for both punctual and area lights.

This means the illuminance of a SphereLight (illumination of the scene) will be independent of the radius of the light. That is a consequential API design decision, but a decision I would support.

CapsuleLight vs TubeLight

Whichever you like better is fine with me. Capsules aren't normally bent, so that may be a better term.

bhouston commented 8 years ago

Actually, Frostbite also supports luminance units for AreaLights according to the PDF.

Right. Here is table 8 on page 28:

image

What I am trying to say is that just by adding a radius to the Point and Spot lights doesn't mean they have to become a type of Area lights or a SphereLight. They can just be spot and point lights with correct soft shadows.

That would keep things very simple.

mrdoob commented 8 years ago

Maybe we can add the radius parameter to the shadow then?

WestLangley commented 8 years ago

I like the idea of adding the new soft shadow type, and of switching to SI units, but I am not sure adding radius to punctual lights makes sense to me. Would @mrdoob's idea be OK with you? Or a penumbra parameter for shadows?

bhouston commented 8 years ago

Yeah, @mrdoob's idea is a great one. It would achieve what I am looking for for Point and Spot and it is reusable as is for other area light types.

MasterJames commented 8 years ago

Excellent work guys. My only other integrated thought I've been trying to find a place to slip in. Is about some kind of light helper that gets the total scene size as a light power multiplier. I imagine you'd have to turn it on (not a default) but it would solve the problem when you scale a scene and the lack of distance vs lumes falloff. Lights could have illumination intensity in percentage of 10 meters say. Then the light intensity would increase for larger scenes or possibly effect the lights' sourceSize which could also use a similar dimensions helper to adjust the shadow softness. Ideally it would also adjust based on frame rates achieved and a valued priority for effects the user could change but the default would be robust. So first reflection maps or thier resolutions are sacrificed then shadow map size or maybe texture size etc. Anyway just some more crazy ideas maybe the discussion will trigger a more attainable idea or direction in the future. And yes it seems relevent that a closeup of a shadow would jack up the resolution dynamically as you get closer for a smoother edge, or yes the softness of that calculation. Basically in interactive graphics it is a procedural problem, and should be handled in the renderer when possible that dynamic way too. Similar to having higher geo detail for a window (amongst many) on a skyscraper as you get closer. Ultimately with point clouds you get intelligent sampling through lookup optimization or something like that (as I am understanding those ideas only in general thus far.) It's related and important for map lookups unless GPU performance on mobile gets game station performance/ram or some remote 'shield' like RAAS rendering as a service. Maybe that could also involve a different idea of extending distribution of the lookup of larger maps etc. Again just thinking aloud thanks for listening. I believe THREE.js will replace the DOM in my world. Signed MJ aka Night Dreamer

bhouston commented 6 years ago

Implemented as radius on light.shadow.