]> git.tdb.fi Git - libs/gl.git/commitdiff
Improve the ambient occlusion postprocessor
authorMikko Rasa <tdb@tdb.fi>
Sat, 10 Dec 2016 23:01:51 +0000 (01:01 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sat, 10 Dec 2016 23:01:51 +0000 (01:01 +0200)
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
shaderlib/ambientocclusion_combine.glsl
shaderlib/ambientocclusion_occlude.glsl
shaderlib/postprocess.glsl
source/ambientocclusion.cpp
source/ambientocclusion.h

index 0ca81a40b6cb21ed5fd8af08ce1852f25e94a46d..286072c03813d420f1be5970ca91034be236c04c 100644 (file)
@@ -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;
+}
index 572ba2f14bcf73858c3b500d992ffebafa14d3db..f33520f8aa05ae76bd25a0499cf86ae319b4fa26 100644 (file)
@@ -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);
 }
index f9b755c9c7b71d0667dae0c940cd275b87c69ad0..6d04d43846cd9b378b211cc88795e819ce1745f3 100644 (file)
@@ -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<n_samples; ++i)
+       {
+               vec3 psp = project(center+transform*sample_points[i]);
+               float sample = texture(depth, psp.xy*0.5+0.5).r;
+               if(sample>=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<psp.z)
+                               occlusion_sum += 1.0;
+                       count += 1.0;
                }
-       frag_color = vec4(min(1.0-sum*darkness/16.0, 1.0), 0.0, 0.0, 1.0);
+       }
+       frag_color = vec4(1.0-occlusion_sum/count, 0.0, 0.0, 1.0);
 }
index cb65794793e5a5190474c0273f1a6da45aab5597..ff395cdec52193ab4cc73f48350b871094db7bbc 100644 (file)
@@ -6,6 +6,7 @@ void main()
 {
        gl_Position = vertex;
        out vec2 texcoord = vertex.xy*0.5+0.5;
+       passthrough;
 }
 
 ////// fragment
index 1b078cdc116a187a718e9d92b8ef97b3fd84f5ec..af1008b4230cf3283c2fe3362c6815f71d6c078c 100644 (file)
@@ -1,15 +1,17 @@
-#define _USE_MATH_DEFINES
-#include <cmath>
+#include <algorithm>
 #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<unsigned char>(127+c*127);
-               data[i*3+1] = static_cast<unsigned char>(127+s*127);
-               data[i*3+2] = static_cast<unsigned char>(127-s*127);
-               data[i*3+4] = static_cast<unsigned char>(127+c*127);
+               Geometry::Angle<float> a = Geometry::Angle<float>::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<float>(w), static_cast<float>(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<float>(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; ++i)
+       {
+               Vector3 v(random(seed)-0.5f, random(seed)-0.5f, random(seed)-0.5f);
+               sample_points[i] = normalize(v)*(0.1f+0.9f*i*i/radius_divisor);
+       }
+       shdata.uniform3_array("sample_points", n, &sample_points[0].x);
+       shdata.uniform("n_samples", static_cast<int>(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);
index 8b0b0fcdd543a17246642ed5fca945c2606fb8b6..b6daa776d6af8804f5cf1186cb7974d1b5f3783d 100644 (file)
@@ -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 &);