1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
#include <epoxy/gl.h>
#include <assert.h>
#include <stdio.h>
#include <algorithm>
#include "dither_effect.h"
#include "effect_util.h"
#include "init.h"
#include "util.h"
using namespace std;
namespace movit {
namespace {
// A simple LCG (linear congruental generator) random generator.
// We implement our own so we can be deterministic from frame to frame
// and run to run; we don't have special needs for speed or quality,
// as long as the period is reasonably long. The output is in range
// [0, 2^31>.
//
// This comes from http://en.wikipedia.org/wiki/Linear_congruential_generator.
unsigned lcg_rand(unsigned x)
{
return (x * 1103515245U + 12345U) & ((1U << 31) - 1);
}
} // namespace
DitherEffect::DitherEffect()
: width(1280), height(720), num_bits(8),
last_width(-1), last_height(-1), last_num_bits(-1)
{
register_int("output_width", &width);
register_int("output_height", &height);
register_int("num_bits", &num_bits);
register_uniform_float("round_fac", &uniform_round_fac);
register_uniform_float("inv_round_fac", &uniform_inv_round_fac);
register_uniform_vec2("tc_scale", uniform_tc_scale);
register_uniform_sampler2d("dither_tex", &uniform_dither_tex);
glGenTextures(1, &texnum);
}
DitherEffect::~DitherEffect()
{
glDeleteTextures(1, &texnum);
}
string DitherEffect::output_fragment_shader()
{
char buf[256];
sprintf(buf, "#define NEED_EXPLICIT_ROUND %d\n", (movit_num_wrongly_rounded > 0));
return buf + read_file("dither_effect.frag");
}
void DitherEffect::update_texture(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
{
float *dither_noise = new float[width * height];
float dither_double_amplitude = 1.0f / (1 << num_bits);
// We don't need a strictly nonrepeating dither; reducing the resolution
// to max 128x128 saves a lot of texture bandwidth, without causing any
// noticeable harm to the dither's performance.
texture_width = min(width, 128);
texture_height = min(height, 128);
// Using the resolution as a seed gives us a consistent dither from frame to frame.
// It also gives a different dither for e.g. different aspect ratios, which _feels_
// good, but probably shouldn't matter.
unsigned seed = (width << 16) ^ height;
for (int i = 0; i < texture_width * texture_height; ++i) {
seed = lcg_rand(seed);
float normalized_rand = seed * (1.0f / (1U << 31)) - 0.5; // [-0.5, 0.5>
dither_noise[i] = dither_double_amplitude * normalized_rand;
}
glActiveTexture(GL_TEXTURE0 + *sampler_num);
check_error();
glBindTexture(GL_TEXTURE_2D, texnum);
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
check_error();
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, texture_width, texture_height, 0, GL_RED, GL_FLOAT, dither_noise);
check_error();
delete[] dither_noise;
}
void DitherEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
{
Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
assert(width > 0);
assert(height > 0);
assert(num_bits > 0);
if (width != last_width || height != last_height || num_bits != last_num_bits) {
update_texture(glsl_program_num, prefix, sampler_num);
last_width = width;
last_height = height;
last_num_bits = num_bits;
}
glActiveTexture(GL_TEXTURE0 + *sampler_num);
check_error();
glBindTexture(GL_TEXTURE_2D, texnum);
check_error();
uniform_dither_tex = *sampler_num;
++*sampler_num;
// In theory, we should adjust for the texel centers that have moved here as well,
// but since we use GL_NEAREST and we don't really care a lot what texel we sample,
// we don't have to worry about it.
uniform_tc_scale[0] = float(width) / float(texture_width);
uniform_tc_scale[1] = float(height) / float(texture_height);
// Used if the shader needs to do explicit rounding.
int round_fac = (1 << num_bits) - 1;
uniform_round_fac = round_fac;
uniform_inv_round_fac = 1.0f / round_fac;
}
} // namespace movit
|