From 1b9640375606a305f19c76cf15406202322b5bdf Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2016 01:01:51 +0200 Subject: [PATCH] Improve the ambient occlusion postprocessor Now that postprocessors have access to the Renderer and its state, the camera projection matrix can be used for more sophisticated depth calculations. --- shaderlib/ambientocclusion.glsl | 23 ++++++++- shaderlib/ambientocclusion_combine.glsl | 28 +++++----- shaderlib/ambientocclusion_occlude.glsl | 30 ++++++----- shaderlib/postprocess.glsl | 1 + source/ambientocclusion.cpp | 68 ++++++++++++++++++------- source/ambientocclusion.h | 13 ++++- 6 files changed, 115 insertions(+), 48 deletions(-) diff --git a/shaderlib/ambientocclusion.glsl b/shaderlib/ambientocclusion.glsl index 0ca81a40..286072c0 100644 --- a/shaderlib/ambientocclusion.glsl +++ b/shaderlib/ambientocclusion.glsl @@ -1,9 +1,28 @@ +const int max_samples = 32; + +uniform mat4 projection_matrix; + uniform sampler2D depth; uniform sampler2D occlusion; uniform sampler2D rotate; uniform AmbientOcclusionParams { - vec2 screen_size; - vec2 depth_ratio; + mat4 inverse_projection; float darkness; + vec3 sample_points[max_samples]; + int n_samples; + float occlusion_radius; }; + +////// fragment +vec3 project(vec3 position) +{ + vec4 pp = projection_matrix*vec4(position, 1.0); + return pp.xyz/pp.w; +} + +vec3 unproject(vec3 position) +{ + vec4 upp = inverse_projection*vec4(position, 1.0); + return upp.xyz/upp.w; +} diff --git a/shaderlib/ambientocclusion_combine.glsl b/shaderlib/ambientocclusion_combine.glsl index 572ba2f1..f33520f8 100644 --- a/shaderlib/ambientocclusion_combine.glsl +++ b/shaderlib/ambientocclusion_combine.glsl @@ -4,20 +4,24 @@ import ambientocclusion; ////// fragment void main() { - float sample = depth_ratio.x/(texture(depth, texcoord).r-depth_ratio.y); - float sum = 1.0; - float count = 1.0; - for(int i=0; i<=3; ++i) - for(int j=0; j<=3; ++j) + vec3 center = unproject(vec3(vertex.xy, texture(depth, texcoord).r)); + float min_depth = project(vec3(center.xy, center.z+occlusion_radius*0.1)).z; + float max_depth = project(vec3(center.xy, center.z-occlusion_radius*0.1)).z; + vec2 tex_scale = 1.0/vec2(textureSize(occlusion, 0)); + float sum = 0.0; + float count = 0.0; + for(int i=0; i<4; ++i) + for(int j=0; j<4; ++j) { - vec2 offs = vec2(float(i)-1.5, float(j)-1.5)/screen_size; - float dxy = length(offs)*-sample; - float dz = depth_ratio.x/(texture(depth, texcoord+offs).r-depth_ratio.y)-sample; - if(abs(dz)<3.0*dxy) + vec2 offset = vec2(float(i)-1.5, float(j)-1.5)*tex_scale; + float occ = texture(occlusion, texcoord+offset).r; + float depth = texture(depth, texcoord+offset).r; + if(depth>=min_depth && depth<=max_depth) { - sum += texture(occlusion, texcoord+offs).r; - count += 1.0; + sum += occ; + count += 1; } } - frag_color = texture(source, texcoord)*sum/count; + vec4 src_color = texture(source, texcoord); + frag_color = vec4(src_color.rgb*mix(1.0, min(sum*2.0/count, 1.0), darkness), src_color.a); } diff --git a/shaderlib/ambientocclusion_occlude.glsl b/shaderlib/ambientocclusion_occlude.glsl index f9b755c9..6d04d438 100644 --- a/shaderlib/ambientocclusion_occlude.glsl +++ b/shaderlib/ambientocclusion_occlude.glsl @@ -4,20 +4,22 @@ import ambientocclusion; ////// fragment void main() { - mat2 transform = mat2(texture(rotate, texcoord*screen_size/4.0)*2.0-1.0) - *mat2(screen_size.y/screen_size.x, 0.0, 0.0, 1.0)*3.0/screen_size.y; - float sample = depth_ratio.x/(texture(depth, texcoord).r-depth_ratio.y); - float sum = 0.0; - for(int i=0; i<=3; ++i) - for(int j=0; j<=3; ++j) + vec4 rv = texture(rotate, gl_FragCoord.xy/4.0)*2.0-1.0; + mat3 transform = mat3(rv.xy, 0.0, rv.zx, 0.0, 0.0, 0.0, rv.w)*occlusion_radius; + vec3 center = unproject(vec3(vertex.xy, texture(depth, texcoord).r)); + float min_depth = project(vec3(center.xy, center.z+occlusion_radius)).z; + float occlusion_sum = 0.0; + float count = 0.0; + for(int i=0; i=min_depth) { - vec2 offs = transform*vec2(float(i)-1.5, float(j)-1.5); - float dxy = length(offs)*-sample; - float dz = depth_ratio.x/(texture(depth, texcoord+offs).r-depth_ratio.y)-sample; - if(abs(dz)<3.0*dxy) - sum += atan(dz/dxy)/1.570796; - else if(dz<0.0) - sum -= 0.8; + if(sample +#include #include "ambientocclusion.h" #include "blend.h" +#include "camera.h" #include "renderer.h" #include "shader.h" #include "tests.h" +using namespace std; + namespace Msp { namespace GL { -AmbientOcclusion::AmbientOcclusion(unsigned w, unsigned h, float depth_ratio): +AmbientOcclusion::AmbientOcclusion(unsigned w, unsigned h, float): occlude_target(w, h, (RENDER_COLOR,RGB)), occlude_shader("ambientocclusion_occlude.glsl"), combine_shader("ambientocclusion_combine.glsl"), @@ -17,19 +19,19 @@ AmbientOcclusion::AmbientOcclusion(unsigned w, unsigned h, float depth_ratio): { texturing.attach(2, occlude_target.get_target_texture(RENDER_COLOR)); + unsigned seed = 1; rotate_lookup.storage(RGBA, 4, 4); - rotate_lookup.set_min_filter(NEAREST); - rotate_lookup.set_mag_filter(NEAREST); + rotate_lookup.set_filter(NEAREST); unsigned char data[64]; for(unsigned i=0; i<16; ++i) { - float a = ((i*541)%16)*M_PI/32; - float c = cos(a); - float s = sin(a); - data[i*3 ] = static_cast(127+c*127); - data[i*3+1] = static_cast(127+s*127); - data[i*3+2] = static_cast(127-s*127); - data[i*3+4] = static_cast(127+c*127); + Geometry::Angle a = Geometry::Angle::from_turns(random(seed)); + unsigned char c = (cos(a)*0.5f+0.5f)*255; + unsigned char s = (sin(a)*0.5f+0.5f)*255; + data[i*3 ] = c; + data[i*3+1] = s; + data[i*3+2] = 255-s; + data[i*3+4] = ((i+i/4)%2)*255; } rotate_lookup.image(0, RGBA, UNSIGNED_BYTE, data); @@ -39,17 +41,44 @@ AmbientOcclusion::AmbientOcclusion(unsigned w, unsigned h, float depth_ratio): shdata.uniform("depth", 1); shdata.uniform("occlusion", 2); shdata.uniform("rotate", 3); - shdata.uniform("screen_size", static_cast(w), static_cast(h)); + shdata.uniform("inverse_projection", Matrix()); + + set_n_samples(16); + set_occlusion_radius(0.5f); + set_darkness(1.0f); +} - set_depth_ratio(depth_ratio); - set_darkness(1.5); +float AmbientOcclusion::random(unsigned &seed) +{ + static const unsigned modulus = (1U<<31)-1; + seed = (seed*48271)%modulus; // minstd + return static_cast(seed)/(modulus-1); } -void AmbientOcclusion::set_depth_ratio(float depth_ratio) +void AmbientOcclusion::set_n_samples(unsigned n) { - depth_ratio = 1/depth_ratio; + if(n<1 || n>32) + throw out_of_range("AmbientOcclusion::set_n_samples"); - shdata.uniform("depth_ratio", depth_ratio, 1+depth_ratio); + unsigned seed = 1; + float radius_divisor = (n-1)*(n-1); + Vector3 sample_points[32]; + for(unsigned i=0; i(n)); +} + +void AmbientOcclusion::set_occlusion_radius(float r) +{ + shdata.uniform("occlusion_radius", r); +} + +void AmbientOcclusion::set_depth_ratio(float) +{ } void AmbientOcclusion::set_darkness(float darkness) @@ -62,6 +91,9 @@ void AmbientOcclusion::render(Renderer &renderer, const Texture2D &color, const texturing.attach(0, color); texturing.attach(1, depth); + if(renderer.get_camera()) + shdata.uniform("inverse_projection", invert(renderer.get_camera()->get_projection_matrix())); + Renderer::Push push(renderer); renderer.set_texturing(&texturing); renderer.set_shader_program(&occlude_shader, &shdata); diff --git a/source/ambientocclusion.h b/source/ambientocclusion.h index 8b0b0fcd..b6daa776 100644 --- a/source/ambientocclusion.h +++ b/source/ambientocclusion.h @@ -26,13 +26,22 @@ private: Texturing texturing; Program occlude_shader; Program combine_shader; - ProgramData shdata; + mutable ProgramData shdata; const Mesh &quad; public: - AmbientOcclusion(unsigned, unsigned, float); + AmbientOcclusion(unsigned, unsigned, float = 1.0f); +private: + static float random(unsigned &); + +public: + void set_n_samples(unsigned); + void set_occlusion_radius(float); + + // Deprecated void set_depth_ratio(float); + void set_darkness(float); virtual void render(Renderer &, const Texture2D &, const Texture2D &); -- 2.43.0