// Shaped Bokeh Depth of Field
// CoC Calculations based off Knu's DoF shader for MGE
// Blurring Algorithm Created by Tomerk
// Optimized and lens effects by Ethatron
// ---------------------------------------
// NOTES
//
// If you use "Bloom" instead of "HDR" it is necessary
// to bump the screen-buffer depth to 16bits with
// "iBufferTexturesNumBits=-16" in the OBGE.ini
// ---------------------------------------
// TWEAKABLE VARIABLES.
#undef TEST_MODE
// Toggle for test-mode. If enabled, you can see the raw ssao
//"#define ..." is enabled, and "#undef ..." is disabled.
iface float fr
< string help = "retina focus point, dpt"; >
= 60.0;
// set slightly lower than fp to simulate myopia or as an alternative to MGE's distant blur
// set slightly higher than fp+fpa to simulate hyperopia
iface float fp
< string help = "eye relaxed focus power, dpt"; >
= 60.0;
iface float fpa
< string help = "accomodation, dpt"; >
= 10.0;
// set lower to simulate presbyopia
iface float base_blur_radius
< string help = "base blur radius;"; >
= 0.75;
// higher values mean more blur when out of DoF and shorter DoF.
iface float R
< string help = "maximum blur radius in pixels;"; >
= 16.0;
/* 4.87ms without
* 4.93ms with global astigmatism
* 5.15ms with full local astigmatism
* 9.05ms with global aberration
* 9.02ms with full local aberration
* 9.05ms everything
*/
#define ABERRATION 0 // take lens refraction into account
#define ASTIGMATISM 1 // take lens curvature into account
#undef EDGEWEIGHTING // #undef has constant weighting in highlights,
// #define gives edges more weight in highlights
#undef WEAPONBLUR // Define to keep weapons from blurring
iface float weaponblur_cutoff
< string help = "Cutoff distance for what is considered a weapon"; >
= 0.8;
#define SMOOTHBLUR // when defined, smooths the blur, may have a performance hit
// END OF TWEAKABLE VARIABLES.
// ---------------------------------------
#include "includes/Resolution.hlsl"
#include "includes/Depth.hlsl"
#include "includes/Color.hlsl"
#include "includes/IO.hlsl"
#define drangeZ oblv_ProjectionDepthRange_MAINPASS.z * 0.01
texture2D obge_LastRendertarget0_EFFECTPASS;
sampler2D PassSamplerL = sampler_state {
Texture = ;
AddressU = Mirror;
AddressV = Mirror;
MagFilter = LINEAR;
MinFilter = LINEAR;
MipFilter = LINEAR;
};
struct VSOUT
{
float4 vertPos : POSITION;
float2 UVCoord : TEXCOORD0;
};
struct VSIN
{
float4 vertPos : POSITION0;
float2 UVCoord : TEXCOORD0;
};
VSOUT FrameVS(VSIN IN)
{
VSOUT OUT = (VSOUT)0.0f;
OUT.vertPos = IN.vertPos;
OUT.UVCoord = IN.UVCoord;
return OUT;
}
static const float focus = LinearDepth(float2(0.5, 0.5));
#define precalc(x,y,z) float3(x,y,z)
#define M 60
static const float3 taps[M] =
{
precalc( -0.0000, -1.0000, 1.0000 ),
precalc( -0.2158, -0.8763, 0.9070 ),
precalc( 0.2170, -0.8750, 0.9065 ),
precalc( -0.4325, -0.7509, 0.8670 ),
precalc( 0.4340, -0.7500, 0.8670 ),
precalc( -0.0000, -0.7500, 1.0000 ),
precalc( -0.2160, -0.6259, 0.8851 ),
precalc( -0.6493, -0.6254, 0.9065 ),
precalc( 0.6510, -0.6250, 0.9070 ),
precalc( 0.2170, -0.6250, 0.8847 ),
precalc( -0.4328, -0.5004, 0.8847 ),
precalc( -0.0000, -0.5000, 1.0000 ),
precalc( 0.8660, -0.5000, 1.0000 ),
precalc( -0.8660, -0.5000, 1.0000 ),
precalc( 0.4340, -0.5000, 0.8851 ),
precalc( -0.2163, -0.3754, 0.8670 ),
precalc( 0.2170, -0.3750, 0.8670 ),
precalc( 0.6495, -0.3750, 1.0000 ),
precalc( -0.6495, -0.3750, 1.0000 ),
precalc( -0.8668, -0.2513, 0.9070 ),
precalc( 0.4330, -0.2500, 1.0000 ),
precalc( -0.0000, -0.2500, 1.0000 ),
precalc( -0.4330, -0.2500, 1.0000 ),
precalc( 0.8663, -0.2496, 0.9065 ),
precalc( -0.6500, -0.1259, 0.8851 ),
precalc( 0.2165, -0.1250, 1.0000 ),
precalc( -0.2165, -0.1250, 1.0000 ),
precalc( 0.6498, -0.1246, 0.8847 ),
precalc( -0.8665, -0.0009, 0.8670 ),
precalc( -0.4333, -0.0004, 0.8670 ),
precalc( 0.4333, 0.0004, 0.8670 ),
precalc( 0.8665, 0.0009, 0.8670 ),
precalc( -0.6498, 0.1246, 0.8847 ),
precalc( -0.2165, 0.1250, 1.0000 ),
precalc( 0.2165, 0.1250, 1.0000 ),
precalc( 0.6500, 0.1259, 0.8851 ),
precalc( -0.8663, 0.2496, 0.9065 ),
precalc( 0.0000, 0.2500, 1.0000 ),
precalc( -0.4330, 0.2500, 1.0000 ),
precalc( 0.4330, 0.2500, 1.0000 ),
precalc( 0.8668, 0.2513, 0.9070 ),
precalc( -0.6495, 0.3750, 1.0000 ),
precalc( -0.2170, 0.3750, 0.8670 ),
precalc( 0.6495, 0.3750, 1.0000 ),
precalc( 0.2163, 0.3754, 0.8670 ),
precalc( 0.0000, 0.5000, 1.0000 ),
precalc( -0.8660, 0.5000, 1.0000 ),
precalc( -0.4340, 0.5000, 0.8851 ),
precalc( 0.8660, 0.5000, 1.0000 ),
precalc( 0.4328, 0.5004, 0.8847 ),
precalc( -0.6510, 0.6250, 0.9070 ),
precalc( -0.2170, 0.6250, 0.8847 ),
precalc( 0.6493, 0.6254, 0.9065 ),
precalc( 0.2160, 0.6259, 0.8851 ),
precalc( -0.4340, 0.7500, 0.8670 ),
precalc( 0.0000, 0.7500, 1.0000 ),
precalc( 0.4325, 0.7509, 0.8670 ),
precalc( -0.2170, 0.8750, 0.9065 ),
precalc( 0.2158, 0.8763, 0.9070 ),
precalc( 0.0000, 1.0000, 1.0000 ),
};
static const float k = 0.00001;
/* does a poor job optimizing this way */
#define lodm 0.0 // 0.0
#define lodw 1.0 // 1.0 / (1.0 + lodm * lodm * 0.5)
#define lod 0.0 // clamp(lodm * c / R, 0.0, lodm)
float4 DepthOfField(float2 tex : TEXCOORD0) : COLOR0 {
float depth = LinearDepth(tex);
float s = focus * drangeZ;
float z = depth * drangeZ;
float weapon = 1;
float fpf = clamp(1 / s + fr, fp, fp + fpa);
float c = base_blur_radius * 0.009 * (fr - fpf + 1 / z) / fr / k;
c = sign(z - s) * min(abs©, R);
#ifndef WEAPONBLUR
weapon = smoothstep(weaponblur_cutoff - 0.05, weaponblur_cutoff, z);
c *= weapon;
#endif
#ifdef TEST_MODE
return abs©;
#else
return float4(tex2D(PassSamplerL, tex).rgb, store(c, R));
#endif
}
float4 SmartBlur(float2 tex : TEXCOORD0) : COLOR0 {
float4 color = tex2D(PassSamplerL, tex);
const float c = load(color.a, R);
float weight = (1 / (c * c + 1)) * GetLuminance(color.rgb + 0.01);
weight = max(0, weight) * lodw;
#ifdef EDGEWEIGHTING
weight *= 0.25;
#endif
float2 curvature = 0.0;
float modifier = 1.0;
float chroma = 1.0;
#if (ASTIGMATISM >= 1) || (ABERRATION >= 1)
curvature = (tex - 0.5) * float2(1.0, raspect);
#endif
#if (ASTIGMATISM >= 1)
modifier = pow(1.0 + dot(3.0 * curvature, 3.0 * curvature), 0.5);
#endif
#if (ABERRATION >= 1)
chroma = pow(1.0 + dot(1.5 * curvature, 1.5 * curvature), 1.0);
#endif
color.rgb *= weight;
float amount = weight;
[unroll]
for (int i = 0; i < M; i++) {
const float3 dir = taps[i];
const float len = length(dir.xy) * abs©;
#if (ASTIGMATISM >= 2) || (ABERRATION >= 2)
curvature = (tex + rcpres * c * dir.xy - 0.5) * float2(1.0, raspect);
#endif
#if (ASTIGMATISM >= 2)
modifier = pow(1.0 + dot(3.0 * curvature, 3.0 * curvature), 0.5);
#endif
#if (ABERRATION >= 2)
chroma = pow(1.0 + dot(1.5 * curvature, 1.5 * curvature), 1.0);
#endif
float2 s_tex = tex + rcpres * c * modifier * dir.xy;
float4 s_color = tex2Dlod(PassSamplerL, float4(s_tex, 0, 0));
float s_c = abs(load(s_color.a, R)) * dir.z;
/* TODO: alternate implementation:
* weight the colors against a shifted smoothstep per channel
* then we don't need read more samples
*
* |bokeh range|
* _____
* __|_/ \___|__ r
* _____
* __|__/ \__|__ g
* _____
* __|___/ \_|__ b
*/
#if (ABERRATION >= 1)
float2 s_tex_r = tex + rcpres * c * modifier * dir.xy * float2(chroma, 1.0);
float2 s_tex_g = tex + rcpres * c * modifier * dir.xy * float2(1.0 , 1.0);
float2 s_tex_b = tex + rcpres * c * modifier * dir.xy * float2(1.0, chroma);
s_color.r = tex2Dlod(PassSamplerL, float4(s_tex_r, 0, lod)).r;
s_color.g = tex2Dlod(PassSamplerL, float4(s_tex_g, 0, lod)).g;
s_color.b = tex2Dlod(PassSamplerL, float4(s_tex_b, 0, lod)).b;
#endif
// if (c < 0) s_c = max(abs©, s_c);
s_c = max( -c , s_c);
weight = (1 / (s_c * s_c + 1)) * GetLuminance(s_color.rgb + 0.01);
weight *= saturate(1 - smoothstep(s_c, s_c * 1.1, len));
#ifdef EDGEWEIGHTING
weight *= saturate(0.25 + 0.75 * pow(len / (s_c), 2));
#endif
/* weight is positive [0,inf]*[0,1]*[0,1] */
color.rgb += s_color.rgb * weight;
amount += weight;
}
#ifdef SMOOTHBLUR
return float4(color.rgb / amount, color.a);
#endif
return float4(color.rgb / amount, 1);
}
float4 HorizontalBlur(float2 tex : TEXCOORD0) : COLOR0 {
float4 color = tex2D(PassSamplerL, tex);
float c = load(color.a, R);
float scale = c / 8; color.rgb *= 6;
color.rgb += tex2Dlod(PassSamplerL, float4(tex.x - (rcpres.x ) * scale, tex.y, 0, lod)).rgb * 4;
color.rgb += tex2Dlod(PassSamplerL, float4(tex.x + (rcpres.x ) * scale, tex.y, 0, lod)).rgb * 4;
color.rgb += tex2Dlod(PassSamplerL, float4(tex.x - (rcpres.x * 2) * scale, tex.y, 0, lod)).rgb * 1;
color.rgb += tex2Dlod(PassSamplerL, float4(tex.x + (rcpres.x * 2) * scale, tex.y, 0, lod)).rgb * 1;
return float4(color.rgb / 16, color.a);
}
float4 VerticalBlur(float2 tex : TEXCOORD0) : COLOR0 {
float4 color = tex2D(PassSamplerL, tex);
float c = load(color.a, R);
float scale = c / 8; color.rgb *= 6;
color.rgb += tex2Dlod(PassSamplerL, float4(tex.x, tex.y - (rcpres.y ) * scale, 0, lod)).rgb * 4;
color.rgb += tex2Dlod(PassSamplerL, float4(tex.x, tex.y + (rcpres.y ) * scale, 0, lod)).rgb * 4;
color.rgb += tex2Dlod(PassSamplerL, float4(tex.x, tex.y - (rcpres.y * 2) * scale, 0, lod)).rgb * 1;
color.rgb += tex2Dlod(PassSamplerL, float4(tex.x, tex.y + (rcpres.y * 2) * scale, 0, lod)).rgb * 1;
return float4(color.rgb / 16, 1);
}
technique main
<
int group = EFFECTGROUP_POST;
int fxclass = EFFECTCLASS_DOF;
int conditions = EFFECTCOND_ZBUFFER | EFFECTCOND_ACHANNEL
#if (ABERRATION >= 1)
| EFFECTCOND_MIPMAPS
#endif
;
>
{
pass {
VertexShader = compile vs_3_0 FrameVS();
PixelShader = compile ps_3_0 DepthOfField();
}
#ifndef TEST_MODE
pass {
VertexShader = compile vs_3_0 FrameVS();
PixelShader = compile ps_3_0 SmartBlur();
}
#endif
#ifdef SMOOTHBLUR
pass {
VertexShader = compile vs_3_0 FrameVS();
PixelShader = compile ps_3_0 HorizontalBlur();
}
pass {
VertexShader = compile vs_3_0 FrameVS();
PixelShader = compile ps_3_0 VerticalBlur();
}
#endif
}