[font=calibri]It's been a while since my last post. I've been busy with the university and stuff and couldn't make much time to work on my journal, though I've tried a lot of new things, so I decided to share some of them with you guys.[/font]
[font=calibri]If you don't know what PCSS (Percentage-closer Soft Shadows) is, it's a technique NVidia came up with to generated soft shadows with photorealistic variable penumbra size. [/font][font=calibri]I tried to implement this method a while back but I couldn't get as good results as they got on their paper so I though it was just the paper exaggeration that most often happens on graphics papers. So I Forgot about it, until a few days ago that I was playing GTA V on my friends PC and noticed there is PCSS option on the graphics settings, so that was a motivation to re-implement it, and it actually worked well this time.[/font]
[font=calibri]PCSS (Percentage-closer Soft Shadows)[/font]
[font=calibri][/font]
[font=calibri]There are two points about PCSS that makes it convenient. First that it uses normal shadow maps and second that it utilizes PCF (Percentage-closer Filtering, described on previous posts) to filter the shadow map.[/font]
[font=calibri]Let's say you want to calculate the value SE the density of the light that the fragment R receives. First you have to calculate PW the width of the penumbra which is done on the blocker search step, and then you have to filter the shadows which is done using variable kernel size PCF.[/font]
[font=calibri]So PCSS is consisted of two steps, blocker search and PCF filtering.[/font]
[font=calibri]Blocker Search[/font]
[font=calibri]On blocker search the algorithm calculates the average distance of the object blocking light from light. This is performed by projecting F to shadow map space. Consider SMC as the shadow map coordinates of R, the algorithm read texels around SMC and if the distance of a texel from light was less than R's distance from the light, RD, that texel is considered a blocker texel. The algorithm calculates the blocker distance from the light, BD, by averaging the blocker texels' distance from the light.[/font]
[font=calibri]Using the light's Width LW, BD and RD the penumbra width, PW, is calculated as below:[/font]
[font=calibri][/font]
[font=calibri][/font]
[font=calibri]After calculation of PW, kernel size is calculated for the PCF step, this is done by calculating penumbra width after being projected on the on to shadow map space.[/font]
[font=calibri]PCF[/font]
[font=calibri]On this step a simple bilinear PCF (described on previous posts) is applied to filter the edges, the kernel size is available from the previous step.[/font]
[font=calibri]Implementation[/font]
[font=calibri]Shadow map shader[/font][code=:0]//Vertex Shader#version 450in vec3 Vertex;out vec4 Position;uniform mat4 ProjMatrix;uniform mat4 ViewMatrix;uniform mat4 ModelMatrix;void main(){ Position=ViewMatrix*(ModelMatrix*vec4(Vertex,1)); gl_Position=ProjMatrix*Position;}//Fragment Shader#version 450out float Output1;in vec4 Position;void main(){ Output1=length(Position.xyz);}
[font=calibri]Back face is rendered.[/font]
[font=calibri]Blocker search function[/font]float AvarageShadowCasterDepth(vec2 ShadowCoord, float Depth, sampler2DArray ShadowMap, int TextID, int R, ivec2 TextureSize)//reciever's coordinate on shadow map, reciever's depth, shadow map sampler, current layer of the shadow map array sampler, radiouse of the blocker search, size of the shadow map texture{ float RetValue=0.0f; float Count=0.0f; const int start=-R/2;; const int end=start+R; for (int IS=0;IS<4;IS++)//cause a descrete search for (int JS=0;JS<4;JS++) { for (int i=start+IS;i16)//early bail return RetValue/Count; } } return (Count!=0.0f)?RetValue/Count:0.0f;}
Results
Thanks for reading :)