For some reason, I had PCSS stuck in my mind for the past few days and couldn’t find any code for it so I decided to try and implement it based on NVDIA’s paper. Patched together code all over the internet for the shadow depth test/PCF and then implemented my own PCSS on top of it. It still needs a fair bit of work, but I can definitely post the code if people would be interested in seeing how it works! (Or have any interest in making it look a bit better!)
Hi macdude2 - I would be interested to look at your PCSS - working on a spotlight-reimplementation I plan to walk throughin a Unity Usergroup Talk - PCSS would be a nice addition. How did you integrate it with the rest of Unity Lighting ?
That was the hardest part for sure, fortunately I found some code online that pretty much implemented it for directional lights. I was originally aiming to make the effect work for spotlights too - but I don’t think I found any code for it online, and I wasn’t really satisfied enough with the quality of the pcss here…
Actually tho, found NVIDIA’s actual shader source (which produced the output below). I directly translated it and things almost worked… Still suffers from aliasing. Not sure if there’s any good way to fix this? Seems like NVDIA uses their custom interpolation function, maybe writing something like that would be the trick.
Nice work! I finally got it somewhat working today using their “PCSS Integration” whitepaper. Like you said, it almost works, which made for a maddening time debugging to see what was throwing it. The main problem I had was that you can’t read the declared shadow map’s depth when it’s a Texture2D and requires a sampler, unless I mucked around a fair bit with the built-in cg includes (may try another time though).
They’ve got a complete version here with much more (derivative biasing, cubemaps, all sorts of cool stuff). It’s actually part of a sample pack of a bunch of effects/whatever written for OpenGL ES, but it’s pretty easy to translate, at least much easier than the stuff in their SDK haha.
So I ended up using a command buffer to blit a copy in, which I’ve seen done a few times to get a hold of the shadowmap as a sampler2D, even in the Adam demo’s smoke. I might do some extra smoothing and stuff this way too, depending on performance. But even with these somewhat kludgey methods, I still think I got it working somewhat decently so far.
My main issue is the aliasing between different Poisson sample offsets, which I’ll be working on next and would love to take with you about since you seem to have do it in the first one. You can obviously see it in the photos below, but the main thing I noticed was how much more important PCF is than blocking, at least the way I’ve got it set up.
I also found Nvidia’s use of ordered Poisson points to be lacking, as interleaving them prevents them from aligning when using smaller sample sizes for performance reasons:
Anyways, I’ll eventually be uploading this to GitHub, but I obviously have a lot of cleanup work to do first, both in visuals and code. But I think it’s on the right track, and I bet I can figure out some more from that
Well, I improved it a bit, using the declared shadow map for the final PCF sampling, which at least smoothed out the edges. Still need to implement that “irregular sampling” to trade banding for noise, but I tried it anyways in a game I’m working on, The Universim (I’m trying to convince my boss to let me add them). Luckily, it’s as easy as importing the shaders and adding the script to the light object. Here’s the results outside a test environment:
I’m super pleased with how they performed in game, and I think they really added a lot to the scene (maybe I’m just seeing what I want to see though haha). What do you guys think?
That looks sick! Nice job with that! How did you end up fixing the issues from the Poisson sampling? I know NVIDIA uses a special lerp algorithm to negate the affects of the sampling, but I tried using a standard one and the effects didn’t really go away.
Also, how did you manage to make edges of your shadows appear smooth? – mine look super aliased…
Thanks! I’m not exactly sure what you mean. In that picture, I still have the banding (I have yet to figure out the “irregular sampling” mentioned briefly in their Advanced Shadows whitepaper but I haven’t seen it in any of their implementations, so it can’t be that important), but it’s hidden by being in a more detailed scene, by using a slightly smaller light radius (my boss is a sucker for subtlety, so I made sure to keep it small to help convince him to let me add them), by using a large number of samples (32 each runs fine on my beast), and by using an exponential falloff (1f - (1f - x) * (1f - x)) so that the outsides blended in better (maybe not the most accurate, but it had a nice smoothing effect).
As far as the smooth edges, I used a sampler2D that was blited in by a ComputeBuffer to get the depth for the blocker search, and the DECLARE_SHADOWMAP(tex) texture2D for the PCF Poisson sampling at the end, which is automatically filtered and soft at the edges. So not the best on texture memory, but it looks a bit nicer haha. If only the shadow map had a second SamplerState to use for depth…
Oh, and a benefit of checking out their GitHub version I mentioned, was a “Depth Gradient” solution to the selfshadowing problem described in their Advanced Soft Shadows white paper. I get the dz_duv in the frag shader:
float2 dz_duv = depthGradient(shadowCoord.xy, shadowCoord.z);
// Derivatives of light-space depth with respect to texture2D coordinates
float2 depthGradient(float2 uv, float z)
{
float2 dz_duv = float2(0.0, 0.0);
float3 duvdist_dx = ddx(float3(uv, z));
float3 duvdist_dy = ddy(float3(uv, z));
dz_duv.x = duvdist_dy.y * duvdist_dx.z;
dz_duv.x -= duvdist_dx.y * duvdist_dy.z;
dz_duv.y = duvdist_dx.x * duvdist_dy.z;
dz_duv.y -= duvdist_dy.x * duvdist_dx.z;
float det = (duvdist_dx.x * duvdist_dy.y) - (duvdist_dx.y * duvdist_dy.x);
dz_duv /= det;
return dz_duv;
}
Which I pass into the PCSS, and then into both the blocker and pcfSampler, which use the eyeDepth (zReceiver), dz_duz (something about slopes and gradients), and then the uv offset (poisson * radius):
float z = biasedZ(zReceiver, dz_duv, offset);
float biasedZ(float z0, float2 dz_duv, float2 offset)
{
return z0 + dot(dz_duv, offset);
}
Then you use this to test the depth/shadow sample instead of zReceiver, which helps a bit and is super cheap. EDIT: Didn’t mean to pull an Nvidia on you there and release only some of what you needed haha. I wrote this after a full day of work, so I’m bound to make a mistake here and there haha
@macdude2_1 @TheMasonX Looks good, do you guys have a prototype / alpha shader we could try out (before the release)?
There’s actually a pcss shader on the asset store right now that is much better than anything I did!
@macdude2_1 thanks, though a lot of us indie game jammers are looking for non commercial or FOSS solutions to muck around with
On reddit, MasonX said he would release the source on GitHub. Tho I don’t think he has yet. This is where it will show up apparently. https://github.com/TheMasonX/UnityPCSS
I’d give you my source, but it doesn’t work in Unity5 and I have yet to debug it… Not sure it would be of much use to you.
Hey everyone, I would have some question for the people who have worked on PCSS on Unity. I would like to know if they achieved to handle penumbra width variation as the object get far from the light source.
Any idea about that ? @macdude2_1 @TheMasonX ?
Sorry for the delay, but it’s finally up! As @macdude2_1 said, it’s available here: https://github.com/TheMasonX/UnityPCSS
Saw it on reddit yesterday! Looks very cool, will certainly have to try it out, thanks for making it public!