medfall

A super great game engine
Log | Files | Refs

commit 6933aedd927d215fbf30a74a316950c86c30fc97
parent 9aa29a6e5d221d0afd962a7d9a3fdd16b0cea2d8
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Sat, 13 Oct 2018 00:17:18 +0900

Support multiple render targets, add outlines to the BSP renderer

Diffstat:
bsp.cc | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
bsp_renderer.cc | 12+++++++++---
renderer.cc | 67++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
renderer.h | 24++++++++++++++++++++++--
shaders.cc | 7+++++++
shaders.h | 3+++
shaders/common.glsl | 8++++++++
shaders/flat_vertex_colours_to_gbuffer.glsl | 37+++++++++++++++++++++++++++++++++++++
shaders/gbuffer.glsl | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
shaders/shadowed_vertex_colours.glsl | 5+++--
10 files changed, 282 insertions(+), 23 deletions(-)

diff --git a/bsp.cc b/bsp.cc @@ -16,6 +16,8 @@ static bool fix = false; static v3 fix_start; static v3 fix_end; +static FB gbuffer; + static const float EPSILON = 1.0 / 32.0; // TODO: bit twiddling? @@ -246,7 +248,7 @@ BSP_Leaf & BSP::position_to_leaf( v3 pos ) const { } GAME_INIT( game_init ) { - bsp_init( &game->bsp, "acidwdm2.bsp" ); + bsp_init( &game->bsp, "assets/acidwdm2.bsp" ); MemoryArena arena = memarena_push_arena( &mem->persistent_arena, megabytes( 10 ) ); @@ -255,6 +257,30 @@ GAME_INIT( game_init ) { game->yaw = 0; bspr_init( &game->bspr, &arena, &game->bsp ); + + { + v2u32 window_size = get_window_size(); + + TextureConfig texture_config; + texture_config.width = window_size.x; + texture_config.height = window_size.y; + + FramebufferConfig fb_config; + + texture_config.format = TEXFMT_RGB_U8; + fb_config.textures[ OUTPUT_ALBEDO ].config = texture_config; + fb_config.textures[ OUTPUT_ALBEDO ].attachment = FB_COLOUR; + + texture_config.format = TEXFMT_RGB_HALF; + fb_config.textures[ OUTPUT_NORMAL ].config = texture_config; + fb_config.textures[ OUTPUT_NORMAL ].attachment = FB_NORMAL; + + texture_config.format = TEXFMT_DEPTH; + fb_config.textures[ OUTPUT_DEPTH ].config = texture_config; + fb_config.textures[ OUTPUT_DEPTH ].attachment = FB_DEPTH; + + gbuffer = renderer_new_fb( fb_config ); + } } GAME_FRAME( game_frame ) { @@ -284,13 +310,13 @@ GAME_FRAME( game_frame ) { m4 V = m4_view( forward, right, up, game->pos ); renderer_begin_frame(); - renderer_begin_pass( RENDERER_CLEAR_COLOUR_DO, RENDERER_CLEAR_DEPTH_DO ); + renderer_begin_pass( gbuffer, RENDERER_CLEAR_COLOUR_DO, RENDERER_CLEAR_DEPTH_DO ); UniformBinding view_uniforms = renderer_uniforms( V, P ); { RenderState render_state; - render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS ); + render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS_TO_GBUFFER ); render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms; render_state.cull_face = CULLFACE_FRONT; bspr_render( &game->bspr, game->pos, render_state ); @@ -318,14 +344,37 @@ GAME_FRAME( game_frame ) { immediate_render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms; immediate_render( immediate_render_state ); - char buf[ 256 ]; - snprintf( buf, sizeof( buf ), "pos: (%.2f %.2f %.2f) pitch: %.2f yaw: %.2f forward: (%.2f %.2f %.2f) right: (%.2f %.2f %.2f) up: (%.2f %.2f %.2f)", game->pos.x, game->pos.y, game->pos.z, game->pitch, game->yaw, - forward.x, forward.y, forward.z, - right.x, right.y, right.z, - up.x, up.y, up.z - ); - draw_text( buf, 2, 2, 16 ); - renderer_end_pass(); + + { + renderer_begin_pass( RENDERER_CLEAR_COLOUR_DONT, RENDERER_CLEAR_DEPTH_DONT ); + + RenderState render_state; + render_state.shader = get_shader( SHADER_GBUFFER ); + render_state.depth_func = DEPTHFUNC_DISABLED; + render_state.textures[ 0 ] = gbuffer.textures[ OUTPUT_ALBEDO ]; + render_state.textures[ 1 ] = gbuffer.textures[ OUTPUT_NORMAL ]; + render_state.textures[ 2 ] = gbuffer.textures[ OUTPUT_DEPTH ]; + + renderer_draw_fullscreen_triangle( render_state ); + + renderer_end_pass(); + } + + { + renderer_begin_pass( RENDERER_CLEAR_COLOUR_DONT, RENDERER_CLEAR_DEPTH_DONT ); + + char buf[ 256 ]; + snprintf( buf, sizeof( buf ), "pos: (%.2f %.2f %.2f) pitch: %.2f yaw: %.2f forward: (%.2f %.2f %.2f) right: (%.2f %.2f %.2f) up: (%.2f %.2f %.2f)", game->pos.x, game->pos.y, game->pos.z, game->pitch, game->yaw, + forward.x, forward.y, forward.z, + right.x, right.y, right.z, + up.x, up.y, up.z + ); + draw_text( buf, 2, 2, 16 ); + draw_text( str< 128 >( "drawcalls = {}, tris = {}", renderer_num_draw_calls(), renderer_num_vertices() / 3 ).c_str(), 2, 20, 16 ); + + renderer_end_pass(); + } + renderer_end_frame(); } diff --git a/bsp_renderer.cc b/bsp_renderer.cc @@ -48,10 +48,8 @@ v3 bspr_face_colour( const BSP * bsp, const BSP_Face & face ) { } else if(strcmp(bsp->textures[face.texture].name, "textures/acidwdm2/sky_black") == 0) { return v3(0, 0, 0); } else if(strcmp(bsp->textures[face.texture].name, "textures/acidwdm2/ink") == 0) { - //return v3(0, 0, 0); return v3( 0, 0, 0 ); } else { - //return v3( 1, 1, 1 ); return v3( 0, 0, 0 ); } } @@ -63,12 +61,14 @@ void bspr_init( BSPRenderer * bspr, MemoryArena * arena, const BSP * bsp ) { MEMARENA_SCOPED_CHECKPOINT( arena ); array< v3 > positions = memarena_push_array( arena, v3, 2048 ); + array< v3 > normals = memarena_push_array( arena, v3, 2048 ); array< v3 > colours = memarena_push_array( arena, v3, 2048 ); array< u32 > indices = memarena_push_array( arena, u32, 2048 ); - size_t num_positions, num_colours, num_indices; + size_t num_positions, num_normals, num_colours, num_indices; for( u32 l = 0; l < bsp->num_leaves; l++ ) { num_positions = 0; + num_normals = 0; num_colours = 0; num_indices = 0; @@ -81,6 +81,10 @@ void bspr_init( BSPRenderer * bspr, MemoryArena * arena, const BSP * bsp ) { const BSP_Face & face = bsp->faces[ leaf_face ]; const v3 colour = bspr_face_colour( bsp, face ); + // drop outlines + if( strcmp( bsp->textures[ face.texture ].name, "textures/acidwdm2/ink" ) == 0 ) + continue; + const BSP_Vertex * vertices = &bsp->vertices[ face.first_vert ]; const s32 * face_indices = &bsp->mesh_verts[ face.first_mesh_vert ]; @@ -88,6 +92,7 @@ void bspr_init( BSPRenderer * bspr, MemoryArena * arena, const BSP * bsp ) { v3 pos = vertices[ v ].pos; positions[ num_positions++ ] = pos; + normals[ num_normals++ ] = face.normal; colours[ num_colours++ ] = colour; } @@ -98,6 +103,7 @@ void bspr_init( BSPRenderer * bspr, MemoryArena * arena, const BSP * bsp ) { MeshConfig config; config.positions = renderer_new_vb( positions.ptr(), num_positions * sizeof( v3 ) ); + config.normals = renderer_new_vb( normals.ptr(), num_normals * sizeof( v3 ) ); config.colours = renderer_new_vb( colours.ptr(), num_colours * sizeof( v3 ) ); config.indices = renderer_new_ib( indices.ptr(), num_indices * sizeof( u32 ) ); config.num_vertices = num_indices; diff --git a/renderer.cc b/renderer.cc @@ -47,7 +47,6 @@ struct DeleteCommand { VB vb; IB ib; TB tb; - FB fb; Shader shader; Texture texture; Mesh mesh; @@ -474,6 +473,8 @@ static GLenum texfmt_to_internal_format( TextureFormat format ) { return GL_RGB8; case TEXFMT_RGB_U8_SRGB: return GL_SRGB8; + case TEXFMT_RGB_HALF: + return GL_RGB16F; case TEXFMT_RGB_FLOAT: return GL_RGB32F; @@ -613,12 +614,57 @@ static GLenum framebufferattachment_to_glenum( FramebufferAttachment attachment switch( attachment ) { case FB_COLOUR: return GL_COLOR_ATTACHMENT0; case FB_DEPTH: return GL_DEPTH_ATTACHMENT; + case FB_NORMAL: return GL_COLOR_ATTACHMENT1; } FATAL( "invalid FramebufferAttachment: {}", attachment ); return GL_INVALID_ENUM; } +FB renderer_new_fb( const FramebufferConfig & config ) { + GLuint fbo; + glGenFramebuffers( 1, &fbo ); + glBindFramebuffer( GL_FRAMEBUFFER, fbo ); + + FB fb; + fb.fbo = fbo; + + u32 width = 0; + u32 height = 0; + GLenum bufs[ OUTPUTS_COUNT ]; + + for( size_t i = 0; i < config.textures.size(); i++ ) { + bufs[ i ] = GL_NONE; + + const TextureConfig & texture_config = config.textures[ i ].config; + if( texture_config.format == TEXFMT_INVALID ) + continue; + + ASSERT( !is_compressed( texture_config.format ) ); + + GLenum attachment = framebufferattachment_to_glenum( config.textures[ i ].attachment ); + Texture texture = renderer_new_texture( texture_config ); + glFramebufferTexture2D( GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture, 0 ); + + width = texture_config.width; + height = texture_config.height; + fb.textures[ i ] = texture; + if( config.textures[ i ].attachment != FB_DEPTH ) + bufs[ i ] = attachment; + } + + glDrawBuffers( ARRAY_COUNT( bufs ), bufs ); + + ASSERT( glCheckFramebufferStatus( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE ); + ASSERT( width > 0 && height > 0 ); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + fb.width = width; + fb.height = height; + + return fb; +} + FB renderer_new_fb( TextureConfig texture_config, FramebufferAttachment attachment ) { ASSERT( !is_compressed( texture_config.format ) ); @@ -628,7 +674,6 @@ FB renderer_new_fb( TextureConfig texture_config, FramebufferAttachment attachme glGenFramebuffers( 1, &fbo ); glBindFramebuffer( GL_FRAMEBUFFER, fbo ); glFramebufferTexture2D( GL_FRAMEBUFFER, framebufferattachment_to_glenum( attachment ), GL_TEXTURE_2D, texture, 0 ); - glReadBuffer( GL_NONE ); if( attachment == FB_DEPTH ) { glDrawBuffer( GL_NONE ); @@ -639,8 +684,7 @@ FB renderer_new_fb( TextureConfig texture_config, FramebufferAttachment attachme FB fb; fb.fbo = fbo; - fb.attachment = attachment; - fb.texture = texture; + fb.textures[ attachment == FB_COLOUR ? OUTPUT_ALBEDO : OUTPUT_DEPTH ] = texture; fb.width = texture_config.width; fb.height = texture_config.height; return fb; @@ -648,7 +692,11 @@ FB renderer_new_fb( TextureConfig texture_config, FramebufferAttachment attachme void renderer_delete_fb( FB fb ) { glDeleteBuffers( 1, &fb.fbo ); - glDeleteTextures( 1, &fb.texture ); + for( Texture texture : fb.textures ) { + if( texture != 0 ) { + glDeleteTextures( 1, &texture ); + } + } } static GLuint new_gl_shader( GLenum type, array< const char * > srcs ) { @@ -706,6 +754,9 @@ Shader renderer_new_shader( ShaderConfig config ) { glBindAttribLocation( program, ATTR_COLOUR, "colour" ); glBindAttribLocation( program, ATTR_MODEL_TO_WORLD_COL0, "model_to_world" ); + // glBindAttribLocation( program, 1, "output_albedo" ); + // glBindAttribLocation( program, 0, "output_normal" ); + glLinkProgram( program ); glDeleteShader( vs ); @@ -807,6 +858,8 @@ static u32 texture_byte_size( u32 width, u32 height, TextureFormat format, bool return total_dim * 3 * sizeof( u8 ); case TEXFMT_RGB_U8_SRGB: return total_dim * 3 * sizeof( u8 ); + case TEXFMT_RGB_HALF: + return total_dim * 3 * sizeof( u16 ); case TEXFMT_RGB_FLOAT: return total_dim * 3 * sizeof( float ); @@ -880,6 +933,10 @@ Texture renderer_new_texture( TextureConfig config ) { channels = GL_RGB; type = GL_UNSIGNED_BYTE; break; + case TEXFMT_RGB_HALF: + channels = GL_RGB; + type = GL_HALF_FLOAT; + break; case TEXFMT_RGB_FLOAT: channels = GL_RGB; type = GL_FLOAT; diff --git a/renderer.h b/renderer.h @@ -29,6 +29,15 @@ enum UniformSlots { UNIFORMS_COUNT, }; +enum OutputSlots { + OUTPUT_ALBEDO, + OUTPUT_NORMAL, + + OUTPUT_DEPTH, + + OUTPUTS_COUNT, +}; + #define RENDERER_MAX_TEXTURES 4 #define RENDERER_MAX_TEXTURE_BUFFERS 4 @@ -70,6 +79,7 @@ enum TextureFormat { TEXFMT_RGB_U8, TEXFMT_RGB_U8_SRGB, + TEXFMT_RGB_HALF, TEXFMT_RGB_FLOAT, TEXFMT_RGBA_U8, @@ -99,6 +109,7 @@ enum TextureFilterMode { enum FramebufferAttachment { FB_COLOUR, FB_DEPTH, + FB_NORMAL, }; struct TB { @@ -108,8 +119,7 @@ struct TB { struct FB { FramebufferObject fbo; - FramebufferAttachment attachment; - Texture texture; + StaticArray< Texture, OUTPUTS_COUNT > textures = { }; u32 width, height; }; @@ -197,6 +207,15 @@ struct TextureConfig { v4 border_colour; }; +struct FramebufferConfig { + struct FramebufferTexture { + TextureConfig config; + FramebufferAttachment attachment; + }; + + StaticArray< FramebufferTexture, OUTPUTS_COUNT > textures = { }; +}; + enum ClearColourBool { RENDERER_CLEAR_COLOUR_DONT, RENDERER_CLEAR_COLOUR_DO }; enum ClearDepthBool { RENDERER_CLEAR_DEPTH_DONT, RENDERER_CLEAR_DEPTH_DO }; @@ -227,6 +246,7 @@ void renderer_tb_data( TB tb, const void * data, u32 len, BufferUsage usage = BU void renderer_delete_tb( TB tb ); FB renderer_new_fb( TextureConfig texture_format, FramebufferAttachment attachment ); +FB renderer_new_fb( const FramebufferConfig & config ); void renderer_delete_fb( FB fb ); Shader renderer_new_shader( ShaderConfig config ); diff --git a/shaders.cc b/shaders.cc @@ -72,6 +72,13 @@ void shaders_init() { shaders[ SHADER_CLIPMAP_SKIRT ].path = "shaders/clipmap_skirt.glsl"; shaders[ SHADER_CLIPMAP_SKIRT ].texture_uniform_names[ 0 ] = "blue_noise"; + shaders[ SHADER_FLAT_VERTEX_COLOURS_TO_GBUFFER ].path = "shaders/flat_vertex_colours_to_gbuffer.glsl"; + + shaders[ SHADER_GBUFFER ].path = "shaders/gbuffer.glsl"; + shaders[ SHADER_GBUFFER ].texture_uniform_names[ 0 ] = "albedo"; + shaders[ SHADER_GBUFFER ].texture_uniform_names[ 1 ] = "normal"; + shaders[ SHADER_GBUFFER ].texture_uniform_names[ 2 ] = "depth"; + int failed = hotload_shaders(); if( failed != 0 ) { FATAL( "failed to load shaders" ); diff --git a/shaders.h b/shaders.h @@ -16,6 +16,9 @@ enum ShaderID { SHADER_CLIPMAP, SHADER_CLIPMAP_SKIRT, + SHADER_FLAT_VERTEX_COLOURS_TO_GBUFFER, + SHADER_GBUFFER, + SHADER_COUNT, }; diff --git a/shaders/common.glsl b/shaders/common.glsl @@ -54,3 +54,11 @@ vec3 apply_fog( vec3 c, float dist ) { } #define get_dither_noise( tex ) ( ( texture( tex, gl_FragCoord.xy / textureSize( tex, 0 ) ).xxx - vec3( 0.5 ) ) / 128.0 ) + +vec3 normal_to_01( vec3 normal ) { + return ( normal + 1.0 ) * 0.5; +} + +vec4 normal_to_01( vec4 normal ) { + return vec4( ( normal.xyz + 1.0 ) * 0.5, 1.0 ); +} diff --git a/shaders/flat_vertex_colours_to_gbuffer.glsl b/shaders/flat_vertex_colours_to_gbuffer.glsl @@ -0,0 +1,37 @@ +layout( std140 ) uniform view { + mat4 V; + mat4 P; +}; + +struct VSOut { + vec3 normal; + vec3 colour; +}; + +#ifdef VERTEX_SHADER + +in vec3 position; +in vec3 normal; +in vec3 colour; + +out VSOut v2f; + +void main() { + gl_Position = P * V * vec4( position, 1.0 ); + v2f.colour = colour; + v2f.normal = normal; +} + +#else + +in VSOut v2f; + +out vec4 output_albedo; +out vec3 output_normal; + +void main() { + output_albedo = vec4( linear_to_srgb( v2f.colour ), 1.0 ); + output_normal = normalize( v2f.normal ); +} + +#endif diff --git a/shaders/gbuffer.glsl b/shaders/gbuffer.glsl @@ -0,0 +1,71 @@ +uniform sampler2D albedo; +uniform sampler2D normal; +uniform sampler2D depth; + +#ifdef VERTEX_SHADER + +in vec2 position; + +void main() { + gl_Position = vec4( position, 0.0, 1.0 ); +} + +#else + +out vec4 screen_colour; + +float LinearizeDepth(float depth) +{ + float z = depth * 2.0 - 1.0; // Back to NDC + return (2.0 * 0.1 * 10000.0) / (10000.0 + 0.1 - z * (10000.0 - 0.1)); +} + +void main() { + vec2 pixel = vec2( 1.0 / textureSize( albedo, 0 ) ); + vec3 pole = vec3( -1.0, 0.0, 1.0 ); + + vec2 uv = gl_FragCoord.xy / textureSize( albedo, 0 ); + + float d0 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.xx ).r ); // x1, y1 + float d1 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.yx ).r ); // x2, y1 + float d2 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.zx ).r ); // x3, y1 + float d3 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.xy ).r ); // x1, y2 + // float d4 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.yy ).r ); // x2, y2 + float d5 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.zy ).r ); // x3, y2 + float d6 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.xz ).r ); // x1, y3 + float d7 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.yz ).r ); // x2, y3 + float d8 = LinearizeDepth( texture( depth, uv + pixel.xy * pole.zz ).r ); // x3, y3 + + float edgeness_depth = ( + abs( d1 - d7 ) + + abs( d5 - d3 ) + + abs( d0 - d8 ) + + abs( d2 - d6 ) + ); + + vec3 n0 = texture( normal, uv + pixel.xy * pole.xx ).rgb; // x1, y1 + vec3 n1 = texture( normal, uv + pixel.xy * pole.yx ).xyz; // x2, y1 + vec3 n2 = texture( normal, uv + pixel.xy * pole.zx ).xyz; // x3, y1 + vec3 n3 = texture( normal, uv + pixel.xy * pole.xy ).xyz; // x1, y2 + // vec3 n4 = texture( normal, uv + pixel.xy * pole.yy ).xyz; // x2, y2 + vec3 n5 = texture( normal, uv + pixel.xy * pole.zy ).xyz; // x3, y2 + vec3 n6 = texture( normal, uv + pixel.xy * pole.xz ).xyz; // x1, y3 + vec3 n7 = texture( normal, uv + pixel.xy * pole.yz ).xyz; // x2, y3 + vec3 n8 = texture( normal, uv + pixel.xy * pole.zz ).xyz; // x3, y3 + + float edgeness_normal = ( + max( 0.0, 1.0 - dot( n1, n7 ) ) + + max( 0.0, 1.0 - dot( n5, n3 ) ) + + max( 0.0, 1.0 - dot( n0, n8 ) ) + + max( 0.0, 1.0 - dot( n2, n6 ) ) + ) * 0.25; + + edgeness_depth = max( edgeness_depth - 10.0, 0.0 ); + edgeness_normal = max( edgeness_normal - 0.1, 0.0 ) / 0.9; + + vec4 edgeness = vec4( vec3( max( edgeness_depth, edgeness_normal ) ), 0.0 ); + + screen_colour = texture( albedo, uv ) * 0.0001 + normal_to_01( texture( normal, uv ) ) - edgeness; +} + +#endif diff --git a/shaders/shadowed_vertex_colours.glsl b/shaders/shadowed_vertex_colours.glsl @@ -57,10 +57,11 @@ void main() { vec3 light_colour = vec3( 400.0, 400.0, 400.0 ); vec3 lightdir = normalize( light_pos - v2f.position ); vec3 to_camera = normalize( camera_pos - v2f.position ); + vec3 normal = normalize( v2f.normal ); vec3 light_ndc = v2f.light_space_position.xyz / v2f.light_space_position.w; vec3 light_norm = light_ndc * 0.5 + 0.5; - vec2 offsets = get_shadow_offsets( v2f.normal, lightdir ); + vec2 offsets = get_shadow_offsets( normal, lightdir ); float bias = 0.00002 + 0.00001 * offsets.x + 0.00005 * offsets.y; float shadow = 0; @@ -85,7 +86,7 @@ void main() { noise = ( noise - vec3( 0.5 ) ) / 128.0; vec3 ambient = vec3( 0.03 ) * v2f.colour; - vec3 Lo = cook_torrance_brdf( to_camera, v2f.position, v2f.normal, light_pos, light_colour, v2f.colour, 0.75, 0.0 ); + vec3 Lo = cook_torrance_brdf( to_camera, v2f.position, normal, light_pos, light_colour, v2f.colour, 0.75, 0.0 ); screen_colour = vec4( linear_to_srgb( reinhard_tonemap( ambient + Lo * shadow + noise ) ), 1.0 ); }