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:
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 );
}