Merge pull request #2 from andrewjhaman/main

Implement Bilinearly-Blended PCF Shadow Mapping for non-armature Meshes
main
Cameron Murphy Reikes 1 year ago committed by GitHub
commit fcd7b99f43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,3 +7,5 @@ Be very cautious about committing a change to any large asset files, i.e the art
You must clone with git lfs is, and download git lfs files in this repository. If you don't know what that is, google it You must clone with git lfs is, and download git lfs files in this repository. If you don't know what that is, google it
Open `art.blend`, go to the scripting tab and hit the play button run the script and export all the 3d assets. Then, make sure that when you build, you also build and run the codegen so that said assets and other files are copied and imported. For debug builds on windows, that's `call build_desktop_debug.bat codegen`, the codegen argument to the build script causing it to run codegen Open `art.blend`, go to the scripting tab and hit the play button run the script and export all the 3d assets. Then, make sure that when you build, you also build and run the codegen so that said assets and other files are copied and imported. For debug builds on windows, that's `call build_desktop_debug.bat codegen`, the codegen argument to the build script causing it to run codegen
To enable codegen error messages, change @echo off to @echo on in run_codegen.bat

358
main.c

@ -1320,6 +1320,7 @@ ThreeDeeLevel load_level(MD_Arena *arena, MD_String8 binary_file)
#include "quad-sapp.glsl.h" #include "quad-sapp.glsl.h"
#include "threedee.glsl.h" #include "threedee.glsl.h"
#include "armature.glsl.h" #include "armature.glsl.h"
#include "shadow_mapper.glsl.h"
AABB level_aabb = { .upper_left = { 0.0f, 0.0f }, .lower_right = { TILE_SIZE * LEVEL_TILES, -(TILE_SIZE * LEVEL_TILES) } }; AABB level_aabb = { .upper_left = { 0.0f, 0.0f }, .lower_right = { TILE_SIZE * LEVEL_TILES, -(TILE_SIZE * LEVEL_TILES) } };
GameState gs = { 0 }; GameState gs = { 0 };
@ -2673,6 +2674,17 @@ stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
stbtt_fontinfo font; stbtt_fontinfo font;
typedef struct {
sg_pass_action pass_action;
sg_pass pass;
sg_pipeline pip;
sg_image color_img;
sg_image depth_img;
} Shadow_State;
Shadow_State init_shadow_state();
// @Place(sokol state struct) // @Place(sokol state struct)
static struct static struct
{ {
@ -2684,35 +2696,80 @@ static struct
sg_pipeline threedee_pip; sg_pipeline threedee_pip;
sg_pipeline armature_pip; sg_pipeline armature_pip;
sg_bindings threedee_bind; sg_bindings threedee_bind;
Shadow_State shadows;
} state; } state;
int num_draw_calls = 0; int num_draw_calls = 0;
int num_vertices = 0; int num_vertices = 0;
void draw_placed(Mat4 view, Mat4 projection, PlacedMesh *cur) void draw_shadow(Mat4 view, Mat4 projection, PlacedMesh *cur)
{
sg_apply_pipeline(state.shadows.pip);
Mesh *drawing = cur->draw_with;
sg_bindings bindings = {0};
bindings.fs_images[SLOT_threedee_tex] = image_gigatexture;
ARR_ITER(sg_image, state.threedee_bind.vs_images)
{
*it = (sg_image){0};
}
bindings.vertex_buffers[0] = drawing->loaded_buffer;
sg_apply_bindings(&bindings);
Mat4 model = transform_to_mat(cur->t);
shadow_mapper_vs_params_t vs_params = {0};
memcpy(vs_params.model, (float*)&model, sizeof(model));
memcpy(vs_params.view, (float*)&view, sizeof(view));
memcpy(vs_params.projection, (float*)&projection, sizeof(projection));
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_shadow_mapper_vs_params, &SG_RANGE(vs_params));
num_draw_calls += 1;
num_vertices += (int)drawing->num_vertices;
sg_draw(0, (int)drawing->num_vertices, 1);
}
void draw_placed(Mat4 view, Mat4 projection, Mat4 light_matrix, PlacedMesh *cur)
{ {
sg_apply_pipeline(state.threedee_pip); sg_apply_pipeline(state.threedee_pip);
Mesh *drawing = cur->draw_with; Mesh *drawing = cur->draw_with;
state.threedee_bind.fs_images[SLOT_threedee_tex] = image_gigatexture;
ARR_ITER(sg_image, state.threedee_bind.vs_images) ARR_ITER(sg_image, state.threedee_bind.vs_images)
{ {
*it = (sg_image){0}; *it = (sg_image){0};
} }
ARR_ITER(sg_image, state.threedee_bind.fs_images)
{
*it = (sg_image){0};
}
state.threedee_bind.fs_images[SLOT_threedee_tex] = image_gigatexture;
state.threedee_bind.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img;
state.threedee_bind.vertex_buffers[0] = drawing->loaded_buffer; state.threedee_bind.vertex_buffers[0] = drawing->loaded_buffer;
sg_apply_bindings(&state.threedee_bind); sg_apply_bindings(&state.threedee_bind);
Mat4 model = transform_to_mat(cur->t); Mat4 model = transform_to_mat(cur->t);
threedee_vs_params_t params = {0}; threedee_vs_params_t vs_params = {0};
memcpy(params.model, (float*)&model, sizeof(model)); memcpy(vs_params.model, (float*)&model, sizeof(model));
memcpy(params.view, (float*)&view, sizeof(view)); memcpy(vs_params.view, (float*)&view, sizeof(view));
memcpy(params.projection, (float*)&projection, sizeof(projection)); memcpy(vs_params.projection, (float*)&projection, sizeof(projection));
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_threedee_vs_params, &SG_RANGE(params)); memcpy(vs_params.directional_light_space_matrix, (float*)&light_matrix, sizeof(light_matrix));
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_threedee_vs_params, &SG_RANGE(vs_params));
num_draw_calls += 1; num_draw_calls += 1;
num_vertices += (int)drawing->num_vertices; num_vertices += (int)drawing->num_vertices;
threedee_fs_params_t fs_params = {0};
fs_params.shadow_map_dimension = SHADOW_MAP_DIMENSION;
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_fs_params, &SG_RANGE(fs_params));
sg_draw(0, (int)drawing->num_vertices, 1); sg_draw(0, (int)drawing->num_vertices, 1);
} }
@ -2842,9 +2899,19 @@ void draw_armature(Mat4 view, Mat4 projection, Transform t, Armature *armature,
.subimage[0][0] = (sg_range){bones_tex, bones_tex_size}, .subimage[0][0] = (sg_range){bones_tex, bones_tex_size},
}); });
ARR_ITER(sg_image, state.threedee_bind.vs_images)
{
*it = (sg_image){0};
}
ARR_ITER(sg_image, state.threedee_bind.fs_images)
{
*it = (sg_image){0};
}
state.threedee_bind.vertex_buffers[0] = armature->loaded_buffer; state.threedee_bind.vertex_buffers[0] = armature->loaded_buffer;
state.threedee_bind.vs_images[SLOT_armature_bones_tex] = armature->bones_texture; state.threedee_bind.vs_images[SLOT_armature_bones_tex] = armature->bones_texture;
state.threedee_bind.fs_images[SLOT_armature_tex] = image_gigatexture; state.threedee_bind.fs_images[SLOT_armature_tex] = image_gigatexture;
sg_apply_bindings(&state.threedee_bind); sg_apply_bindings(&state.threedee_bind);
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_armature_vs_params, &SG_RANGE(params)); sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_armature_vs_params, &SG_RANGE(params));
@ -3200,6 +3267,9 @@ void init(void)
.label = "quad-vertices" .label = "quad-vertices"
}); });
state.shadows = init_shadow_state();
const sg_shader_desc *desc = quad_program_shader_desc(sg_query_backend()); const sg_shader_desc *desc = quad_program_shader_desc(sg_query_backend());
assert(desc); assert(desc);
sg_shader shd = sg_make_shader(desc); sg_shader shd = sg_make_shader(desc);
@ -3778,6 +3848,8 @@ extern bool profiling;
#else #else
bool profiling; bool profiling;
#endif #endif
#else
const bool show_devtools = false;
#endif #endif
Color debug_color = {1,0,0,1}; Color debug_color = {1,0,0,1};
@ -3870,6 +3942,11 @@ Vec2 threedee_to_screenspace(Vec3 world)
// In that case the projected value is undefined, because the perspective // In that case the projected value is undefined, because the perspective
// divide produces nans. // divide produces nans.
Vec3 clip_space; Vec3 clip_space;
if (clip_space_no_perspective_divide.z < 0.0) {
return V2(0.0, 0.0);
}
if(clip_space_no_perspective_divide.w != 0.0) if(clip_space_no_perspective_divide.w != 0.0)
{ {
clip_space = perspective_divide(clip_space_no_perspective_divide); clip_space = perspective_divide(clip_space_no_perspective_divide);
@ -4072,24 +4149,6 @@ int sorting_key_at(Vec2 pos)
return -(int)pos.y; return -(int)pos.y;
} }
void draw_shadow_for(DrawParams d)
{
Quad sheared_quad = d.quad;
float height = d.quad.ur.y - d.quad.lr.y;
Vec2 shear_addition = V2(-height*0.35f, -height*0.2f);
sheared_quad.ul = AddV2(sheared_quad.ul, shear_addition);
sheared_quad.ur = AddV2(sheared_quad.ur, shear_addition);
d.quad = sheared_quad;
d.tint = (Color) { 0, 0, 0, 0.2f };
d.sorting_key -= 1;
d.alpha_clip_threshold = 0.0f;
dbgline(sheared_quad.ul, sheared_quad.ur);
dbgline(sheared_quad.ur, sheared_quad.lr);
dbgline(sheared_quad.lr, sheared_quad.ll);
dbgline(sheared_quad.ll, sheared_quad.ul);
draw_quad(d);
}
//void draw_animated_sprite(AnimatedSprite *s, double elapsed_time, bool flipped, Vec2 pos, Color tint) //void draw_animated_sprite(AnimatedSprite *s, double elapsed_time, bool flipped, Vec2 pos, Color tint)
void draw_animated_sprite(DrawnAnimatedSprite d) void draw_animated_sprite(DrawnAnimatedSprite d)
{ {
@ -4123,7 +4182,6 @@ void draw_animated_sprite(DrawnAnimatedSprite d)
region.lower_right = AddV2(region.upper_left, s->region_size); region.lower_right = AddV2(region.upper_left, s->region_size);
DrawParams drawn = (DrawParams) { q, spritesheet_img, region, d.tint, .sorting_key = sorting_key_at(d.pos), .layer = LAYER_WORLD, }; DrawParams drawn = (DrawParams) { q, spritesheet_img, region, d.tint, .sorting_key = sorting_key_at(d.pos), .layer = LAYER_WORLD, };
if (!d.no_shadow) draw_shadow_for(drawn);
draw_quad(drawn); draw_quad(drawn);
} }
@ -4799,6 +4857,227 @@ Transform entity_transform(Entity *e)
*/ */
} }
void do_shadow_pass(Shadow_State* shadow_state, Mat4 shadow_view_matrix, Mat4 shadow_projection_matrix) {
sg_begin_pass(shadow_state->pass, &shadow_state->pass_action);
sg_apply_pipeline(shadow_state->pip);
//We use the texture, just in case we want to do alpha cards. I.e. we need to test alpha for leaf quads etc.
state.threedee_bind.fs_images[SLOT_threedee_tex] = image_gigatexture;
state.threedee_bind.fs_images[SLOT_threedee_shadow_map].id = 0;
for(PlacedMesh *cur = level_threedee.placed_mesh_list; cur; cur = cur->next)
{
draw_shadow(shadow_view_matrix, shadow_projection_matrix, cur);
}
ENTITIES_ITER(gs.entities)
{
if(it->is_npc || it->is_character)
{
Transform draw_with = entity_transform(it);
if(it->npc_kind == NPC_SimpleWorm)
{
// draw_armature_shadow(shadow_view_matrix, shadow_projection_matrix, draw_with, &armature, (float)elapsed_time);
} else {
draw_shadow(shadow_view_matrix, shadow_projection_matrix, &(PlacedMesh){.draw_with = &mesh_player, .t = draw_with, });
}
}
}
sg_end_pass();
}
Shadow_State init_shadow_state() {
//To start off with, most of this initialisation code is taken from the
// sokol shadows sample, which can be found here.
// https://floooh.github.io/sokol-html5/shadows-sapp.html
Shadow_State shadows = {0};
shadows.pass_action = (sg_pass_action) {
.colors[0] = {
.action = SG_ACTION_CLEAR,
.value = { 1.0f, 1.0f, 1.0f, 1.0f }
}
};
/*
As of right now, it looks like sokol_gfx does not support depth only
rendering passes, so we create the colour buffer always. It will likely
be pertinent to just dig into sokol and add the functionality we want later,
but as a first pass, we will just do as the romans do. I.e. have both a colour
and depth component. - Canada Day 2023.
*/
sg_image_desc img_desc = {
.render_target = true,
.width = SHADOW_MAP_DIMENSION,
.height = SHADOW_MAP_DIMENSION,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_CLAMP_TO_BORDER,
.wrap_v = SG_WRAP_CLAMP_TO_BORDER,
.border_color = SG_BORDERCOLOR_OPAQUE_WHITE,
.sample_count = 1,
.label = "shadow-map-color-image"
};
shadows.color_img = sg_make_image(&img_desc);
img_desc.pixel_format = SG_PIXELFORMAT_DEPTH;
img_desc.label = "shadow-map-depth-image";
shadows.depth_img = sg_make_image(&img_desc);
shadows.pass = sg_make_pass(&(sg_pass_desc){
.color_attachments[0].image = shadows.color_img,
.depth_stencil_attachment.image = shadows.depth_img,
.label = "shadow-map-pass"
});
shadows.pip = sg_make_pipeline(&(sg_pipeline_desc){
.layout = {
.attrs = {
[ATTR_threedee_vs_pos_in].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_threedee_vs_uv_in].format = SG_VERTEXFORMAT_FLOAT2,
}
},
.shader = sg_make_shader(shadow_mapper_program_shader_desc(sg_query_backend())),
// Cull front faces in the shadow map pass
.cull_mode = SG_CULLMODE_FRONT,
.sample_count = 1,
.depth = {
.pixel_format = SG_PIXELFORMAT_DEPTH,
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true,
},
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
.label = "shadow-map-pipeline"
});
return shadows;
}
typedef struct {
float l;
float r;
float t;
float b;
float n;
float f;
} Shadow_Volume_Params;
float round_to_nearest(float input, float round_target)
{
float result = 0.0f;
if(round_target != 0.0f)
{
float div = roundf(input / round_target);
result = div * round_target;
}
return result;
}
Shadow_Volume_Params calculate_shadow_volume_params(Vec3 light_dir)
{
Shadow_Volume_Params result = {0};
//first, we calculate the scene bound
//NOTE: Once we are moved to a pre-pass queue making type deal, this could be moved into that
// loop.
//For simplicity and speed, at the moment we consider only entity positions, not their extents when constructing the scene bounds.
//To make up for this, we add an extra padding-skirt to the bounds.
Mat4 light_space_matrix = LookAt_RH((Vec3){0}, light_dir, V3(0, 1, 0));
Vec3 scene_min = V3( INFINITY, INFINITY, INFINITY);
Vec3 scene_max = V3(-INFINITY, -INFINITY, -INFINITY);
for(PlacedMesh *cur = level_threedee.placed_mesh_list; cur; cur = cur->next)
{
Vec3 p = MulM4V3(light_space_matrix, cur->t.offset);
scene_min.x = fminf(scene_min.x, p.x);
scene_max.x = fmaxf(scene_max.x, p.x);
scene_min.y = fminf(scene_min.y, p.y);
scene_max.y = fmaxf(scene_max.y, p.y);
scene_min.z = fminf(scene_min.z, p.z);
scene_max.z = fmaxf(scene_max.z, p.z);
}
ENTITIES_ITER(gs.entities)
{
if(it->is_npc || it->is_character)
{
Transform draw_with = entity_transform(it);
Vec3 p = MulM4V3(light_space_matrix, draw_with.offset);
scene_min.x = fminf(scene_min.x, p.x);
scene_max.x = fmaxf(scene_max.x, p.x);
scene_min.y = fminf(scene_min.y, p.y);
scene_max.y = fmaxf(scene_max.y, p.y);
scene_min.z = fminf(scene_min.z, p.z);
scene_max.z = fmaxf(scene_max.z, p.z);
}
}
//pad to account for entity width
float pad = 2.5f;
scene_min.x -= pad;
scene_min.y -= pad;
scene_max.x += pad;
scene_max.y += pad;
result.l = scene_min.x;
result.r = scene_max.x;
result.b = scene_min.y;
result.t = scene_max.y;
float w = result.r - result.l;
float h = result.t - result.b;
float actual_size = fmaxf(w, h);
{//Make sure it is square
float diff = actual_size - h;
if (diff > 0) {
float half_diff = diff * 0.5f;
result.t += half_diff;
result.b -= half_diff;
}
diff = actual_size - w;
if (diff > 0) {
float half_diff = diff * 0.5f;
result.r += half_diff;
result.l -= half_diff;
}
}
{//Snap the light position to shadow_map texel grid, to reduce shimmering when moving
float texel_size = actual_size / (float)SHADOW_MAP_DIMENSION;
result.l = round_to_nearest(result.l, texel_size);
result.r = round_to_nearest(result.r, texel_size);
result.b = round_to_nearest(result.b, texel_size);
result.t = round_to_nearest(result.t, texel_size);
}
result.n = -100.0;
result.f = 200.0;
return result;
}
void frame(void) void frame(void)
{ {
static float speed_factor = 1.0f; static float speed_factor = 1.0f;
@ -4877,6 +5156,24 @@ void frame(void)
movement = NormV2(movement); movement = NormV2(movement);
} }
float spin_factor = 0.5f;
float t = (float)elapsed_time * spin_factor;
float x = cosf(t);
float z = sinf(t);
Vec3 light_dir = NormV3(V3(x, -0.5, z));
Shadow_Volume_Params svp = calculate_shadow_volume_params(light_dir);
Mat4 shadow_view_matrix = LookAt_RH(V3(0, 0, 0), light_dir, V3(0, 1, 0));
Mat4 shadow_projection_matrix = Orthographic_RH_NO(svp.l, svp.r, svp.b, svp.t, svp.n, svp.f);
Mat4 light_space_matrix = MulM4(shadow_projection_matrix, shadow_view_matrix);
do_shadow_pass(&state.shadows, shadow_view_matrix, shadow_projection_matrix);
// make movement relative to camera forward // make movement relative to camera forward
Vec3 facing = NormV3(SubV3(player_pos, cam_pos)); Vec3 facing = NormV3(SubV3(player_pos, cam_pos));
Vec3 right = Cross(facing, V3(0,1,0)); Vec3 right = Cross(facing, V3(0,1,0));
@ -4888,6 +5185,7 @@ void frame(void)
sg_apply_pipeline(state.threedee_pip); sg_apply_pipeline(state.threedee_pip);
state.threedee_bind.fs_images[SLOT_threedee_tex] = image_gigatexture; state.threedee_bind.fs_images[SLOT_threedee_tex] = image_gigatexture;
state.threedee_bind.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img;
view = Translate(V3(0.0, 1.0, -5.0f)); view = Translate(V3(0.0, 1.0, -5.0f));
//view = LookAt_RH(V3(0,1,-5 //view = LookAt_RH(V3(0,1,-5
@ -4901,7 +5199,7 @@ void frame(void)
{ {
view = LookAt_RH(cam_pos, player_pos, V3(0, 1, 0)); view = LookAt_RH(cam_pos, player_pos, V3(0, 1, 0));
} }
projection = Perspective_RH_NO(PI32/4.0f, screen_size().x / screen_size().y, 0.01f, 1000.0f); projection = Perspective_RH_NO(FIELD_OF_VIEW, screen_size().x / screen_size().y, NEAR_PLANE_DISTANCE, FAR_PLANE_DISTANCE);
// debug draw armature // debug draw armature
for(MD_u64 i = 0; i < armature.bones_length; i++) for(MD_u64 i = 0; i < armature.bones_length; i++)
@ -4965,7 +5263,7 @@ void frame(void)
for(PlacedMesh *cur = level_threedee.placed_mesh_list; cur; cur = cur->next) for(PlacedMesh *cur = level_threedee.placed_mesh_list; cur; cur = cur->next)
{ {
draw_placed(view, projection, cur); draw_placed(view, projection, light_space_matrix, cur);
} }
@ -4980,7 +5278,7 @@ void frame(void)
} }
else else
{ {
draw_placed(view, projection, &(PlacedMesh){.draw_with = &mesh_player, .t = draw_with, }); draw_placed(view, projection, light_space_matrix, &(PlacedMesh){.draw_with = &mesh_player, .t = draw_with, });
} }
} }
} }
@ -6204,13 +6502,11 @@ void frame(void)
draw_centered_text((TextParams){ false, MD_S8Lit("Needs 3 party members"), AddV2(it->pos, V2(0.0, 100.0)), blendalpha(WHITE, it->idol_reminder_opacity), 1.0f}); draw_centered_text((TextParams){ false, MD_S8Lit("Needs 3 party members"), AddV2(it->pos, V2(0.0, 100.0)), blendalpha(WHITE, it->idol_reminder_opacity), 1.0f});
draw_shadow_for(d);
draw_quad(d); draw_quad(d);
} }
else if(it->machine_kind == MACH_arrow_shooter) else if(it->machine_kind == MACH_arrow_shooter)
{ {
DrawParams d = (DrawParams){ quad_centered(it->pos, V2(TILE_SIZE, TILE_SIZE)), IMG(image_arrow_shooter), WHITE, .layer = LAYER_WORLD, .sorting_key = sorting_key_at(it->pos) }; DrawParams d = (DrawParams){ quad_centered(it->pos, V2(TILE_SIZE, TILE_SIZE)), IMG(image_arrow_shooter), WHITE, .layer = LAYER_WORLD, .sorting_key = sorting_key_at(it->pos) };
draw_shadow_for(d);
draw_quad(d); draw_quad(d);
} }

@ -8,7 +8,6 @@ rmdir /S /q "assets\exported_3d"
mkdir "assets\exported_3d" || goto :error mkdir "assets\exported_3d" || goto :error
copy "art\exported\*" "assets\exported_3d\" || goto :error copy "art\exported\*" "assets\exported_3d\" || goto :error
copy "art\gigatexture.png" "assets\exported_3d\gigatexture.png" || goto :error copy "art\gigatexture.png" "assets\exported_3d\gigatexture.png" || goto :error
@echo off
rmdir /S /q gen rmdir /S /q gen
mkdir gen mkdir gen
@ -17,6 +16,7 @@ mkdir gen
thirdparty\sokol-shdc.exe --input quad.glsl --output gen\quad-sapp.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error thirdparty\sokol-shdc.exe --input quad.glsl --output gen\quad-sapp.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
thirdparty\sokol-shdc.exe --input threedee.glsl --output gen\threedee.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error thirdparty\sokol-shdc.exe --input threedee.glsl --output gen\threedee.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
thirdparty\sokol-shdc.exe --input armature.glsl --output gen\armature.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error thirdparty\sokol-shdc.exe --input armature.glsl --output gen\armature.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
thirdparty\sokol-shdc.exe --input shadow_mapper.glsl --output gen\shadow_mapper.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
@REM metadesk codegen @REM metadesk codegen
cl /Ithirdparty /W3 /Zi /WX codegen.c || goto :error cl /Ithirdparty /W3 /Zi /WX codegen.c || goto :error

@ -0,0 +1,54 @@
@module shadow_mapper
@vs vs
in vec3 pos_in;
in vec2 uv_in;
out vec3 pos;
out vec2 uv;
uniform vs_params {
mat4 model;
mat4 view;
mat4 projection;
};
void main() {
pos = pos_in;
uv = uv_in;
gl_Position = projection * view * model * vec4(pos_in, 1.0);
}
@end
@fs fs
vec4 encodeDepth(float v) {
vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v;
enc = fract(enc);
enc -= enc.yzww * vec4(1.0/255.0,1.0/255.0,1.0/255.0,0.0);
return enc;
}
uniform sampler2D tex;
in vec3 pos;
in vec2 uv;
out vec4 frag_color;
void main() {
vec4 col = texture(tex, uv);
if(col.a < 0.5)
{
discard;
}
float depth = gl_FragCoord.z;
frag_color = encodeDepth(depth);
}
@end
@program program vs fs

@ -3,28 +3,121 @@
@vs vs @vs vs
in vec3 pos_in; in vec3 pos_in;
in vec2 uv_in; in vec2 uv_in;
out vec3 pos; out vec3 pos;
out vec2 uv; out vec2 uv;
out vec4 light_space_fragment_position;
uniform vs_params { uniform vs_params {
mat4 model; mat4 model;
mat4 view; mat4 view;
mat4 projection; mat4 projection;
mat4 directional_light_space_matrix;
}; };
void main() { void main() {
pos = pos_in; pos = pos_in;
uv = uv_in; uv = uv_in;
gl_Position = projection * view * model * vec4(pos_in, 1.0);
vec4 world_space_frag_pos = model * vec4(pos_in, 1.0);
vec4 frag_pos = view * world_space_frag_pos;
gl_Position = projection * frag_pos;
light_space_fragment_position = directional_light_space_matrix * vec4(world_space_frag_pos.xyz, 1.0);
} }
@end @end
@fs fs @fs fs
uniform sampler2D tex; uniform sampler2D tex;
uniform sampler2D shadow_map;
uniform fs_params {
int shadow_map_dimension;
};
in vec3 pos; in vec3 pos;
in vec2 uv; in vec2 uv;
in vec4 light_space_fragment_position;
out vec4 frag_color; out vec4 frag_color;
float decodeDepth(vec4 rgba) {
return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0));
}
float do_shadow_sample(sampler2D shadowMap, vec2 uv, float scene_depth) {
{
//WebGL does not support GL_CLAMP_TO_BORDER, or border colors at all it seems, so we have to check explicitly.
//This will probably slow down other versions which do support texture borders, but the current system does
// not provide a non-overly complex way to include/not-include this code based on the backend. So here it is.
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)
return 1.0;
}
float map_depth = decodeDepth(texture(shadowMap, uv));
map_depth += 0.001;//bias to counter self-shadowing
return step(scene_depth, map_depth);
}
float bilinear_shadow_sample(sampler2D shadowMap, vec2 uv, int texture_width, int texture_height, float scene_depth_light_space) {
vec2 texture_dim = vec2(float(texture_width), float(texture_height));
vec2 texel_dim = vec2(1.0 / float(texture_width ), 1.0 / float(texture_height));
vec2 texel_uv = uv * vec2(texture_dim);
vec2 texel_uv_floor = floor(texel_uv) * texel_dim;
vec2 texel_uv_ceil = ceil(texel_uv) * texel_dim;
vec2 uv_0 = texel_uv_floor;
vec2 uv_1 = vec2(texel_uv_ceil.x , texel_uv_floor.y);
vec2 uv_2 = vec2(texel_uv_floor.x, texel_uv_ceil.y );
vec2 uv_3 = vec2(texel_uv_ceil.x , texel_uv_ceil.y );
float bl = do_shadow_sample(shadowMap, uv_0, scene_depth_light_space);
float br = do_shadow_sample(shadowMap, uv_1, scene_depth_light_space);
float tl = do_shadow_sample(shadowMap, uv_2, scene_depth_light_space);
float tr = do_shadow_sample(shadowMap, uv_3, scene_depth_light_space);
vec2 interp = fract(texel_uv);
float bot = mix(bl, br, interp.x);
float top = mix(tl, tr, interp.x);
float result = mix(bot, top, interp.y);
return result;
}
float calculate_shadow_factor(sampler2D shadowMap, vec4 light_space_fragment_position) {
float shadow = 1.0;
vec3 projected_coords = light_space_fragment_position.xyz / light_space_fragment_position.w;
if(projected_coords.z > 1.0)
return shadow;
projected_coords = projected_coords * 0.5f + 0.5f;
float current_depth = projected_coords.z;
vec2 shadow_uv = projected_coords.xy;
float texel_step_size = 1.0 / float(shadow_map_dimension);
for (int x=-2; x<=2; x++) {
for (int y=-2; y<=2; y++) {
vec2 off = vec2(x*texel_step_size, y*texel_step_size);
// shadow += do_shadow_sample(shadowMap, shadow_uv+off, current_depth);
shadow += bilinear_shadow_sample(shadowMap, shadow_uv+off, shadow_map_dimension, shadow_map_dimension, current_depth);
}
}
shadow /= 25.0;
return shadow;
}
void main() { void main() {
vec4 col = texture(tex, uv); vec4 col = texture(tex, uv);
if(col.a < 0.5) if(col.a < 0.5)
@ -33,7 +126,12 @@ void main() {
} }
else else
{ {
frag_color = vec4(col.rgb, 1.0); vec3 light_dir = normalize(vec3(1, -1, 0));
float shadow_factor = calculate_shadow_factor(shadow_map, light_space_fragment_position);
shadow_factor = shadow_factor * 0.5 + 0.5;
frag_color = vec4(col.rgb*shadow_factor, 1.0);
} }
} }
@end @end

@ -50,3 +50,10 @@
#define MAX_ASTAR_NODES 512 #define MAX_ASTAR_NODES 512
#define TIME_BETWEEN_PATH_GENS (0.5f) #define TIME_BETWEEN_PATH_GENS (0.5f)
//Rendering
#define FIELD_OF_VIEW (PI32/4.0f)
#define NEAR_PLANE_DISTANCE (0.01f)
#define FAR_PLANE_DISTANCE (1000.0f)
#define SHADOW_MAP_DIMENSION (2048)

@ -0,0 +1,5 @@
@block clamp_to_border
//WebGL does not support GL_CLAMP_TO_BORDER, or border colors at all it seems, so we have to check explicitly.
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)
return 1.0;
@end
Loading…
Cancel
Save