+/*
+ * Shaders
+ */
+VG_STATIC void shader_link_standard_ub( GLuint shader, int texture_id )
+{
+ GLuint idx = glGetUniformBlockIndex( shader, "ub_world_lighting" );
+ glUniformBlockBinding( shader, idx, 0 );
+
+ glActiveTexture( GL_TEXTURE0 + texture_id );
+ glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_depthmap );
+ 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 );
+}
+
+/*
+ * Framebuffers
+ */
+
+VG_STATIC void fb_use( struct framebuffer *fb )