diff --git a/default-frag.glsl b/default-frag.glsl index 5b74bcc..4c5cd74 100644 --- a/default-frag.glsl +++ b/default-frag.glsl @@ -1,8 +1,28 @@ #version 450 -float ambient = 0.1; -vec3 light_dir = normalize(vec3(2, 2, -1)); -vec3 base_colour = vec3(1.0, 0.94, 0.8); +#define PI 3.14159265358979 + +float floor_light_coeff = 0.01; + +float sunny_sky_light_coeff = 0.5; +float cloudy_sky_light_coeff = 0.4; + +float sunny_sun_light_coeff = 0.8; +float cloudy_sun_light_coeff = 0.3; + +float sunny_shadow_pcf_radius = 1; +float cloudy_shadow_pcf_radius = 24; + +vec3 sunny_sky_colour = vec3(0.5, 0.7, 1.0); +vec3 cloudy_sky_colour = vec3(0.7, 0.7, 1.0); + +vec3 sunny_sun_colour = vec3(1.0, 0.95, 0.8); +vec3 cloudy_sun_colour = vec3(0.7, 0.7, 1.0); + +vec3 material_colour = vec3(1.0, 0.94, 0.8); + +float shadow_bias = 0.005; +float shadow_map_size = 4096; layout(location = 0) in vec3 frag_normal; layout(location = 1) in vec4 light_pos; @@ -11,18 +31,54 @@ layout(location = 0) out vec4 out_colour; layout(binding = 2) uniform sampler2DShadow shadow_map; +layout(push_constant) uniform params_t { + layout(offset = 128) vec3 sun_dir; + layout(offset = 140) float cloudiness; +} params; + void main() { - const float lambertian = max(dot(frag_normal, light_dir), 0.0); + const float sky_light_coeff + = mix(sunny_sky_light_coeff, cloudy_sky_light_coeff, + params.cloudiness); + const float sun_light_coeff + = mix(sunny_sun_light_coeff, cloudy_sun_light_coeff, + params.cloudiness); + const float shadow_pcf_radius + = mix(sunny_shadow_pcf_radius, cloudy_shadow_pcf_radius, + params.cloudiness); + const vec3 sky_colour + = mix(sunny_sky_colour, cloudy_sky_colour, + params.cloudiness); + const vec3 sun_colour + = mix(sunny_sun_colour, cloudy_sun_colour, + params.cloudiness); + + const float lambertian = max(dot(frag_normal, params.sun_dir), 0.0); - const float shadow_bias = 0.005; const vec3 proj_coords = light_pos.xyz / light_pos.w; const vec3 shadow_map_pos = vec3(0.5 * proj_coords.xy + 0.5, proj_coords.z); - float shadow = texture( - shadow_map, - vec3(shadow_map_pos.xy, shadow_map_pos.z - shadow_bias)); + float shadow = 0.0; + for (int x = -2; x <= 2; ++x) { + for (int y = -2; y < 3; ++y) { + const vec2 offset + = shadow_pcf_radius * vec2(x, y) / shadow_map_size; + shadow += texture( + shadow_map, + vec3(shadow_map_pos.xy + offset, + shadow_map_pos.z - shadow_bias)); + } + } + shadow /= 25; + const vec3 sun_light = shadow * lambertian * sun_colour; - const float intensity = min(shadow * lambertian + ambient, 1.0); - out_colour = vec4(intensity * base_colour, 1.0); + const vec3 floor_colour = floor_light_coeff * material_colour; + const float t = 0.5 * clamp(frag_normal.y, -1, 1) + 0.5; + const vec3 sky_light = mix(floor_colour, sky_colour, t); + + const vec3 lighting + = sun_light_coeff * sun_light + + sky_light_coeff * sky_light; + out_colour = vec4(lighting * material_colour, 1.0); } diff --git a/floor-vert.glsl b/floor-vert.glsl index 1c8f33c..a01f849 100644 --- a/floor-vert.glsl +++ b/floor-vert.glsl @@ -3,10 +3,10 @@ layout(location = 0) out vec3 frag_normal; layout(location = 1) out vec4 light_pos; -layout(push_constant) uniform push_t { +layout(push_constant) uniform vps_t { mat4 vp; mat4 light_vp; -} push; +} vps; vec2 vertices[4] = vec2[]( vec2(-1, -1), @@ -16,9 +16,9 @@ vec2 vertices[4] = vec2[]( ); void main() { - const vec2 pos = 3 * vertices[gl_VertexIndex]; + const vec2 pos = 4 * vertices[gl_VertexIndex]; const vec4 world_pos = vec4(pos.x, 0, pos.y, 1); - gl_Position = push.vp * world_pos; + gl_Position = vps.vp * world_pos; frag_normal = vec3(0, 1, 0); - light_pos = push.light_vp * world_pos; + light_pos = vps.light_vp * world_pos; } diff --git a/gpu.c b/gpu.c index 6ded68b..7bd3a5c 100644 --- a/gpu.c +++ b/gpu.c @@ -13,7 +13,7 @@ #define MAX_MODEL_COUNT 4 -#define SHADOW_IMG_SIZE 2048 +#define SHADOW_IMG_SIZE 4096 #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) @@ -24,6 +24,11 @@ typedef struct { mat4_t vp; mat4_t light_vp; +} vps_t; + +typedef struct { + vps_t vps; + light_params_t light; } push_t; static const char *layers[] = { "VK_LAYER_KHRONOS_validation" }; @@ -570,11 +575,18 @@ void gpu_init(gpu_ctx_t *ctx, GLFWwindow *window, const char *name) .pAttachments = &colour_blend_attatchment, }; - // Set up push constant config - const VkPushConstantRange push_constant_range = { - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, - .offset = 0, - .size = sizeof(push_t), + // Push constant configs + const VkPushConstantRange push_constants[] = { + { + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .offset = offsetof(push_t, vps), + .size = sizeof(vps_t), + }, + { + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = offsetof(push_t, light), + .size = sizeof(light_params_t), + }, }; // Load vertex and fragment shader modules @@ -638,8 +650,8 @@ void gpu_init(gpu_ctx_t *ctx, GLFWwindow *window, const char *name) .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, .pSetLayouts = &ctx->floor_desc_set_layout, - .pushConstantRangeCount = 1, - .pPushConstantRanges = &push_constant_range, + .pushConstantRangeCount = 2, + .pPushConstantRanges = push_constants, }; result = vkCreatePipelineLayout( ctx->dev, &floor_pipeline_layout_config, nullptr, @@ -753,8 +765,8 @@ void gpu_init(gpu_ctx_t *ctx, GLFWwindow *window, const char *name) .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, .pSetLayouts = &ctx->desc_set_layout, - .pushConstantRangeCount = 1, - .pPushConstantRanges = &push_constant_range, + .pushConstantRangeCount = 2, + .pPushConstantRanges = push_constants, }; result = vkCreatePipelineLayout( ctx->dev, &model_pipeline_layout_config, nullptr, @@ -1187,12 +1199,18 @@ void gpu_model_cleanup(const gpu_ctx_t *ctx, const gpu_model_t *model) } void gpu_render( - const gpu_ctx_t *ctx, mat4_t vp, mat4_t light_vp, size_t model_count, - const gpu_model_t *models, const mat4_t **bone_matrices, - const mat4_t *model_transforms) + const gpu_ctx_t *ctx, mat4_t vp, light_params_t light, + size_t model_count, const gpu_model_t *models, + const mat4_t **bone_matrices, const mat4_t *model_transforms) { VkResult result; + // Compute light VP + const mat4_t light_view = mat4_lookat( + light.sun_dir, (vec3_t) { 0, 0, 0 }, (vec3_t) { 0, 1, 0 }); + const mat4_t light_proj = mat4_orthographic(8, 8, -10, 10); + const mat4_t light_vp = mat4_mul(light_proj, light_view); + // Wait for previous frame to finish vkWaitForFences(ctx->dev, 1, &ctx->in_flight, VK_TRUE, UINT64_MAX); vkResetFences(ctx->dev, 1, &ctx->in_flight); @@ -1238,7 +1256,7 @@ void gpu_render( ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->shadow_pipeline); vkCmdPushConstants( ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, - 0, sizeof(push_t), &(push_t) { .vp = light_vp }); + 0, sizeof(vps_t), &(vps_t) { .vp = light_vp }); for (unsigned i = 0; i < model_count; ++i) { vkCmdBindVertexBuffers( ctx->cmd_buf, 0, 1, &models[i].vertex_buf, @@ -1290,14 +1308,18 @@ void gpu_render( vkCmdBeginRenderPass( ctx->cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); - const push_t push = { .vp = vp, .light_vp = light_vp }; + const vps_t vps = { .vp = vp, .light_vp = light_vp }; // Draw floor vkCmdBindPipeline( ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->floor_pipeline); vkCmdPushConstants( - ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, - 0, sizeof(push_t), &push); + ctx->cmd_buf, ctx->floor_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, + offsetof(push_t, vps), sizeof(vps_t), &vps); + vkCmdPushConstants( + ctx->cmd_buf, ctx->floor_pipeline_layout, + VK_SHADER_STAGE_FRAGMENT_BIT, offsetof(push_t, light), + sizeof(light_params_t), &light); vkCmdBindDescriptorSets( ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->floor_pipeline_layout, 0, 1, ctx->floor_desc_sets + curr_img, 0, @@ -1309,7 +1331,11 @@ void gpu_render( ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->model_pipeline); vkCmdPushConstants( ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, - 0, sizeof(push_t), &push); + offsetof(push_t, vps), sizeof(vps_t), &vps); + vkCmdPushConstants( + ctx->cmd_buf, ctx->model_pipeline_layout, + VK_SHADER_STAGE_FRAGMENT_BIT, offsetof(push_t, light), + sizeof(light_params_t), &light); for (unsigned i = 0; i < model_count; ++i) { vkCmdBindVertexBuffers( ctx->cmd_buf, 0, 1, &models[i].vertex_buf, diff --git a/gpu.h b/gpu.h index 7552219..fea7ed4 100644 --- a/gpu.h +++ b/gpu.h @@ -9,6 +9,11 @@ #define MAX_FRAMES_IN_FLIGHT 4 +typedef struct { + vec3_t sun_dir; + float cloudiness; +} light_params_t; + typedef struct { VkInstance inst; VkSurfaceKHR surface; @@ -77,8 +82,8 @@ void gpu_model_load( void gpu_model_cleanup(const gpu_ctx_t *ctx, const gpu_model_t *model); void gpu_render( - const gpu_ctx_t *ctx, mat4_t vp, mat4_t light_vp, size_t model_count, - const gpu_model_t *models, const mat4_t **bone_matrices, - const mat4_t *model_transforms); + const gpu_ctx_t *ctx, mat4_t vp, light_params_t light, + size_t model_count, const gpu_model_t *models, + const mat4_t **bone_matrices, const mat4_t *model_transforms); #endif diff --git a/main.c b/main.c index 03891bc..0296637 100644 --- a/main.c +++ b/main.c @@ -7,13 +7,15 @@ #include #include -#define W 800 -#define H 600 +#define W 1280 +#define H 720 #define DT (1'000'000 / 60) // Assume 60 Hz for now #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) +static light_params_t light_params; + static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) { @@ -28,6 +30,17 @@ key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) case GLFW_KEY_Q: glfwSetWindowShouldClose(window, GL_TRUE); break; + + case GLFW_KEY_UP: + light_params.cloudiness += 0.05; + if (light_params.cloudiness > 1) + light_params.cloudiness = 1; + break; + case GLFW_KEY_DOWN: + light_params.cloudiness -= 0.05; + if (light_params.cloudiness < 0) + light_params.cloudiness = 0; + break; } } @@ -57,16 +70,12 @@ int main(int argc, char *argv[]) gpu_model_load(&gpu, &model, gpu_models + 0); gpu_model_load(&gpu, &model, gpu_models + 1); - const vec3_t cam = { 0, 4, -8 }; + const vec3_t cam = { 2, 2, -8 }; const vec3_t tgt = { 0, 0, 0 }; const vec3_t up = { 0, 1, 0 }; - const vec3_t light = { 2, 2, -1 }; const mat4_t view = mat4_lookat(cam, tgt, up); - const mat4_t proj = mat4_perspective((float)W / H, PI / 8, 0.1, 100.0); + const mat4_t proj = mat4_perspective((float)W / H, PI / 12, 0.1, 100.0); const mat4_t vp = mat4_mul(proj, view); - const mat4_t light_view = mat4_lookat(light, tgt, up); - const mat4_t light_proj = mat4_orthographic(5, 5, 10); - const mat4_t light_vp = mat4_mul(light_proj, light_view); mat4_t transforms[NELEMS(gpu_models)] = { mat4_mul( MAT4_TRANSLATE(1, 0, 0), @@ -92,6 +101,9 @@ int main(int argc, char *argv[]) animation_state_t anim = animation_init(&model); animation_start(&anim, &model, 0); + light_params.sun_dir = vec3_norm((vec3_t) { 2, 4, -1 }); + light_params.cloudiness = 0.0; + while (!glfwWindowShouldClose(window)) { glfwPollEvents(); @@ -105,7 +117,7 @@ int main(int argc, char *argv[]) anim.bone_matrices, }; gpu_render( - &gpu, vp, light_vp, NELEMS(gpu_models), gpu_models, + &gpu, vp, light_params, NELEMS(gpu_models), gpu_models, bone_matrices, transforms); } diff --git a/model-vert.glsl b/model-vert.glsl index 8a1e68d..bc8a162 100644 --- a/model-vert.glsl +++ b/model-vert.glsl @@ -8,10 +8,10 @@ layout(location = 3) in vec4 weights; layout(location = 0) out vec3 frag_normal; layout(location = 1) out vec4 light_pos; -layout(push_constant) uniform push_t { +layout(push_constant) uniform vps_t { mat4 vp; mat4 light_vp; -} push; +} vps; layout(binding = 0) uniform transform_t { mat4 model; @@ -29,8 +29,8 @@ void main() { + weights.w * bones.matrices[bone_indices.w]; const vec4 world_pos = transform.model * skinning_matrix * vec4(position, 1.0); - gl_Position = push.vp * world_pos; + gl_Position = vps.vp * world_pos; frag_normal = transpose( inverse(mat3(transform.model * skinning_matrix))) * normal; - light_pos = push.light_vp * world_pos; + light_pos = vps.light_vp * world_pos; }