]> git.tdb.fi Git - libs/demoscene.git/commitdiff
Add a film grain postprocessor
authorMikko Rasa <tdb@tdb.fi>
Sat, 15 Jun 2019 11:21:53 +0000 (14:21 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 15 Jun 2019 11:22:05 +0000 (14:22 +0300)
data/filmgrain.glsl [new file with mode: 0644]
source/filmgrain.cpp [new file with mode: 0644]
source/filmgrain.h [new file with mode: 0644]
source/launcher.cpp

diff --git a/data/filmgrain.glsl b/data/filmgrain.glsl
new file mode 100644 (file)
index 0000000..9ae3d8f
--- /dev/null
@@ -0,0 +1,17 @@
+import postprocess;
+
+uniform sampler2DArray grain;
+uniform GrainParams
+{
+       float coarseness;
+       float strength;
+};
+uniform int grain_layer;
+
+#pragma MSP stage(fragment)
+void main()
+{
+       vec3 gcoord = vec3(gl_FragCoord.xy/textureSize(grain, 0).xy/coarseness, grain_layer);
+       vec4 incoming = texture(source, texcoord);
+       frag_color = vec4(incoming.rgb*mix(1.0, texture(grain, gcoord).r, strength), incoming.a);
+}
diff --git a/source/filmgrain.cpp b/source/filmgrain.cpp
new file mode 100644 (file)
index 0000000..151daf5
--- /dev/null
@@ -0,0 +1,156 @@
+#include <random>
+#include <msp/gl/renderer.h>
+#include <msp/gl/texture2d.h>
+#include "filmgrain.h"
+#include "resources.h"
+
+using namespace std;
+
+namespace Msp {
+namespace DemoScene {
+
+FilmGrain::FilmGrain(unsigned s, unsigned l):
+       mesh(get_fullscreen_quad()),
+       shprog(Resources::get_builtins().get<GL::Program>("filmgrain.glsl")),
+       size(s),
+       layers(l),
+       current_layer(0)
+{
+       grain.set_min_filter(GL::LINEAR);
+       grain.set_mag_filter(GL::LINEAR);
+       grain.set_wrap(GL::REPEAT);
+       grain.storage(GL::R8, size, size, layers, 1);
+
+       generate_grain();
+
+       texturing.attach(1, grain);
+       shdata.uniform("grain", 1);
+
+       set_coarseness(2.0f);
+       set_strength(0.1f);
+}
+
+void FilmGrain::generate_grain()
+{
+       UInt8 *data = new UInt8[size*size*layers];
+
+       unsigned noise_size = size/2;
+       UInt8 *noise = new UInt8[noise_size*noise_size];
+       Int16 *dbuf = new Int16[3*noise_size*noise_size];
+       minstd_rand random;
+
+       for(unsigned i=0; i<layers; ++i)
+       {
+               for(unsigned j=0; j<noise_size*noise_size; ++j)
+                       noise[j] = random();
+
+               bicubic2x(noise, data+i*size*size, dbuf);
+       }
+
+       delete[] noise;
+       delete[] dbuf;
+
+       grain.image(0, GL::RED, GL::UNSIGNED_BYTE, data);
+       delete[] data;
+}
+
+void FilmGrain::bicubic2x(const UInt8 *src, UInt8 *target, Int16 *dbuf)
+{
+       unsigned src_size = size/2;
+
+       // dx and dy will have an implicit scaling factor of 2
+       Int16 *dx = dbuf;
+       Int16 *dy = dbuf+src_size*src_size;
+       for(unsigned y=0; y<src_size; ++y)
+               for(unsigned x=0; x<src_size; ++x)
+               {
+                       unsigned i = x+y*src_size;
+                       dx[i] = src[(x+1)%src_size + y*src_size] - src[(x+src_size-1)%src_size + y*src_size];
+                       dy[i] = src[x + (y+1)%src_size*src_size] - src[x + (y+src_size-1)%src_size*src_size];
+               }
+
+       // dxy will have an implicit scaling factor of 4
+       Int16 *dxy = dbuf+2*src_size*src_size;
+       for(unsigned y=0; y<src_size; ++y)
+               for(unsigned x=0; x<src_size; ++x)
+                       dxy[x+y*src_size] = dy[(x+1)%src_size + y*src_size] - dy[(x+src_size-1)%src_size + y*src_size];
+
+       for(unsigned y=0; y<src_size; ++y)
+       {
+               unsigned y2 = (y+1)%src_size;
+
+               for(unsigned x=0; x<src_size; ++x)
+               {
+                       unsigned x2 = (x+1)%src_size;
+
+                       unsigned i00 = x+y*src_size;
+                       unsigned i10 = x2+y*src_size;
+                       unsigned i01 = x+y2*src_size;
+                       unsigned i11 = x2+y2*src_size;
+
+                       // Cubic hermite at t=0.5: 0.5*p0 + 0.125*m0 + 0.5*p1 - 0.125*m1
+                       unsigned i = x*2+y*2*size;
+                       target[i] = src[i00];
+                       target[i+1] = clamp(((src[i00]+src[i10])*8+dx[i00]-dx[i10])/16);
+                       target[i+size] = clamp(((src[i00]+src[i01])*8+dy[i00]-dy[i01])/16);
+                       // dyh0 and dyh1 will have an implicit scaling factor of 32
+                       Int16 dyh0 = (dy[i00]+dy[i10])*8+dxy[i00]-dxy[i10];
+                       Int16 dyh1 = (dy[i01]+dy[i11])*8+dxy[i01]-dxy[i11];
+                       target[i+1+size] = clamp(((src[i00]+src[i10]+src[i01]+src[i11])*64+dyh0-dyh1)/256);
+               }
+       }
+}
+
+UInt8 FilmGrain::clamp(int v)
+{
+       return min(max(v, 0), 255);
+}
+
+void FilmGrain::set_coarseness(float c)
+{
+       shdata.uniform("coarseness", c/2.0f);
+}
+
+void FilmGrain::set_strength(float s)
+{
+       shdata.uniform("strength", s);
+}
+
+void FilmGrain::render(GL::Renderer &renderer, const GL::Texture2D &color, const GL::Texture2D &)
+{
+       current_layer = (current_layer+1)%layers;
+       shdata.uniform("grain_layer", static_cast<int>(current_layer));
+       texturing.attach(0, color);
+       renderer.set_texturing(&texturing);
+       renderer.set_shader_program(&shprog, &shdata);
+       mesh.draw(renderer);
+}
+
+
+FilmGrain::Template::Template():
+       size(256),
+       layers(16),
+       coarseness(2.0f),
+       strength(0.1f)
+{ }
+
+FilmGrain *FilmGrain::Template::create(unsigned, unsigned) const
+{
+       FilmGrain *grain = new FilmGrain(size, layers);
+       grain->set_coarseness(coarseness);
+       grain->set_strength(strength);
+       return grain;
+}
+
+
+FilmGrain::Template::Loader::Loader(Template &t):
+       DerivedObjectLoader<Template, PostProcessor::Template::Loader>(t)
+{
+       add("coarseness", &Template::coarseness);
+       add("layers", &Template::layers);
+       add("size", &Template::size);
+       add("strength", &Template::strength);
+}
+
+} // namespace DemoScene
+} // namespace Msp
diff --git a/source/filmgrain.h b/source/filmgrain.h
new file mode 100644 (file)
index 0000000..810b9a4
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef MSP_DEMOSCENE_FILMGRAIN_H_
+#define MSP_DEMOSCENE_FILMGRAIN_H_
+
+#include <msp/gl/mesh.h>
+#include <msp/gl/postprocessor.h>
+#include <msp/gl/program.h>
+#include <msp/gl/programdata.h>
+#include <msp/gl/texture2darray.h>
+#include <msp/gl/texturing.h>
+
+namespace Msp {
+namespace DemoScene {
+
+class FilmGrain: public GL::PostProcessor
+{
+public:
+       struct Template: public PostProcessor::Template
+       {
+               class Loader: public DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>
+               {
+               public:
+                       Loader(Template &t);
+               };
+
+               unsigned size;
+               unsigned layers;
+               float coarseness;
+               float strength;
+
+               Template();
+
+               virtual FilmGrain *create(unsigned, unsigned) const;
+       };
+
+private:
+       const Msp::GL::Mesh &mesh;
+       const Msp::GL::Program &shprog;
+       Msp::GL::ProgramData shdata;
+       Msp::GL::Texturing texturing;
+       unsigned size;
+       unsigned layers;
+       Msp::GL::Texture2DArray grain;
+       unsigned current_layer;
+
+public:
+       FilmGrain(unsigned = 256, unsigned = 16);
+
+private:
+       void generate_grain();
+       void bicubic2x(const UInt8 *, UInt8 *, Int16 *);
+       static UInt8 clamp(int);
+
+public:
+       void set_coarseness(float);
+       void set_strength(float);
+
+       virtual void render(GL::Renderer &, const GL::Texture2D &, const GL::Texture2D &);
+};
+
+} // namespace DemoScene
+} // namespace Msp
+
+#endif
index 7dc9b9830eb0169148a3870f38a04cce7ab50011..aefa1be2b4b57b808f7379ee312bb9034d45b2dd 100644 (file)
@@ -7,6 +7,7 @@
 #include <msp/strings/regex.h>
 #include <msp/time/utils.h>
 #include "demo.h"
+#include "filmgrain.h"
 #include "launcher.h"
 #include "launchscreen.h"
 #include "vignette.h"
@@ -105,6 +106,7 @@ LauncherBase::LauncherBase(int argc, char **argv):
        window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &LauncherBase::exit), 0));
        keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &LauncherBase::key_press), false));
 
+       GL::PipelineTemplate::register_postprocessor<FilmGrain>("film_grain");
        GL::PipelineTemplate::register_postprocessor<Vignette>("vignette");
 }