_Sebastian_ said:A while back I've posted a fast SSAO code which is OSP aswell.
You might check it out;
_Sebastian_ said:Heyho.
Since La Grandmaster and some other guys asked me how I achieved the SSAO effect, it would be a shame to not share it's basic code...
But since all of my shaders and the structure itself is rewritten from scratch, it's not that easy to apply the effect to your mod.
Due to that your HLSL experience is key to get it working and not to mess everything up.
So let's get started.
I actually use the postFX_sampler1(used for HDR) which is rendered in half-(low hdr) or quarter-(high hdr) resolution anyway and bake the SSAO to it's alpha channel.
After that I blur the sampler based on depth difference by using the ps_main_blur(X/Y) and then pass it to the shaders file, but you also can apply it to the final PostFX pass ofc.
The code is not well documented(I'm lazy ikr) and the SSAO only works in combination with HDR(I recommend low hdr cause of the half res sampler) and enabled Depth Effects.
Make sure you can access the depth buffer.
Code:#if defined(USE_FX_STATE_MANAGER) && !defined(USE_DEVICE_TEXTURE_ASSIGN) //else we can use direct device access with sampler indexes... texture postFX_texture0, postFX_texture1, postFX_texture2, postFX_texture3, postFX_texture4; //non-srgb samplers sampler postFX_sampler0 : register(s0) = sampler_state { Texture = postFX_texture0; }; // scene sampler postFX_sampler1 : register(s1) = sampler_state { Texture = postFX_texture1; }; // hdr-low = 1/2res high = 1/4res sampler postFX_sampler2 : register(s2) = sampler_state { Texture = postFX_texture2; }; // brightness .. AVG and MAX sampler postFX_sampler3 : register(s3) = sampler_state { Texture = postFX_texture3; }; // black sampler postFX_sampler4 : register(s4) = sampler_state { Texture = postFX_texture4; }; // == sampler 2 + WithLuminance??? sampler postFX_sampler5 : register(s5);// depth // sampler postFX_sampler6 : register(s6);// cubic ...black screen // sampler postFX_sampler7 : register(s7);// shadowmap ...white screen // sampler postFX_sampler8 : register(s8);// screen ...half resolution screen // sampler postFX_sampler9 : register(s9);// mesh ...shows random texture // sampler postFX_sampler10 : register(s10);// clamped ...black screen // sampler postFX_sampler11 : register(s11);// font ...shows font texture // sampler postFX_sampler12 : register(s12);// char shadow ...black screen // sampler postFX_sampler13 : register(s13);// mesh no filter ...shows random texture without filter // sampler postFX_sampler14 : register(s14);// diffuse no wrap ...black screen // sampler postFX_sampler15 : register(s15);// grass ...black screen // sampler postFX_sampler16 : register(s0);//reflection ...shows blurry screen // sampler postFX_sampler17 : register(s1);//env ...shows env texture // sampler postFX_sampler18 : register(s2);//diffuse ...shows random diffuse texture // sampler postFX_sampler19 : register(s3);//normal ...shows random normal texture #else #ifdef USE_REGISTERED_SAMPLERS sampler postFX_sampler0 : register(s0); //linear clamp sampler postFX_sampler1 : register(s1); //linear clamp sampler postFX_sampler2 : register(s2); //linear clamp sampler postFX_sampler3 : register(s3); //linear clamp sampler postFX_sampler4 : register(s4); //linear clamp #else sampler postFX_sampler0 : register(s0) = sampler_state{ AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; //linear clamp sampler postFX_sampler1 : register(s1) = sampler_state{ AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; //linear clamp sampler postFX_sampler2 : register(s2) = sampler_state{ AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; //linear clamp sampler postFX_sampler3 : register(s3) = sampler_state{ AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; //linear clamp sampler postFX_sampler4 : register(s4) = sampler_state{ AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; //linear clamp #endif #endif
Set up the static constants.
Code:static const int clip_distance = 1250;// is 1250 by default you can change it in module.ini ...=> far_plane_distance = 1250 #clip distance static const int num_iterations = 8; static const float BlurPixelWeight[num_iterations] = {1.000, 0.875, 0.750, 0.625, 0.500, 0.375, 0.250, 0.125};
Render the SSAO and bake it to the sampler's alpha channel.
Code:float4 ps_main_brightPass(float2 texCoord: TEXCOORD0 ) : COLOR0 { float3 color = tex2D(postFX_sampler0, texCoord); color *= 255.0f; //make it real values.. more accuracy color = pow(color, 2.0f); //SSAO float occlusion = 0.0f; float base_depth = tex2D(postFX_sampler5, texCoord).r; if (base_depth < 1.0f)//1 = clip_distance { base_depth *= clip_distance; float2 normal;//now generate pseudo random values normal.x = frac(sin((texCoord.x + 0.5) * (texCoord.y + 0.5) * 1000.0f) * 1000.0f); normal.y = frac(cos((texCoord.x + 0.5) * (texCoord.y + 0.5) * 1000.0f) * 1000.0f); normal *= 2.0f; normal -= 1.0f; float2 offset = g_HalfPixel_ViewportSizeInv.zw * normal * 250.0f / base_depth; float2 cur_offset; float difference_1, difference_2, check_dist; for(int i = 1; i <= 4; i++) { check_dist = i; cur_offset = offset * i; difference_1 = base_depth - tex2D(postFX_sampler5, texCoord + cur_offset).r * clip_distance; difference_2 = base_depth - tex2D(postFX_sampler5, texCoord - cur_offset).r * clip_distance; // if one sample distance is invalid then use its twins inverse ... almost completely removes haloing if (difference_1 > check_dist && difference_2 < check_dist) difference_1 = -difference_2; else if (difference_2 > check_dist && difference_1 < check_dist) difference_2 = -difference_1; difference_1 = saturate(difference_1 / check_dist); difference_2 = saturate(difference_2 / check_dist); occlusion += saturate((difference_1 * (1.0f - difference_1) + difference_2 * (1.0f - difference_2)) - 0.25); } occlusion *= 2.0f; } return float4(color, saturate(occlusion)); }
Blur the sampler.
Code:float4 ps_main_blurX(float2 inTex: TEXCOORD0) : COLOR0 { float4 total_color = tex2D(postFX_sampler0, inTex) * BlurPixelWeight[0]; float4 cur_color; float2 offset = float2(g_HalfPixel_ViewportSizeInv.z, 0); float2 cur_offset; float base_depth = tex2D(postFX_sampler5, inTex).r * clip_distance * 10.0f; float cur_amount; float2 blur_amount = 1.0f; for(int i = 1; i < num_iterations; i++) { for(int j = 0; j < 2; j++) { if(j == 0) cur_offset = inTex + offset * i; else cur_offset = inTex - offset * i; cur_color = tex2D(postFX_sampler0, cur_offset) * BlurPixelWeight[i]; cur_amount = max(1.0f / i, 1.0f - abs(base_depth - tex2D(postFX_sampler5, cur_offset).r * clip_distance * 10.0f));//based on difference total_color.a += cur_color.a * cur_amount; total_color.rgb += cur_color.rgb; blur_amount.x += BlurPixelWeight[i]; blur_amount.y += BlurPixelWeight[i] * cur_amount; } } total_color.rgb /= blur_amount.x; total_color.a /= blur_amount.y; return total_color; } float4 ps_main_blurY(float2 inTex: TEXCOORD0) : COLOR0 { float4 total_color = tex2D(postFX_sampler0, inTex) * BlurPixelWeight[0]; float4 cur_color; float2 offset = float2(0, g_HalfPixel_ViewportSizeInv.w); float2 cur_offset; float base_depth = tex2D(postFX_sampler5, inTex).r * clip_distance * 10.0f; float cur_amount; float2 blur_amount = 1.0f; for(int i = 1; i < num_iterations; i++) { for(int j = 0; j < 2; j++) { if(j == 0) cur_offset = inTex + offset * i; else cur_offset = inTex - offset * i; cur_color = tex2D(postFX_sampler0, cur_offset) * BlurPixelWeight[i]; cur_amount = max(1.0f / i, 1.0f - abs(base_depth - tex2D(postFX_sampler5, cur_offset).r * clip_distance * 10.0f));//based on difference total_color.a += cur_color.a * cur_amount; total_color.rgb += cur_color.rgb; blur_amount.x += BlurPixelWeight[i]; blur_amount.y += BlurPixelWeight[i] * cur_amount; } } total_color.rgb /= blur_amount.x; total_color.a /= blur_amount.y; return total_color; }
Now you either can apply the SSAO to the final pass FinalScenePassPS and use it as pure PostFX effect...
Code:// return 1.0f - tex2D(postFX_sampler1, texCoord).a;//SSAO only return lerp(color, 0.0f, tex2D(postFX_sampler1, texCoord).a);
...or pass it to the shaders file and apply the SSAO to any shader in order to avoid some issues;
_Sebastian_ said:The shader is actually a PostFX shader, but applied to a PostFX sampler that is passed to the shaders file.
So you can use it on every pixel shader you like, or not...
This way you're able to avoid occlusion artifacts on surfaces with manipulated vertices or materials that are not affected by the depth buffer, eg. particles, water, fog etc.
And here starts the tricky part...
You have to replace the ENV sampler with the specific PostFX sampler, cause all 16 samplers are already used.
So you wont be able to use fake reflections by using env textures anymore... but I dont mind since I use screen space reflections instead.
Code:// Texture&Samplers texture postFX_texture1; #if defined(USE_SHARED_DIFFUSE_MAP) || !defined(USE_DEVICE_TEXTURE_ASSIGN) texture diffuse_texture; #endif #ifndef USE_DEVICE_TEXTURE_ASSIGN texture diffuse_texture_2; texture specular_texture; texture normal_texture; texture env_texture; texture shadowmap_texture; texture cubic_texture; texture depth_texture; texture screen_texture; #ifdef USE_REGISTERED_SAMPLERS sampler ReflectionTextureSampler : register(fx_ReflectionTextureSampler_RegisterS ) = sampler_state { Texture = env_texture; }; sampler PostFXTextureSampler : register(fx_EnvTextureSampler_RegisterS ) = sampler_state { Texture = postFX_texture1; }; sampler Diffuse2Sampler : register(fx_Diffuse2Sampler_RegisterS ) = sampler_state { Texture = diffuse_texture_2; }; sampler NormalTextureSampler : register(fx_NormalTextureSampler_RegisterS ) = sampler_state { Texture = normal_texture; }; sampler SpecularTextureSampler : register(fx_SpecularTextureSampler_RegisterS ) = sampler_state { Texture = specular_texture; }; sampler DepthTextureSampler : register(fx_DepthTextureSampler_RegisterS ) = sampler_state { Texture = depth_texture; }; sampler CubicTextureSampler : register(fx_CubicTextureSampler_RegisterS ) = sampler_state { Texture = cubic_texture; }; sampler ShadowmapTextureSampler : register(fx_ShadowmapTextureSampler_RegisterS ) = sampler_state { Texture = shadowmap_texture; }; sampler ScreenTextureSampler : register(fx_ScreenTextureSampler_RegisterS ) = sampler_state { Texture = screen_texture; }; sampler MeshTextureSampler : register(fx_MeshTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler ClampedTextureSampler : register(fx_ClampedTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler FontTextureSampler : register(fx_FontTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler CharacterShadowTextureSampler:register(fx_CharacterShadowTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler MeshTextureSamplerNoFilter : register(fx_MeshTextureSamplerNoFilter_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler DiffuseTextureSamplerNoWrap : register(fx_DiffuseTextureSamplerNoWrap_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler GrassTextureSampler : register(fx_GrassTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; #else sampler ReflectionTextureSampler = sampler_state { Texture = env_texture; AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler PostFXTextureSampler = sampler_state { Texture = postFX_texture1; AddressU = WRAP; AddressV = WRAP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler Diffuse2Sampler = sampler_state { Texture = diffuse_texture_2; AddressU = WRAP; AddressV = WRAP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler NormalTextureSampler = sampler_state { Texture = normal_texture; AddressU = WRAP; AddressV = WRAP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler SpecularTextureSampler = sampler_state { Texture = specular_texture; AddressU = WRAP; AddressV = WRAP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler DepthTextureSampler = sampler_state { Texture = depth_texture; AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler CubicTextureSampler = sampler_state { Texture = cubic_texture; AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler ShadowmapTextureSampler = sampler_state { Texture = shadowmap_texture; AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler ScreenTextureSampler = sampler_state { Texture = screen_texture; AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler MeshTextureSampler = sampler_state { Texture = diffuse_texture; AddressU = WRAP; AddressV = WRAP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler ClampedTextureSampler = sampler_state { Texture = diffuse_texture; AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler FontTextureSampler = sampler_state { Texture = diffuse_texture; AddressU = WRAP; AddressV = WRAP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler CharacterShadowTextureSampler= sampler_state { Texture = diffuse_texture; AddressU = BORDER; AddressV = BORDER; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler MeshTextureSamplerNoFilter = sampler_state { Texture = diffuse_texture; AddressU = WRAP; AddressV = WRAP; MinFilter = NONE; MagFilter = NONE; }; sampler DiffuseTextureSamplerNoWrap = sampler_state { Texture = diffuse_texture; AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler GrassTextureSampler = sampler_state { Texture = diffuse_texture; AddressU = CLAMP; AddressV = CLAMP; MinFilter = LINEAR; MagFilter = LINEAR; }; #endif #else sampler ReflectionTextureSampler : register(fx_ReflectionTextureSampler_RegisterS ); sampler PostFXTextureSampler : register(fx_EnvTextureSampler_RegisterS ); sampler Diffuse2Sampler : register(fx_Diffuse2Sampler_RegisterS ); sampler NormalTextureSampler : register(fx_NormalTextureSampler_RegisterS ); sampler SpecularTextureSampler : register(fx_SpecularTextureSampler_RegisterS ); sampler DepthTextureSampler : register(fx_DepthTextureSampler_RegisterS ); sampler CubicTextureSampler : register(fx_CubicTextureSampler_RegisterS ); sampler ShadowmapTextureSampler : register(fx_ShadowmapTextureSampler_RegisterS ); sampler ScreenTextureSampler : register(fx_ScreenTextureSampler_RegisterS ); #ifdef USE_SHARED_DIFFUSE_MAP sampler MeshTextureSampler : register(fx_MeshTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler ClampedTextureSampler : register(fx_ClampedTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler FontTextureSampler : register(fx_FontTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler CharacterShadowTextureSampler:register(fx_CharacterShadowTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler MeshTextureSamplerNoFilter : register(fx_MeshTextureSamplerNoFilter_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler DiffuseTextureSamplerNoWrap : register(fx_DiffuseTextureSamplerNoWrap_RegisterS ) = sampler_state { Texture = diffuse_texture; }; sampler GrassTextureSampler : register(fx_GrassTextureSampler_RegisterS ) = sampler_state { Texture = diffuse_texture; }; #else sampler MeshTextureSampler : register(fx_MeshTextureSampler_RegisterS ); sampler ClampedTextureSampler : register(fx_ClampedTextureSampler_RegisterS ); sampler FontTextureSampler : register(fx_FontTextureSampler_RegisterS ); sampler CharacterShadowTextureSampler:register(fx_CharacterShadowTextureSampler_RegisterS ); sampler MeshTextureSamplerNoFilter : register(fx_MeshTextureSamplerNoFilter_RegisterS ); sampler DiffuseTextureSamplerNoWrap : register(fx_DiffuseTextureSamplerNoWrap_RegisterS ); sampler GrassTextureSampler : register(fx_GrassTextureSampler_RegisterS ); #endif #endif
In order to apply the SSAO to a specific shader you first need to get the screen space position within the vertex shader.
Code:if(use_depth_effects) { Out.projCoord.xy = (float2(Out.Pos.x, -Out.Pos.y)+Out.Pos.w)/2.0f; Out.projCoord.xy += (vDepthRT_HalfPixel_ViewportSizeInv.xy * Out.Pos.w); Out.projCoord.zw = Out.Pos.zw; }
And then apply the SSAO to the ambient term of the pixel shader.
Code:if(use_depth_effects) { float4 ssao = tex2Dproj(PostFXTextureSampler, In.projCoord); if ((ssao.r + ssao.g + ssao.b) > 0.0f)//check if HDR is enabled ambient_term *= 1.0f - ssao.a; }
You can use this code to create your own SSAO effect, but dont forget to give me credit.
I've seen your code and tried it as well. But I liked the results of Yoshiboy's ssao much more.
SSAO Comparison. First Image with ssao, second image without ssao.