X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=render.h;h=77bb56e1b570eeb7b4bddd0de12e6f82c5e21077;hb=a1adba47558099cab82aa6d10abdc1dca11d3342;hp=493ed3b6d4520c8954bc7860e4702c28fb9b9893;hpb=7758c7efec3956c68294bc914e7524045a2b1bd7;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/render.h b/render.h index 493ed3b..77bb56e 100644 --- a/render.h +++ b/render.h @@ -1,82 +1,838 @@ +/* + * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved + */ + +#include "common.h" +#include "model.h" +#include "camera.h" + +#include "shaders/blit.h" +#include "shaders/blitblur.h" +#include "shaders/standard.h" +#include "shaders/vblend.h" + +VG_STATIC void render_water_texture( camera *cam ); +VG_STATIC void render_water_surface( camera *cam ); +VG_STATIC void render_world( camera *cam ); +VG_STATIC void shader_link_standard_ub( GLuint shader, int texture_id ); +VG_STATIC void render_world_depth( camera *cam ); + #ifndef RENDER_H #define RENDER_H -#define VG_3D -#include "vg/vg.h" -static struct pipeline +typedef struct framebuffer framebuffer; + + +/* + * All standard buffers used in rendering + */ +VG_STATIC struct pipeline +{ + glmesh fsquad; + + framebuffer *fb_main, + *fb_heightmap, + *fb_water_reflection, + *fb_water_beneath; + + /* STD140 */ + struct ub_world_lighting + { + /* v3f (padded) */ + v4f g_light_colours[3], + g_light_directions[3], + g_ambient_colour; + + v4f g_water_plane, + g_depth_bounds; + + float g_water_fog; + int g_light_count; + int g_light_preview; + int g_shadow_samples; + } + ub_world_lighting; + + struct light_widget + { + int enabled; + v2f dir; + v3f colour; + } + widgets[3]; + + float shadow_spread, shadow_length; + GLuint ubo_world_lighting, + ubo_world; + + int ready; +} +gpipeline = +{ + .widgets = + { + { + .enabled = 1, + .colour = { 1.36f, 1.35f, 1.01f }, + .dir = { 0.63f, -0.08f } + }, + { + .enabled = 1, + .colour = { 0.33f, 0.56f, 0.64f }, + .dir = { -2.60f, -0.13f } + }, + { + .enabled = 1, + .colour = { 0.05f, 0.05f, 0.23f }, + .dir = { 2.60f, -0.84f } + } + }, + .shadow_spread = 0.65f, + .shadow_length = 9.50f, + + .ub_world_lighting = + { + .g_ambient_colour = { 0.09f, 0.03f, 0.07f } + } +}; + +struct framebuffer { - float fov; + const char *display_name; + int resolution_div, + fixed_w, + fixed_h; + + struct framebuffer_attachment + { + const char *display_name; + + enum framebuffer_attachment_type + { + k_framebuffer_attachment_type_none, + k_framebuffer_attachment_type_colour, + k_framebuffer_attachment_type_renderbuffer + } + purpose; + + enum framebuffer_quality_profile + { + k_framebuffer_quality_all, + k_framebuffer_quality_high_only + } + quality; + + GLenum internalformat, + format, + type, + attachment; + + GLuint id; + } + attachments[5]; + GLuint fb; + framebuffer **link; } -gpipeline; +framebuffers[] = +{ + { + /* + * The primary draw target + */ + "Main", + .link = &gpipeline.fb_main, + .resolution_div = 1, + .attachments = + { + { + "Colour", k_framebuffer_attachment_type_colour, + + .internalformat = GL_RGB, + .format = GL_RGB, + .type = GL_UNSIGNED_BYTE, + .attachment = GL_COLOR_ATTACHMENT0 + }, + { + "Motion Vectors", k_framebuffer_attachment_type_colour, + + .quality = k_framebuffer_quality_high_only, + .internalformat = GL_RG16F, + .format = GL_RG, + .type = GL_FLOAT, + .attachment = GL_COLOR_ATTACHMENT1 + }, + { + "Depth/Stencil", k_framebuffer_attachment_type_renderbuffer, + + .internalformat = GL_DEPTH24_STENCIL8, + .attachment = GL_DEPTH_STENCIL_ATTACHMENT + } + } + }, + { + /* + * A ortho projection of the world, used for shadows and ocean colouring. + * Note: it does not have a render buffer attachement because it's + * intended to be drawn to in a MAX blending mode + */ + "Heightmap", + .link = &gpipeline.fb_heightmap, + .fixed_w = 1024, + .fixed_h = 1024, -static void render_water_texture( m4x3f camera ); -static void render_water_surface( m4x4f pv ); -static void render_world( m4x4f projection, m4x3f camera ); + .attachments = + { + { + "Depth", k_framebuffer_attachment_type_colour, + + .internalformat = GL_R32F, + .format = GL_RED, + .type = GL_FLOAT, + .attachment = GL_COLOR_ATTACHMENT0 + } + } + }, + { + /* + * Second rendered view from the perspective of the water reflection + */ + "Water reflection", + .link = &gpipeline.fb_water_reflection, + .resolution_div = 3, + .attachments = + { + { + "Colour", k_framebuffer_attachment_type_colour, + .internalformat = GL_RGB, + .format = GL_RGB, + .type = GL_UNSIGNED_BYTE, + .attachment = GL_COLOR_ATTACHMENT0 + }, + { + "Depth/Stencil", k_framebuffer_attachment_type_renderbuffer, + + .internalformat = GL_DEPTH24_STENCIL8, + .attachment = GL_DEPTH_STENCIL_ATTACHMENT + } + } + }, + { + /* + * Thid rendered view from the perspective of the camera, but just + * captures stuff thats under the water + */ + "Water Beneath", + .link = &gpipeline.fb_water_beneath, + .resolution_div = 4, + .attachments = + { + { + "Colour", k_framebuffer_attachment_type_colour, + .internalformat = GL_RGBA, + .format = GL_RGBA, + .type = GL_UNSIGNED_BYTE, + .attachment = GL_COLOR_ATTACHMENT0 + }, + { + "Depth/Stencil", k_framebuffer_attachment_type_renderbuffer, + + .internalformat = GL_DEPTH24_STENCIL8, + .attachment = GL_DEPTH_STENCIL_ATTACHMENT + } + } + } +}; /* - * http://www.terathon.com/lengyel/Lengyel-Oblique.pdf + * Get the current (automatically scaled or fixed) resolution of framebuffer */ -static void plane_clip_projection( m4x4f mat, v4f plane ) +VG_STATIC void render_fb_get_current_res( struct framebuffer *fb, + int *x, int *y ) { - v4f c = + if( fb->resolution_div ) + { + *x = vg.window_x / fb->resolution_div; + *y = vg.window_y / fb->resolution_div; + } + else { - (vg_signf(plane[0]) + mat[2][0]) / mat[0][0], - (vg_signf(plane[1]) + mat[2][1]) / mat[1][1], - -1.0f, - (1.0f + mat[2][2]) / mat[3][2] + *x = fb->fixed_w; + *y = fb->fixed_h; + } +} + +/* + * Bind framebuffer for drawing to + */ +VG_STATIC void render_fb_bind( framebuffer *fb ) +{ + int x, y; + render_fb_get_current_res( fb, &x, &y ); + glBindFramebuffer( GL_FRAMEBUFFER, fb->fb ); + glViewport( 0, 0, x, y ); +} + +/* + * Bind framebuffer attachment's texture + */ +VG_STATIC void render_fb_bind_texture( framebuffer *fb, + int attachment, int slot ) +{ + struct framebuffer_attachment *at = &fb->attachments[attachment]; + + if( at->purpose != k_framebuffer_attachment_type_colour ) + { + vg_fatal_exit_loop( "illegal operation: bind non-colour framebuffer" + " attachment to texture slot" ); + } + + glActiveTexture( GL_TEXTURE0 + slot ); + glBindTexture( GL_TEXTURE_2D, fb->attachments[attachment].id ); +} + + +/* + * Shaders + */ +VG_STATIC void shader_link_standard_ub( GLuint shader, int texture_id ) +{ + GLuint idx = glGetUniformBlockIndex( shader, "ub_world_lighting" ); + glUniformBlockBinding( shader, idx, 0 ); + + render_fb_bind_texture( gpipeline.fb_heightmap, 0, texture_id ); + glUniform1i( glGetUniformLocation( shader, "g_world_depth" ), texture_id ); +} + +VG_STATIC void render_update_lighting_ub(void) +{ + struct ub_world_lighting *winf = &gpipeline.ub_world_lighting; + int c = 0; + + for( int i=0; i<3; i++ ) + { + struct light_widget *lw = &gpipeline.widgets[i]; + + if( lw->enabled ) + { + float pitch = lw->dir[0], + yaw = lw->dir[1], + xz = cosf( pitch ); + + v3_copy( (v3f){ xz*cosf(yaw), sinf(pitch), xz*sinf(yaw) }, + winf->g_light_directions[c] ); + v3_copy( lw->colour, winf->g_light_colours[c] ); + + c ++; + } + } + + winf->g_light_count = c; + winf->g_light_directions[0][3] = gpipeline.shadow_length; + winf->g_light_colours[0][3] = gpipeline.shadow_spread; + + if( vg.quality_profile == k_quality_profile_low ) + winf->g_shadow_samples = 0; + else + winf->g_shadow_samples = 8; + + glBindBuffer( GL_UNIFORM_BUFFER, gpipeline.ubo_world_lighting ); + glBufferSubData( GL_UNIFORM_BUFFER, 0, sizeof(struct ub_world_lighting), + &gpipeline.ub_world_lighting ); +} + +#define FB_FORMAT_STR( E ) { E, #E }, + +/* + * Convert OpenGL attachment ID enum to string + */ +VG_STATIC const char *render_fb_attachment_str( GLenum e ) +{ + struct { GLenum e; const char *str; } + formats[] = + { + FB_FORMAT_STR(GL_COLOR_ATTACHMENT0) + FB_FORMAT_STR(GL_COLOR_ATTACHMENT1) + FB_FORMAT_STR(GL_COLOR_ATTACHMENT2) + FB_FORMAT_STR(GL_COLOR_ATTACHMENT3) + FB_FORMAT_STR(GL_COLOR_ATTACHMENT4) + FB_FORMAT_STR(GL_DEPTH_STENCIL_ATTACHMENT) }; - v4_muls( plane, 2.0f / v4_dot(plane,c), c ); + for( int i=0; ipurpose == k_framebuffer_attachment_type_renderbuffer ) + { + glBindRenderbuffer( GL_RENDERBUFFER, a->id ); + glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, rx, ry ); + } + else if( a->purpose == k_framebuffer_attachment_type_colour ) + { + glBindTexture( GL_TEXTURE_2D, a->id ); + glTexImage2D( GL_TEXTURE_2D, 0, a->internalformat, rx, ry, + 0, a->format, a->type, NULL ); + } +} + +/* + * Full allocation of a framebuffer + */ +VG_STATIC void render_fb_allocate( struct framebuffer *fb ) +{ + glGenFramebuffers( 1, &fb->fb ); + glBindFramebuffer( GL_FRAMEBUFFER, fb->fb ); + + int rx, ry; + render_fb_get_current_res( fb, &rx, &ry ); + + vg_info( "allocate_framebuffer( %s, %dx%d )\n", fb->display_name, rx, ry ); + vg_info( "{\n" ); + + GLenum colour_attachments[4]; + u32 colour_count = 0; + + for( int j=0; jattachments); j++ ) + { + struct framebuffer_attachment *attachment = &fb->attachments[j]; - glGenTextures( 1, rgb ); - glBindTexture( GL_TEXTURE_2D, *rgb ); - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg_window_x, vg_window_y, - 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + if( attachment->purpose == k_framebuffer_attachment_type_none ) + continue; + + vg_info( " %s: %s\n", + render_fb_attachment_str( attachment->attachment ), + render_fb_format_str( attachment->internalformat ) ); + + if( attachment->purpose == k_framebuffer_attachment_type_renderbuffer ) + { + glGenRenderbuffers( 1, &attachment->id ); + render_fb_allocate_texture( fb, attachment ); + glFramebufferRenderbuffer( GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, attachment->id ); + } + else if( attachment->purpose == k_framebuffer_attachment_type_colour ) + { + glGenTextures( 1, &attachment->id ); + render_fb_allocate_texture( fb, attachment ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + + glFramebufferTexture2D( GL_FRAMEBUFFER, attachment->attachment, + GL_TEXTURE_2D, attachment->id, 0 ); + + colour_attachments[ colour_count ++ ] = attachment->attachment; + } + } + + glDrawBuffers( colour_count, colour_attachments ); + + /* + * Check result + */ + GLenum result = glCheckFramebufferStatus( GL_FRAMEBUFFER ); + + if( result == GL_FRAMEBUFFER_COMPLETE ) + { + /* + * Attatch to gpipeline + */ + if( fb->link ) + *fb->link = fb; + + vg_success( " status: complete\n" ); + vg_info( "}\n" ); + } + else + { + if( result == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT ) + vg_error( " status: Incomplete attachment" ); + else if( result == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT ) + vg_error( " status: Missing attachment" ); + else if( result == GL_FRAMEBUFFER_UNSUPPORTED ) + vg_error( " status: Unsupported framebuffer format" ); + else + vg_error( " status: Generic Error" ); + + vg_info( "}\n" ); + vg_fatal_exit_loop( "Incomplete framebuffer (see logs)" ); + } +} + +/* + * Resize/Update all framebuffers(we know about) + */ +VG_STATIC void render_fb_resize(void) +{ + if( !gpipeline.ready ) + return; + + for( int i=0; iattachments); j++ ) + { + struct framebuffer_attachment *attachment = &fb->attachments[j]; + render_fb_allocate_texture( fb, attachment ); + } + } +} + +#if 0 +VG_STATIC void fb_use( struct framebuffer *fb ) +{ + if( !fb ) + { + glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_background ); + glViewport( 0, 0, vg.window_x, vg.window_y ); + } + else + { + glBindFramebuffer( GL_FRAMEBUFFER, fb->fb ); + glViewport( 0, 0, vg.window_x / fb->div, vg.window_y / fb->div ); + } +} + +VG_STATIC void fb_init( struct framebuffer *fb ) +{ + i32 ix = vg.window_x / fb->div, + iy = vg.window_y / fb->div; + + glGenFramebuffers( 1, &fb->fb ); + glBindFramebuffer( GL_FRAMEBUFFER, fb->fb ); + + glGenTextures( 1, &fb->colour ); + glBindTexture( GL_TEXTURE_2D, fb->colour ); + glTexImage2D( GL_TEXTURE_2D, 0, fb->format, ix, iy, + 0, fb->format, GL_UNSIGNED_BYTE, NULL); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, *rgb, 0); + GL_TEXTURE_2D, fb->colour, 0); + + glGenRenderbuffers( 1, &fb->rb ); + glBindRenderbuffer( GL_RENDERBUFFER, fb->rb ); + glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, ix, iy ); + + glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, fb->rb ); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + VG_CHECK_GL_ERR(); + fb->allocated = 1; +} + +VG_STATIC void _fb_glTexImage2D( GLsizei x, GLsizei y, GLint internalformat, + GLenum format, GLenum type, const void *data ) +{ + glTexImage2D( GL_TEXTURE_2D, 0, internalformat, x,y, 0, + format, type, data ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); +} + +#define fb_tex2d( X,Y, INTERNAL_FORMAT, FORMAT, TYPE, DATA ) \ + _fb_glTexImage2D( X,Y, INTERNAL_FORMAT, FORMAT, TYPE, DATA ); \ + vg_info( "texture( %dx%d, internal: %s, format: %s, type: %s )\n", \ + X,Y, #INTERNAL_FORMAT, #FORMAT, #TYPE ); + +VG_STATIC void fb_free( struct framebuffer *fb ) +{ + glDeleteTextures( 1, &fb->colour ); + glDeleteFramebuffers( 1, &fb->fb ); +} + +VG_STATIC void fb_bindtex( struct framebuffer *fb, int texture ) +{ + glActiveTexture( GL_TEXTURE0 + texture ); + glBindTexture( GL_TEXTURE_2D, fb->colour ); +} + +VG_STATIC void fb_resize( struct framebuffer *fb ) +{ + if( !fb->allocated ) + return; + + i32 ix = vg.window_x / fb->div, + iy = vg.window_y / fb->div; + + glBindTexture( GL_TEXTURE_2D, fb->colour ); + glTexImage2D( GL_TEXTURE_2D, 0, fb->format, ix, iy, 0, + fb->format, GL_UNSIGNED_BYTE, NULL ); + + glBindRenderbuffer( GL_RENDERBUFFER, fb->rb ); + glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, ix, iy ); +} +#endif + +#if 0 +VG_STATIC void render_init_temp_buffer(void) +{ + vg_info( "[render] Allocate framebuffer\n" ); + + glGenFramebuffers( 1, &gpipeline.fb_background ); + glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_background ); + + glGenTextures( 1, &gpipeline.rgb_background ); + glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background ); + fb_tex2d( vg.window_x, vg.window_y, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, NULL ); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + gpipeline.rgb_background, 0 ); - /* TODO: Check for DEPTH32f availiblity and use if possible */ + glGenTextures( 1, &gpipeline.mv_background ); + glBindTexture( GL_TEXTURE_2D, gpipeline.mv_background ); +#if 0 + glTexImage2D( GL_TEXTURE_2D, 0, GL_RG, vg.window_x, vg.window_y, + 0, GL_RG, GL_FLOAT, NULL); +#endif + glTexImage2D( GL_TEXTURE_2D, 0, GL_RG16F, vg.window_x, vg.window_y, + 0, GL_RG, GL_FLOAT, NULL); - glGenRenderbuffers( 1, rb ); - glBindRenderbuffer( GL_RENDERBUFFER, *rb ); - glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, - vg_window_x, vg_window_y ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, + GL_TEXTURE_2D, + gpipeline.mv_background, 0 ); + /* render buffer */ + glGenRenderbuffers( 1, &gpipeline.rb_background ); + glBindRenderbuffer( GL_RENDERBUFFER, gpipeline.rb_background ); + glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, + vg.window_x, vg.window_y ); glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, *rb ); + GL_RENDERBUFFER, gpipeline.rb_background ); + + GLuint attachments[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glDrawBuffers( 2, attachments ); + + render_check_framebuffer_complete(); + + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + VG_CHECK_GL_ERR(); +} + +/* used for drawing world depth from the top view, used in our water and + * lighting calculations */ +VG_STATIC void render_init_depthmap_buffer(void) +{ + vg_info( "[render] Allocate depth map buffer\n" ); + + glGenFramebuffers( 1, &gpipeline.fb_depthmap ); + glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_depthmap ); + + glGenTextures( 1, &gpipeline.rgb_depthmap ); + glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_depthmap ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_R32F, 1024, 1024, 0, + GL_RED, GL_FLOAT, NULL ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + vg_tex2d_clamp(); + + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + gpipeline.rgb_depthmap, 0); + + render_check_framebuffer_complete(); + VG_CHECK_GL_ERR(); } +#endif -static void resize_renderbuffer_std( GLuint *fb, GLuint *rgb, GLuint *rb ) +VG_STATIC void render_init_fs_quad(void) { - glBindTexture( GL_TEXTURE_2D, *rgb ); - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg_window_x, vg_window_y, 0, - GL_RGB, GL_UNSIGNED_BYTE, NULL ); + vg_info( "[render] Allocate quad\n" ); + + float quad[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, - glBindRenderbuffer( GL_RENDERBUFFER, *rb ); - glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, - vg_window_x, vg_window_y ); + 0.2f, 0.0f, 0.8f, 1.0f, 0.2f, 1.0f, + 0.2f, 0.0f, 0.8f, 0.0f, 0.8f, 1.0f}; + + glGenVertexArrays( 1, &gpipeline.fsquad.vao ); + glGenBuffers( 1, &gpipeline.fsquad.vbo ); + glBindVertexArray( gpipeline.fsquad.vao ); + glBindBuffer( GL_ARRAY_BUFFER, gpipeline.fsquad.vbo ); + glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW ); + glBindVertexArray( gpipeline.fsquad.vao ); + glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, + sizeof(float)*2, (void*)0 ); + glEnableVertexAttribArray( 0 ); + + VG_CHECK_GL_ERR(); +} + +VG_STATIC void render_init_uniform_buffers(void) +{ + vg_info( "[render] Allocate uniform buffer\n" ); + + glGenBuffers( 1, &gpipeline.ubo_world_lighting ); + glBindBuffer( GL_UNIFORM_BUFFER, gpipeline.ubo_world_lighting ); + glBufferData( GL_UNIFORM_BUFFER, sizeof(struct ub_world_lighting), + NULL, GL_DYNAMIC_DRAW ); + + render_update_lighting_ub(); + glBindBufferBase( GL_UNIFORM_BUFFER, 0, gpipeline.ubo_world_lighting ); + + VG_CHECK_GL_ERR(); +} + +VG_STATIC void render_init(void) +{ + shader_blit_register(); + shader_blitblur_register(); + shader_standard_register(); + shader_vblend_register(); + + vg_acquire_thread_sync(); + { + /* + * Complete Framebuffers + */ + for( int i=0; i