A hackable template for creating small and fast browser games.

(ForwardShading) Fix shadows ata small angles

+12 -19
+1 -1
ForwardShading/scenes/sce_stage.ts
··· 23 23 game.ViewportResized = true; 24 24 25 25 // Camera. 26 - instantiate(game, [...blueprint_camera(game), set_position(0, 0, 7), set_rotation(0, 180, 0)]); 26 + instantiate(game, [...blueprint_camera(game), set_position(0, 0, 9), set_rotation(0, 180, 0)]); 27 27 28 28 // Minimap Camera. 29 29 instantiate(game, [
+11 -18
materials/mat_forward_colored_shadows.ts
··· 12 12 uniform mat4 pv; 13 13 uniform mat4 world; 14 14 uniform mat4 self; 15 + uniform mat4 shadow_space; 15 16 16 17 layout(location=${Attribute.Position}) in vec4 attr_position; 17 18 layout(location=${Attribute.Normal}) in vec3 attr_normal; 18 19 19 20 out vec4 vert_position; 21 + out vec4 vert_position_shadow; 20 22 out vec3 vert_normal; 21 23 22 24 void main() { 23 25 vert_position = world * attr_position; 26 + vert_position_shadow = shadow_space * vert_position; 24 27 vert_normal = (vec4(attr_normal, 0.0) * self).xyz; 25 28 gl_Position = pv * vert_position; 26 29 } ··· 28 31 29 32 let fragment = `#version 300 es\n 30 33 precision mediump float; 31 - precision lowp sampler2DShadow; 34 + precision mediump sampler2DShadow; 32 35 33 36 uniform vec3 eye; 34 37 uniform vec4 diffuse_color; ··· 36 39 uniform vec4 emissive_color; 37 40 uniform vec4 light_positions[${MAX_FORWARD_LIGHTS}]; 38 41 uniform vec4 light_details[${MAX_FORWARD_LIGHTS}]; 39 - uniform mat4 shadow_space; 40 42 uniform sampler2DShadow shadow_map; 41 43 42 44 in vec4 vert_position; 45 + in vec4 vert_position_shadow; 43 46 in vec3 vert_normal; 44 47 45 48 out vec4 frag_color; 46 49 47 50 ${INCLUDE_GAMMA_CORRECTION} 48 51 49 - // How much shadow to apply at world_pos, expressed as [min, 1]: 50 - // min = completely in shadow, 1 = completely not in shadow 51 - float shadow_factor(vec4 world_pos, float min) { 52 - vec4 shadow_space_pos = shadow_space * world_pos; 53 - vec3 shadow_space_ndc = shadow_space_pos.xyz / shadow_space_pos.w; 54 - // Transform the [-1, 1] NDC to [0, 1] to match the shadow texture data. 55 - shadow_space_ndc = shadow_space_ndc * 0.5 + 0.5; 56 - 57 - // Add shadow bias to avoid shadow acne. 58 - shadow_space_ndc.z -= 0.001; 59 - 60 - return texture(shadow_map, shadow_space_ndc) * (1.0 - min) + min; 61 - } 62 - 63 52 void main() { 64 53 vec3 world_normal = normalize(vert_normal); 65 54 ··· 81 70 vec3 light_normal; 82 71 if (light_kind == ${LightKind.Directional}) { 83 72 light_normal = light_positions[i].xyz; 73 + // --- Shadow mapping --- 74 + // Apply the shadow source's projection and add bias to avoid shadow acne. 75 + vec3 sample_position = vert_position_shadow.xyz / vert_position_shadow.w - vec3(0, 0, 0.01); 76 + // Transform the [-1, 1] NDC to [0, 1] to match the shadow texture data. 77 + light_intensity *= texture(shadow_map, sample_position * 0.5 + 0.5); 84 78 } else if (light_kind == ${LightKind.Point}) { 85 79 vec3 light_dir = light_positions[i].xyz - vert_position.xyz; 86 80 float light_dist = length(light_dir); ··· 112 106 } 113 107 114 108 vec3 emissive_rgb = GAMMA_DECODE(emissive_color.rgb) * emissive_color.a; 115 - vec3 shaded_rgb = light_acc * shadow_factor(vert_position, 0.5); 116 - frag_color= vec4(GAMMA_ENCODE(shaded_rgb + emissive_rgb), diffuse_color.a); 109 + frag_color = vec4(GAMMA_ENCODE(light_acc + emissive_rgb), diffuse_color.a); 117 110 } 118 111 `; 119 112