From: John Tsiombikas Date: Fri, 12 Aug 2016 06:40:13 +0000 (+0300) Subject: gamma correction in post when there's no sRGB framebuffer and simple shader compositi... X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=vrfileman;a=commitdiff_plain;h=7c59a70219aa7bcf80291f41903a9e6fc9b6e073 gamma correction in post when there's no sRGB framebuffer and simple shader composition system --- diff --git a/sdr/post.p.glsl b/sdr/post.p.glsl new file mode 100644 index 0000000..562d79a --- /dev/null +++ b/sdr/post.p.glsl @@ -0,0 +1,24 @@ +uniform sampler2D tex; + +#ifndef set_pixel +#define set_pixel set_pixel_linear +#endif + +void set_pixel_linear(vec3 c) +{ + gl_FragColor.rgb = c; + gl_FragColor.a = 1.0; +} + +void set_pixel_srgb(vec3 c) +{ + gl_FragColor.rgb = pow(c, vec3(1.0 / 2.2)); + gl_FragColor.a = 1.0; +} + +void main() +{ + vec3 texel = texture2D(tex, gl_TexCoord[0].st).rgb; + + set_pixel(texel); +} diff --git a/sdr/post.v.glsl b/sdr/post.v.glsl new file mode 100644 index 0000000..965fa06 --- /dev/null +++ b/sdr/post.v.glsl @@ -0,0 +1,7 @@ +uniform mat4 texmat; + +void main() +{ + gl_Position = gl_Vertex; + gl_TexCoord[0] = texmat * gl_MultiTexCoord0; +} diff --git a/src/app.cc b/src/app.cc index 08f6e8a..b3049bc 100644 --- a/src/app.cc +++ b/src/app.cc @@ -9,6 +9,9 @@ #include "goatvr.h" #include "opt.h" #include "fs.h" +#include "rtarg.h" +#include "texture.h" +#include "sdr.h" static void draw_scene(); @@ -28,6 +31,11 @@ static int prev_x, prev_y; static float fov = 60.0; +static RenderTarget *rtarg; +static bool rtarg_valid; +static unsigned int post_sdr; + + bool app_init(int argc, char **argv) { if(!init_options(argc, argv, "vrfileman.conf")) { @@ -56,6 +64,9 @@ bool app_init(int argc, char **argv) glEnable(GL_FRAMEBUFFER_SRGB); } + rtarg = new RenderTarget; + rtarg->create(GL_RGB16F); + if(opt.vr) { if(goatvr_init() == -1) { return false; @@ -67,6 +78,16 @@ bool app_init(int argc, char **argv) cam_height = goatvr_get_eye_height(); } + if(opt.srgb) { + add_shader_header(GL_FRAGMENT_SHADER, "#define set_pixel set_pixel_linear"); + } else { + add_shader_header(GL_FRAGMENT_SHADER, "#define set_pixel set_pixel_srgb"); + } + if(!(post_sdr = create_program_load("sdr/post.v.glsl", "sdr/post.p.glsl"))) { + return false; + } + clear_shader_header(0); + Mesh::use_custom_sdr_attr = false; if(!init_backdrop()) { @@ -85,11 +106,22 @@ void app_cleanup() if(opt.vr) { goatvr_shutdown(); } + delete rtarg; + free_program(post_sdr); cleanup_backdrop(); } +static void update() +{ + if(!rtarg_valid) { + rtarg->resize(win_width, win_height); + } +} + void app_draw() { + update(); + if(opt.vr) { // VR mode goatvr_draw_start(); @@ -145,13 +177,43 @@ void app_draw() static void draw_scene() { + set_render_target(rtarg); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + draw_backdrop(); draw_fs(); + + set_render_target(0); + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_DEPTH_TEST); + + glUseProgram(post_sdr); + set_uniform_matrix4(post_sdr, "texmat", rtarg->get_texture_matrix()[0]); + + bind_texture(rtarg->get_texture()); + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2f(-1, -1); + glTexCoord2f(1, 0); + glVertex2f(1, -1); + glTexCoord2f(1, 1); + glVertex2f(1, 1); + glTexCoord2f(0, 1); + glVertex2f(-1, 1); + glEnd(); + + bind_texture(0); + glUseProgram(0); + + glPopAttrib(); } void app_reshape(int x, int y) { glViewport(0, 0, x, y); + rtarg_valid = false; if(opt.vr) { goatvr_set_fb_size(x, y, 1.0); diff --git a/src/color.cc b/src/color.cc index ae27f89..1300d82 100644 --- a/src/color.cc +++ b/src/color.cc @@ -3,7 +3,7 @@ Color color(float r, float g, float b) { - return opt.srgb ? Color(r, g, b) : linear_to_srgb(Color(r, g, b)); + return Color(r, g, b);//opt.srgb ? Color(r, g, b) : linear_to_srgb(Color(r, g, b)); } Color linear_to_srgb(const Color &c) diff --git a/src/rtarg.cc b/src/rtarg.cc new file mode 100644 index 0000000..84a9142 --- /dev/null +++ b/src/rtarg.cc @@ -0,0 +1,262 @@ +#include "rtarg.h" +#include "texture.h" +#include "app.h" + +unsigned int RenderTarget::default_fbo = 0; + +RenderTarget::RenderTarget() +{ + width = height = 0; + fbo = 0; + rbuf_zstencil = 0; + color_tex = 0; + tex_face = 0; + tex_targ = 0; +} + +RenderTarget::~RenderTarget() +{ + cleanup(); +} + +bool RenderTarget::create(unsigned int fmt) +{ + return create(win_width, win_height, fmt); +} + +bool RenderTarget::create(int width, int height, unsigned int fmt) +{ + fprintf(stderr, "RenderTarget::create(%d, %d)\n", width, height); + cleanup(); + + tex_targ = GL_TEXTURE_2D; + this->width = width; + this->height = height; + int tex_width = next_pow2(width); + int tex_height = next_pow2(height); + + tex_matrix.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1.0); + + color_tex = new Texture2D; + color_tex->create(tex_width, tex_height, fmt); + tex_face = 0; + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glBindTexture(GL_TEXTURE_2D, color_tex->get_id()); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex->get_id(), 0); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenRenderbuffers(1, &rbuf_zstencil); + glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); +#ifdef GL_ES_VERSION_2_0 + // XXX really? 16bit zbuffer attachment is the best we can do on ES2? + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, tex_width, tex_height); +#else + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height); +#endif + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); +#ifndef GL_ES_VERSION_2_0 + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); +#endif + + glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo); + return true; +} + +bool RenderTarget::create(Texture *tex, int face) +{ + fprintf(stderr, "RenderTarget::create(tex{%d, %d}, face:%d)\n", tex->get_size(0), + tex->get_size(1), face); + + tex_targ = GL_TEXTURE_2D; + if(dynamic_cast(tex)) { + if(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) { + tex_targ = face; + } else if(face >= 0 && face < 6) { + tex_targ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; + } else { + fprintf(stderr, "invalid face (%d) passed to RenderTarget::create(TextureCube*, int)\n", face); + return false; + } + } + + cleanup(); + + width = tex->get_size(0); + height = tex->get_size(1); + + tex_matrix = Mat4::identity; + + color_tex = tex; + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glBindTexture(tex_targ, color_tex->get_id()); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0); + glBindTexture(tex_targ, 0); + + glGenRenderbuffers(1, &rbuf_zstencil); + glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); + + glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo); + return true; +} + +void RenderTarget::cleanup() +{ + delete color_tex; + color_tex = 0; + + if(fbo) { + glDeleteFramebuffers(1, &fbo); + } + if(rbuf_zstencil) { + glDeleteRenderbuffers(1, &rbuf_zstencil); + } + + fbo = rbuf_zstencil = 0; + width = height = 0; + tex_face = 0; + tex_targ = 0; +} + +bool RenderTarget::resize(int width, int height) +{ + if(width == this->width && height == this->height) { + return true; + } + + int tex_width = next_pow2(width); + int tex_height = next_pow2(height); + this->width = width; + this->height = height; + + if(tex_width != color_tex->get_size(0) || tex_height != color_tex->get_size(1)) { + printf("resizing render target (fbo %u): %dx%d [%dx%d]\n", fbo, width, height, tex_width, tex_height); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + color_tex->create(tex_width, tex_height, color_tex->get_format()); + color_tex->bind(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0); + glBindTexture(tex_targ, 0); + + glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); + + unsigned int depth_format; +#ifndef GL_ES_VERSION_2_0 + // desktop OpenGL, use typical depth24-stencil8 format + depth_format = GL_DEPTH24_STENCIL8; +#else // OpenGL ES + // OpenGL ES provides combined depth/stencil formats as an extension +#ifdef GL_OES_packed_depth_stencil + if(have_ext_packed_depth_stencil) { + depth_format = GL_DEPTH24_STENCIL8_OES; + } else +#endif // GL_OES_packed_depth_stencil + { + depth_format = GL_DEPTH_COMPONENT16; // no compile-time or runtime support for packed d/s + } +#endif // GL_ES_VERSION_2_0 + glRenderbufferStorage(GL_RENDERBUFFER, depth_format, tex_width, tex_height); + + glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo); + } + + tex_matrix.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1.0); + return true; +} + +int RenderTarget::get_width() const +{ + return width; +} + +int RenderTarget::get_height() const +{ + return height; +} + +Texture *RenderTarget::get_texture() const +{ + return color_tex; +} + +const Mat4 &RenderTarget::get_texture_matrix() const +{ + return tex_matrix; +} + + +static const char *fbstname[] = { + "GL_FRAMEBUFFER_COMPLETE", + "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT", + "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", + "no such fbo error", + "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS", + "GL_FRAMEBUFFER_INCOMPLETE_FORMATS", + "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER", + "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER", + "GL_FRAMEBUFFER_UNSUPPORTED" +}; + +bool RenderTarget::check() const +{ + bool res = true; + +#ifndef GL_ES_VERSION_2_0 + int prev_fb = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_fb); +#endif + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if(status != GL_FRAMEBUFFER_COMPLETE) { + fprintf(stderr, "RenderTarget::check: incomplete FBO %u: %s\n", fbo, + fbstname[status - GL_FRAMEBUFFER_COMPLETE]); + res = false; + goto end; + } + +end: +#ifndef GL_ES_VERSION_2_0 + glBindFramebuffer(GL_FRAMEBUFFER, prev_fb); +#endif + return res; +} + +void RenderTarget::bind() const +{ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glViewport(0, 0, width, height); +} + +void set_render_target(const RenderTarget *rtarg) +{ + if(rtarg) { + rtarg->bind(); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo); + glViewport(0, 0, win_width, win_height); + } +} + +int next_pow2(int x) +{ + x--; + x = (x >> 1) | x; + x = (x >> 2) | x; + x = (x >> 4) | x; + x = (x >> 8) | x; + x = (x >> 16) | x; + return x + 1; +} + diff --git a/src/rtarg.h b/src/rtarg.h new file mode 100644 index 0000000..ed15ac3 --- /dev/null +++ b/src/rtarg.h @@ -0,0 +1,56 @@ +#ifndef RTARG_H_ +#define RTARG_H_ + +#include "gmath/gmath.h" +#include "opengl.h" + +class Texture; + +class RenderTarget { +private: + int width, height; + + unsigned int fbo; + Texture *color_tex; + unsigned int tex_targ; + int tex_face; + unsigned int rbuf_zstencil; + + Mat4 tex_matrix; + + void bind() const; + +public: + static unsigned int default_fbo; // 0 on most platforms, GLKit fbo on iOS. + + RenderTarget(); + ~RenderTarget(); + + bool create(unsigned int fmt = GL_RGBA); + bool create(int width, int height, unsigned int fmt = GL_RGBA); + bool create(Texture *tex, int face = 0); + + void cleanup(); + + bool resize(int width, int height); + + int get_width() const; + int get_height() const; + + Texture *get_texture() const; + + /** calculates a texture matrix to map the full texture space + * onto the part of the texture occupied by the render target + */ + const Mat4 &get_texture_matrix() const; + + bool check() const; + + friend void set_render_target(const RenderTarget *rtarg); +}; + +void set_render_target(const RenderTarget *rtarg); + +int next_pow2(int x); + +#endif // RTARG_H_ diff --git a/src/sdr.c b/src/sdr.c index 5ccf7fe..0152a6e 100644 --- a/src/sdr.c +++ b/src/sdr.c @@ -14,6 +14,8 @@ #include "sdr.h" static const char *sdrtypestr(unsigned int sdrtype); +static int sdrtypeidx(unsigned int sdrtype); + unsigned int create_vertex_shader(const char *src) { @@ -57,11 +59,21 @@ unsigned int create_shader(const char *src, unsigned int sdr_type) unsigned int sdr; int success, info_len; char *info_str = 0; + const char *src_str[3], *header, *footer; + int src_str_count = 0; GLenum err; + if((header = get_shader_header(sdr_type))) { + src_str[src_str_count++] = header; + } + src_str[src_str_count++] = src; + if((footer = get_shader_footer(sdr_type))) { + src_str[src_str_count++] = footer; + } + sdr = glCreateShader(sdr_type); assert(glGetError() == GL_NO_ERROR); - glShaderSource(sdr, 1, &src, 0); + glShaderSource(sdr, src_str_count, src_str, 0); err = glGetError(); assert(err == GL_NO_ERROR); glCompileShader(sdr); @@ -356,7 +368,7 @@ int set_uniform_float4(unsigned int prog, const char *name, float x, float y, fl END_UNIFORM_CODE; } -int set_uniform_matrix4(unsigned int prog, const char *name, float *mat) +int set_uniform_matrix4(unsigned int prog, const char *name, const float *mat) { BEGIN_UNIFORM_CODE { glUniformMatrix4fv(loc, 1, GL_FALSE, mat); @@ -364,7 +376,7 @@ int set_uniform_matrix4(unsigned int prog, const char *name, float *mat) END_UNIFORM_CODE; } -int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat) +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, const float *mat) { BEGIN_UNIFORM_CODE { glUniformMatrix4fv(loc, 1, GL_TRUE, mat); @@ -394,6 +406,116 @@ void set_attrib_float3(int attr_loc, float x, float y, float z) glVertexAttrib3f(attr_loc, x, y, z); } +/* ---- shader composition ---- */ +struct string { + char *text; + int len; +}; + +#define NUM_SHADER_TYPES 5 +static struct string header[NUM_SHADER_TYPES]; +static struct string footer[NUM_SHADER_TYPES]; + +static void clear_string(struct string *str) +{ + free(str->text); + str->text = 0; + str->len = 0; +} + +static void append_string(struct string *str, const char *s) +{ + int len, newlen; + char *newstr; + + if(!s || !*s) return; + + len = strlen(s); + newlen = str->len + len; + if(!(newstr = malloc(newlen + 2))) { /* leave space for a possible newline */ + fprintf(stderr, "shader composition: failed to append string of size %d\n", len); + abort(); + } + + if(str->text) { + memcpy(newstr, str->text, str->len); + } + memcpy(newstr + str->len, s, len + 1); + + if(s[len - 1] != '\n') { + newstr[newlen] = '\n'; + newstr[newlen + 1] = 0; + } + + free(str->text); + str->text = newstr; + str->len = newlen; +} + +void clear_shader_header(unsigned int type) +{ + if(type) { + int idx = sdrtypeidx(type); + clear_string(&header[idx]); + } else { + int i; + for(i=0; i"; } + +static int sdrtypeidx(unsigned int sdrtype) +{ + switch(sdrtype) { + case GL_VERTEX_SHADER: + return 0; + case GL_FRAGMENT_SHADER: + return 1; + case GL_TESS_CONTROL_SHADER: + return 2; + case GL_TESS_EVALUATION_SHADER: + return 3; + case GL_GEOMETRY_SHADER: + return 4; + default: + break; + } + return 0; +} diff --git a/src/sdr.h b/src/sdr.h index d411202..7bf2389 100644 --- a/src/sdr.h +++ b/src/sdr.h @@ -41,12 +41,26 @@ int set_uniform_float(unsigned int prog, const char *name, float val); int set_uniform_float2(unsigned int prog, const char *name, float x, float y); int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z); int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w); -int set_uniform_matrix4(unsigned int prog, const char *name, float *mat); -int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat); +int set_uniform_matrix4(unsigned int prog, const char *name, const float *mat); +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, const float *mat); int get_attrib_loc(unsigned int prog, const char *name); void set_attrib_float3(int attr_loc, float x, float y, float z); +/* ---- shader composition ---- */ + +/* clear shader header/footer text. + * pass the shader type to clear, or 0 to clear all types */ +void clear_shader_header(unsigned int type); +void clear_shader_footer(unsigned int type); +/* append text to the header/footer of a specific shader type + * or use type 0 to add it to all shade types */ +void add_shader_header(unsigned int type, const char *s); +void add_shader_footer(unsigned int type, const char *s); +/* get the current header/footer text for a specific shader type */ +const char *get_shader_header(unsigned int type); +const char *get_shader_footer(unsigned int type); + #ifdef __cplusplus } #endif /* __cplusplus */