|
Notes on the softShadowSpot shader.
This shader is an implementation on the article in the SIGGRAPH 2006, course 25, Disney bonus section, “Shadow Map Bias Cone and Improved Soft Shadows”. The idea is the following. First, we sample the depth map to get the distance between the shading point and the point that casts the shadow, and we vary the blur size according to the ratio of these distances. Then, we resample the depth map (with variable search radius this time), and we give a bigger bias to the samples that are further to the shading point (creating a “cone” of sampling rather than a “disc”). This reduces the self-shadowing artifacts. And while we know the distance of the occluding object, we will try to add the capability to decrease the shadow opacity as this distance increases. For readability, I’ll use “Ps” for the surface point currently being shaded, and “occluder” for the point which depth was stored in the depth map. I also tried to use “distance” as the 2d distance between two points on the depth map, as opposed to the “depth” in 3d space. The procedures that this shader refer to are declared within the shader for conciseness reasons, and because they are designed for the specific needs of this shader, hence probably not useful elsewhere. You can see the full shader code at the bottom of the page.
light […] This doesn’t need much explanation. The reader will most likely have its own light shader that he may want to modify.
Here, we want to find the average distance between Ps and the occluder. We first need to find the depth of Ps. // get the s and t coord of Ps on shadowmap // get Ps distance from shadow plane Note: we can’t use L because the data stored in the depth map is the distance between the points and a plane (crossing the light), and not the distance of the light. Then we sample the depth map around Ps and average the occluder depths. Note that is the depths found by the samples are larger than Ps’ depth, no occluder was found and the sample is discarded.
// get the average occluder depth in shadowmap around Ps Where the sampleShadowMap function is defined as the following /* We will talk later about sampleCoordX and sampleCoordY as well as the HammersleyToRadial function. The sampling radius is user-defined with the maxRadius parameter.
We then compute the shadow blur width depending on the averaged occluder depth (the ratio between the occluder depth and Ps depth in fact, because it has to be relative to the position of the light). // compute the radius of the filter minRadius is also a user-defined parameter.
We can now sample the depth map again with the search radius found in the preceding step, to find the shadowing value. The use of a shadow() shadeop (to which we pass varying “width” value) leads to unacceptable self-shadowing artifacts. The self-shadowing is being caused by points within the filter region, which are closer to the light plane (smaller depth in the map). We then need to sample the map with a custom loop, to add extra bias values to the samples that are further away from Ps’ coordinates on the depth map. The amount that is added to the sample’s bias is scaled by a user-defined parameter. It is called biasConeSlope here, but may eventually be named “selfShadowingReducer” in a more artist-friendly nomenclature. // get the shadow occlusion of Ps float sampleDepth, distFromPs;
Now that we have a prop soft shadow, we may use the knowledge to the depths of Ps and the occluder to modify the shadow opacity (the opacity decreasing as Ps moves away from the occluder). Different approaches may be implemented. In the presented version, the shadow opacity decreases linearly from a cut-on distance (shadDecayDistMin) to a cut-off distance (shadDecayDistMax). The amount of decrease is also user-defined (shadowDecay). // decrease light attenuation as Ps moves further from occluder The depth map generation is important to get correct values. The value used for the shadow decay should ideally be the distance between Ps and its closest occluder point. We obtain that when the depth map generation type is “max”, and if the occluding objects are double-sided.
The SIGGRAPH article on which this shader is based suggests the Hamersley Point Set for the generation of sample points. What I understand from the process is the following.
// find the num of decimals Then, for every sample
// generate binary fractions and their reversed Note that the order of the digits is already reversed here.
// convert back to floats
Now, you will notice that the distribution of samples, always between 0 and 1, is almost evenly distributed. But this holds up only if the number of samples is a power of 2 (…, 16, 32, 64, 128…). Otherwise, the binary number (the x coordinate in this case) never goes as far as it can, given the number of digits. We thus have to scale the x coordinate accordingly to keep it in its full range. This doesn’t happen for the reversed digits (the y coord) because they alternate naturally between low and high value (their first digit is always oscillating between 0 and 1). Now that we have our samples in a unit square, let’s transfer the values to get radial coordinates. We want our samples to stay within a given radius around Ps (or the projection of Ps on the depth map, more precisely). We will use the x coordinate as angle and y as the radius. The x coordinate should be scale to 2p (or 360 degrees), and y to the given radius of the sampling disc. float angleOfSample = sampleCoordX * 2 * PI;
Notice that the samples are denser around the center. This is alright for the sampling of the average occluder distance (see the SIGGRAPH section), but we need an uniform distribution for the shadowing sampling. This is achieved by taking the square root of the y coordinate to compute the radius. // if these samples are for light shadowing sampling -This implementation does not currently supports deep shadow maps. -The procedures that the shader calls are defined inside the shader. This choice was made for conciseness, and because they are more likely to be used exclusively by this shader. -This was developed using 3Delight renderer. -Brief benchmarking of this shader: Normal z-depth Shadow Map Soft Shadow Shader (z-depth Map) Ray-traced Shadows (with equivalent -even lower- quality) -The MEL script that I used to test the Hammersley distribution can be found here. -The RSL code node that I used to test the Hammersley on a square patch can be found here.
This shader is an implementation of the SIGGRAPH 2006, course 25, “RenderMan For Everyone”, the Disney Bonus Section, “Shadow Map Bias Cone and Improved Soft Shadows”. I used as reference this pdf. -a web page of the RenderManAcademy.com ( archived here ) for the shadow map coordinates of Ps. -Advanced Renderman for various principles and examples. -The 3Delight renderer documentation for various syntaxes and examples. -a web page of MathWorld for the Hammersley Point Set Many thanks to Andreas Bauer, who explained to be how to convert to and from binary numbers, and helping me to understand the Hammersley Point Set principle.
|
||
|
- home - curriculum - competences - gallery -MEL scripts - shaders - contact - links - |
||