gamma correction in post when there's no sRGB framebuffer and simple shader compositi...
authorJohn Tsiombikas <nuclear@mutantstargoat.com>
Fri, 12 Aug 2016 06:40:13 +0000 (09:40 +0300)
committerJohn Tsiombikas <nuclear@mutantstargoat.com>
Fri, 12 Aug 2016 06:40:13 +0000 (09:40 +0300)
sdr/post.p.glsl [new file with mode: 0644]
sdr/post.v.glsl [new file with mode: 0644]
src/app.cc
src/color.cc
src/rtarg.cc [new file with mode: 0644]
src/rtarg.h [new file with mode: 0644]
src/sdr.c
src/sdr.h

diff --git a/sdr/post.p.glsl b/sdr/post.p.glsl
new file mode 100644 (file)
index 0000000..562d79a
--- /dev/null
@@ -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 (file)
index 0000000..965fa06
--- /dev/null
@@ -0,0 +1,7 @@
+uniform mat4 texmat;
+
+void main()
+{
+       gl_Position = gl_Vertex;
+       gl_TexCoord[0] = texmat * gl_MultiTexCoord0;
+}
index 08f6e8a..b3049bc 100644 (file)
@@ -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);
index ae27f89..1300d82 100644 (file)
@@ -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 (file)
index 0000000..84a9142
--- /dev/null
@@ -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<TextureCube*>(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 (file)
index 0000000..ed15ac3
--- /dev/null
@@ -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_
index 5ccf7fe..0152a6e 100644 (file)
--- 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<NUM_SHADER_TYPES; i++) {
+                       clear_string(&header[i]);
+               }
+       }
+}
+
+void clear_shader_footer(unsigned int type)
+{
+       if(type) {
+               int idx = sdrtypeidx(type);
+               clear_string(&footer[idx]);
+       } else {
+               int i;
+               for(i=0; i<NUM_SHADER_TYPES; i++) {
+                       clear_string(&footer[i]);
+               }
+       }
+}
+
+void add_shader_header(unsigned int type, const char *s)
+{
+       if(type) {
+               int idx = sdrtypeidx(type);
+               append_string(&header[idx], s);
+       } else {
+               int i;
+               for(i=0; i<NUM_SHADER_TYPES; i++) {
+                       append_string(&header[i], s);
+               }
+       }
+}
+
+void add_shader_footer(unsigned int type, const char *s)
+{
+       if(type) {
+               int idx = sdrtypeidx(type);
+               append_string(&footer[idx], s);
+       } else {
+               int i;
+               for(i=0; i<NUM_SHADER_TYPES; i++) {
+                       append_string(&footer[i], s);
+               }
+       }
+}
+
+const char *get_shader_header(unsigned int type)
+{
+       int idx = sdrtypeidx(type);
+       return header[idx].text;
+}
+
+const char *get_shader_footer(unsigned int type)
+{
+       int idx = sdrtypeidx(type);
+       return footer[idx].text;
+}
+
 static const char *sdrtypestr(unsigned int sdrtype)
 {
        switch(sdrtype) {
@@ -419,3 +541,22 @@ static const char *sdrtypestr(unsigned int sdrtype)
        }
        return "<unknown>";
 }
+
+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;
+}
index d411202..7bf2389 100644 (file)
--- 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 */