/*
-Volumetric SSAO-
Implemented by Tomerk for OBGE
Adapted and tweaked for Dark Souls by Durante
Modified by Asmodean, for accurate surface occlusion, distance, and dynamic positional depth offsets, for Dark Souls
*/
/***User-controlled variables***/
#define N_SAMPLES 32 //number of samples, currently do not change.
#define M_PI 3.14159265358979323846
extern float aoRadiusMultiplier = 0.25; //Linearly multiplies the radius of the AO Sampling
extern float ThicknessModel = 50.0; //units in space the AO assumes objects' thicknesses are
extern float FOV = 75; //Field of View in Degrees
extern float luminosity_threshold = 0.5;
#ifdef SSAO_STRENGTH_LOW
extern float aoClamp = 0.35;
extern float aoStrengthMultiplier = 0.8;
#endif
#ifdef SSAO_STRENGTH_MEDIUM
extern float aoClamp = 0.25;
extern float aoStrengthMultiplier = 0.9;
#endif
#ifdef SSAO_STRENGTH_HIGH
extern float aoClamp = 0.1;
extern float aoStrengthMultiplier = 1.0;
#endif
#define LUMINANCE_CONSIDERATION //comment this line to not take pixel brightness into account
/***End Of User-controlled Variables***/
static float2 rcpres = PIXEL_SIZE;
static float aspect = rcpres.y/rcpres.x;
static const float nearZ = 1.0f;
static const float farZ = 3000.0f;
static const float2 g_InvFocalLen = { tan(0.5f*radians(FOV)) / rcpres.y * rcpres.x, tan(0.5f*radians(FOV)) };
static const float depthRange = nearZ-farZ;
Buffer<float4>g_Buffer;
texture2D sampleTex2D;
sampler sampleSampler = sampler_state
{
Texture = <sampleTex2D>;
MinFilter = ANISOTROPIC;
MagFilter = ANISOTROPIC;
MipFilter = ANISOTROPIC;
AddressU = Clamp;
AddressV = Clamp;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
MaxAnisotropy = 16;
};
texture2D depthTex2D;
sampler depthSampler = sampler_state
{
texture = <depthTex2D>;
MinFilter = POINT;
MagFilter = POINT;
MipFilter = POINT;
AddressU = Clamp;
AddressV = Clamp;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
MaxAnisotropy = 16;
};
texture2D AOTex2D;
sampler AOSampler = sampler_state
{
texture = <AOTex2D>;
MinFilter = POINT;
MagFilter = POINT;
MipFilter = POINT;
AddressU = Clamp;
AddressV = Clamp;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
MaxAnisotropy = 16;
};
texture2D cust_NoiseTexture < string filename = "ssao/RandomNoiseB.dds"; >;
sampler noiseSampler = sampler_state
{
Texture = <cust_NoiseTexture>;
MinFilter = POINT;
MagFilter = POINT;
MipFilter = POINT;
AddressU = Wrap;
AddressV = Wrap;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
MaxAnisotropy = 16;
};
texture2D frameTex2D;
sampler frameSampler = sampler_state
{
texture = <frameTex2D>;
MinFilter = ANISOTROPIC;
MagFilter = ANISOTROPIC;
MipFilter = ANISOTROPIC;
AddressU = Clamp;
AddressV = Clamp;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
MaxAnisotropy = 16;
};
texture2D prevPassTex2D;
sampler passSampler = sampler_state
{
texture = <prevPassTex2D>;
MinFilter = ANISOTROPIC;
MagFilter = ANISOTROPIC;
MipFilter = ANISOTROPIC;
AddressU = Clamp;
AddressV = Clamp;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
MaxAnisotropy = 16;
};
texture2D focusTex2D;
sampler focusSampler = sampler_state
{
Texture = <focusTex2D>;
MinFilter = ANISOTROPIC;
MagFilter = ANISOTROPIC;
MipFilter = ANISOTROPIC;
AddressU = Clamp;
AddressV = Clamp;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
MaxAnisotropy = 16;
};
texture2D normalTex2D;
sampler normalSampler = sampler_state
{
Texture = <normalTex2D>;
MinFilter = POINT;
MagFilter = POINT;
MipFilter = POINT;
AddressU = Clamp;
AddressV = Clamp;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
MaxAnisotropy = 16;
};
struct VSOUT
{
float4 vertPos : POSITION;
float2 UVCoord : TEXCOORD0;
};
struct VSIN
{
float4 vertPos : POSITION;
float2 UVCoord : TEXCOORD0;
};
VSOUT FrameVS(VSIN IN)
{
/*VSOUT OUT;
float4 pos=float4(IN.vertPos.x, IN.vertPos.y, IN.vertPos.z, 1.0f);
OUT.vertPos=pos;
float2 coord=float2(IN.UVCoord.x, IN.UVCoord.y);
OUT.UVCoord=coord;
return OUT;*/
VSOUT OUT;
OUT.vertPos = IN.vertPos;
OUT.UVCoord = IN.UVCoord;
return OUT;
}
static float2 sample_offset[N_SAMPLES] =
{
float2(1.00f, 1.00f),
float2(-1.00f, -1.00f),
float2(-1.00f, 1.00f),
float2(1.00f, -1.00f),
float2(1.00f, 0.00f),
float2(-1.00f, 0.00f),
float2(0.00f, 1.00f),
float2(0.00f, -1.00f),
float2(1.00f, 0.00f),
float2(-1.00f, 0.00f),
float2(0.00f, 1.00f),
float2(0.00f, -1.00f),
float2(1.00f, 1.00f),
float2(-1.00f, -1.00f),
float2(-1.00f, 1.00f),
float2(1.00f, -1.00f),
float2(1.00f, 0.00f),
float2(-1.00f, 0.00f),
float2(0.00f, 1.00f),
float2(0.00f, -1.00f),
float2(1.00f, 0.00f),
float2(-1.00f, 0.00f),
float2(0.00f, 1.00f),
float2(0.00f, -1.00f),
float2(1.00f, 1.00f),
float2(-1.00f, -1.00f),
float2(-1.00f, 1.00f),
float2(1.00f, -1.00f),
float2(1.00f, 0.00f),
float2(-1.00f, 0.00f),
float2(0.00f, 1.00f),
float2(0.00f, -1.00f)
};
static float sample_radius[N_SAMPLES] =
{
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f,
0.30f, 0.30f
};
float4 ExpandRGBE( float4 RGBE )
{
return float4( ldexp( RGBE.xyz, 255.0 * RGBE.w - 128.0 ), 1.0 );
}
float3 decode(float3 enc)
{
return (2.0f * enc.xyz- 1.0f);
}
float2 rand(in float2 uv : TEXCOORD0) {
//float noise = tex2Dlod(noiseSampler, uv);
float noise = normalize(tex2Dlod(noiseSampler, float4(uv, 0, 0)).xyz * 2 - 1);
float noiseX = noise+(abs(frac(sin(dot(uv, float2(12.9898, 78.233) * M_PI)) * 43758.5453)));
float noiseY = sqrt(1.0f-noiseX*noiseX);
return float2(noiseX, noiseY);
}
float readDepth(in float2 coord : TEXCOORD0) {
float4 col = tex2Dlod(depthSampler, float4(coord, 0, 0));
float posZ = ((1.0-col.z) + (1.0-col.y)*256.0 + (1.0-col.x)*(256.0*256.0));
return (posZ-nearZ)/farZ;
}
float3 getPosition(in float2 uv : TEXCOORD0, in float eye_z) {
uv = (uv * float2(2.0, -2.0) - float2(1.0, -1.0));
float3 pos = float3(uv * g_InvFocalLen * eye_z, eye_z );
return pos;
}
float4 ssao_Main(VSOUT IN) : COLOR0
{
clip(1/SCALE-IN.UVCoord.x);
clip(1/SCALE-IN.UVCoord.y);
IN.UVCoord.xy *= SCALE;
float4 bufferData = g_Buffer.Load(IN.UVCoord);
float depth = readDepth(IN.UVCoord);
float3 pos = getPosition(IN.UVCoord, depth);
float3 dx = ddx(pos);
float3 dy = ddy(pos);
float3 normal = normalize(cross(dx,dy));
normal.y *= -1;
float sample_depth = tex2D(depthSampler, IN.UVCoord);
float ao=tex2D(AOSampler, IN.UVCoord);
float s=tex2D(sampleSampler, IN.UVCoord);
float2 rand_vec = rand(IN.UVCoord);
float2 sample_vec_divisor = g_InvFocalLen*depth*depthRange/(aoRadiusMultiplier*5000*rcpres);
float2 sample_center = IN.UVCoord + normal.xy/sample_vec_divisor*float2(1.0f,aspect);
float sample_center_depth = depth*depthRange + normal.z*aoRadiusMultiplier*7;
for(int i = 0; i < N_SAMPLES; i++)
{
float2 sample_vec = reflect(sample_offset[i], rand_vec);
sample_vec /= sample_vec_divisor;
float2 sample_coords = sample_center + sample_vec*float2(1.0f,aspect);
float curr_sample_radius = sample_radius[i]*aoRadiusMultiplier*7;
float curr_sample_depth = depthRange*readDepth(sample_coords);
ao += clamp(0,curr_sample_radius+sample_center_depth-curr_sample_depth,2*curr_sample_radius);
ao -= clamp(0,curr_sample_radius+sample_center_depth-curr_sample_depth-ThicknessModel,2*curr_sample_radius);
s += 2.0*curr_sample_radius;
}
ao /= s;
// adjust for close and far away
if(depth<0.065f)
{
ao = lerp(ao, 0.0f, (0.065f-depth)*13.3);
}
ao = 1.0f-ao*aoStrengthMultiplier;
return float4(ao,ao,ao, 1.0f);
}
float4 HBlur( VSOUT IN ) : COLOR0 {
float color = tex2D(passSampler, IN.UVCoord);
float blurred = color*0.2270270270;
blurred += tex2D(passSampler, IN.UVCoord + float2(rcpres.x*1.3846153846, 0)) * 0.3162162162;
blurred += tex2D(passSampler, IN.UVCoord - float2(rcpres.x*1.3846153846, 0)) * 0.3162162162;
blurred += tex2D(passSampler, IN.UVCoord + float2(rcpres.x*3.2307692308, 0)) * 0.0702702703;
blurred += tex2D(passSampler, IN.UVCoord - float2(rcpres.x*3.2307692308, 0)) * 0.0702702703;
return blurred;
}
float4 VBlur( VSOUT IN ) : COLOR0 {
float color = tex2D(passSampler, IN.UVCoord);
float blurred = color*0.2270270270;
blurred += tex2D(passSampler, IN.UVCoord + float2(0, rcpres.y*1.3846153846)) * 0.3162162162;
blurred += tex2D(passSampler, IN.UVCoord - float2(0, rcpres.y*1.3846153846)) * 0.3162162162;
blurred += tex2D(passSampler, IN.UVCoord + float2(0, rcpres.y*3.2307692308)) * 0.0702702703;
blurred += tex2D(passSampler, IN.UVCoord - float2(0, rcpres.y*3.2307692308)) * 0.0702702703;
return blurred;
}
float4 Combine( VSOUT IN ) : COLOR0 {
float3 color = tex2D(frameSampler, IN.UVCoord).rgb;
float ao = tex2D(passSampler, IN.UVCoord/SCALE);
ao = clamp(ao, aoClamp, 1.0f);
#ifdef LUMINANCE_CONSIDERATION
float luminance = (color.r*0.2125f)+(color.g*0.7154f)+(color.b*0.0721f);
float white = 1.0f;
float black = 0.0f;
luminance = clamp(max(black,luminance-luminosity_threshold)+max(black,luminance-luminosity_threshold)+max(black,luminance-luminosity_threshold), 0.0f, 1.0f);
ao = lerp(ao, white, luminance);
#endif
color *= 1.05f;
color *= ao;
return float4(color, 1.0f);
}
technique VSSAO
{
pass p0
{
VertexShader = compile vs_3_0 FrameVS();
PixelShader = compile ps_3_0 ssao_Main();
}
pass p1
{
VertexShader = compile vs_3_0 FrameVS();
PixelShader = compile ps_3_0 HBlur();
}
pass p2
{
VertexShader = compile vs_3_0 FrameVS();
PixelShader = compile ps_3_0 VBlur();
}
pass p3
{
VertexShader = compile vs_3_0 FrameVS();
PixelShader = compile ps_3_0 Combine();
}
}