Implement configurable lighting

This commit is contained in:
Camden Dixie O'Brien committed 2025-10-05 13:51:14 +01:00
1 parent 070758b6cc
commit b5bda150d2
6 files changed
+148 -49

No files matched your search

+66 -10
View File
@@ -1,8 +1,28 @@
#version 450 #version 450
float ambient = 0.1; #define PI 3.14159265358979
vec3 light_dir = normalize(vec3(2, 2, -1));
vec3 base_colour = vec3(1.0, 0.94, 0.8); 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 = 0) in vec3 frag_normal;
layout(location = 1) in vec4 light_pos; 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(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() 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 proj_coords = light_pos.xyz / light_pos.w;
const vec3 shadow_map_pos const vec3 shadow_map_pos
= vec3(0.5 * proj_coords.xy + 0.5, proj_coords.z); = vec3(0.5 * proj_coords.xy + 0.5, proj_coords.z);
float shadow = texture( float shadow = 0.0;
shadow_map, for (int x = -2; x <= 2; ++x) {
vec3(shadow_map_pos.xy, shadow_map_pos.z - shadow_bias)); 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); const vec3 floor_colour = floor_light_coeff * material_colour;
out_colour = vec4(intensity * base_colour, 1.0); 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);
} }
+5 -5
View File
@@ -3,10 +3,10 @@
layout(location = 0) out vec3 frag_normal; layout(location = 0) out vec3 frag_normal;
layout(location = 1) out vec4 light_pos; layout(location = 1) out vec4 light_pos;
layout(push_constant) uniform push_t { layout(push_constant) uniform vps_t {
mat4 vp; mat4 vp;
mat4 light_vp; mat4 light_vp;
} push; } vps;
vec2 vertices[4] = vec2[]( vec2 vertices[4] = vec2[](
vec2(-1, -1), vec2(-1, -1),
@@ -16,9 +16,9 @@ vec2 vertices[4] = vec2[](
); );
void main() { 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); 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); frag_normal = vec3(0, 1, 0);
light_pos = push.light_vp * world_pos; light_pos = vps.light_vp * world_pos;
} }
+44 -18
View File
@@ -13,7 +13,7 @@
#define MAX_MODEL_COUNT 4 #define MAX_MODEL_COUNT 4
#define SHADOW_IMG_SIZE 2048 #define SHADOW_IMG_SIZE 4096
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
@@ -24,6 +24,11 @@
typedef struct { typedef struct {
mat4_t vp; mat4_t vp;
mat4_t light_vp; mat4_t light_vp;
} vps_t;
typedef struct {
vps_t vps;
light_params_t light;
} push_t; } push_t;
static const char *layers[] = { "VK_LAYER_KHRONOS_validation" }; 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, .pAttachments = &colour_blend_attatchment,
}; };
// Set up push constant config // Push constant configs
const VkPushConstantRange push_constant_range = { const VkPushConstantRange push_constants[] = {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT, {
.offset = 0, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.size = sizeof(push_t), .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 // 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, .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1, .setLayoutCount = 1,
.pSetLayouts = &ctx->floor_desc_set_layout, .pSetLayouts = &ctx->floor_desc_set_layout,
.pushConstantRangeCount = 1, .pushConstantRangeCount = 2,
.pPushConstantRanges = &push_constant_range, .pPushConstantRanges = push_constants,
}; };
result = vkCreatePipelineLayout( result = vkCreatePipelineLayout(
ctx->dev, &floor_pipeline_layout_config, nullptr, 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, .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1, .setLayoutCount = 1,
.pSetLayouts = &ctx->desc_set_layout, .pSetLayouts = &ctx->desc_set_layout,
.pushConstantRangeCount = 1, .pushConstantRangeCount = 2,
.pPushConstantRanges = &push_constant_range, .pPushConstantRanges = push_constants,
}; };
result = vkCreatePipelineLayout( result = vkCreatePipelineLayout(
ctx->dev, &model_pipeline_layout_config, nullptr, 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( void gpu_render(
const gpu_ctx_t *ctx, mat4_t vp, mat4_t light_vp, size_t model_count, const gpu_ctx_t *ctx, mat4_t vp, light_params_t light,
const gpu_model_t *models, const mat4_t **bone_matrices, size_t model_count, const gpu_model_t *models,
const mat4_t *model_transforms) const mat4_t **bone_matrices, const mat4_t *model_transforms)
{ {
VkResult result; 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 // Wait for previous frame to finish
vkWaitForFences(ctx->dev, 1, &ctx->in_flight, VK_TRUE, UINT64_MAX); vkWaitForFences(ctx->dev, 1, &ctx->in_flight, VK_TRUE, UINT64_MAX);
vkResetFences(ctx->dev, 1, &ctx->in_flight); 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); ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->shadow_pipeline);
vkCmdPushConstants( vkCmdPushConstants(
ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 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) { for (unsigned i = 0; i < model_count; ++i) {
vkCmdBindVertexBuffers( vkCmdBindVertexBuffers(
ctx->cmd_buf, 0, 1, &models[i].vertex_buf, ctx->cmd_buf, 0, 1, &models[i].vertex_buf,
@@ -1290,14 +1308,18 @@ void gpu_render(
vkCmdBeginRenderPass( vkCmdBeginRenderPass(
ctx->cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); 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 // Draw floor
vkCmdBindPipeline( vkCmdBindPipeline(
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->floor_pipeline); ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->floor_pipeline);
vkCmdPushConstants( vkCmdPushConstants(
ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, ctx->cmd_buf, ctx->floor_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->floor_pipeline_layout,
VK_SHADER_STAGE_FRAGMENT_BIT, offsetof(push_t, light),
sizeof(light_params_t), &light);
vkCmdBindDescriptorSets( vkCmdBindDescriptorSets(
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS,
ctx->floor_pipeline_layout, 0, 1, ctx->floor_desc_sets + curr_img, 0, 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); ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->model_pipeline);
vkCmdPushConstants( vkCmdPushConstants(
ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 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) { for (unsigned i = 0; i < model_count; ++i) {
vkCmdBindVertexBuffers( vkCmdBindVertexBuffers(
ctx->cmd_buf, 0, 1, &models[i].vertex_buf, ctx->cmd_buf, 0, 1, &models[i].vertex_buf,
+8 -3
View File
@@ -9,6 +9,11 @@
#define MAX_FRAMES_IN_FLIGHT 4 #define MAX_FRAMES_IN_FLIGHT 4
typedef struct {
vec3_t sun_dir;
float cloudiness;
} light_params_t;
typedef struct { typedef struct {
VkInstance inst; VkInstance inst;
VkSurfaceKHR surface; 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_model_cleanup(const gpu_ctx_t *ctx, const gpu_model_t *model);
void gpu_render( void gpu_render(
const gpu_ctx_t *ctx, mat4_t vp, mat4_t light_vp, size_t model_count, const gpu_ctx_t *ctx, mat4_t vp, light_params_t light,
const gpu_model_t *models, const mat4_t **bone_matrices, size_t model_count, const gpu_model_t *models,
const mat4_t *model_transforms); const mat4_t **bone_matrices, const mat4_t *model_transforms);
#endif #endif
+21 -9
View File
@@ -7,13 +7,15 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#define W 800 #define W 1280
#define H 600 #define H 720
#define DT (1'000'000 / 60) // Assume 60 Hz for now #define DT (1'000'000 / 60) // Assume 60 Hz for now
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
static light_params_t light_params;
static void static void
key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) 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: case GLFW_KEY_Q:
glfwSetWindowShouldClose(window, GL_TRUE); glfwSetWindowShouldClose(window, GL_TRUE);
break; 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 + 0);
gpu_model_load(&gpu, &model, gpu_models + 1); 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 tgt = { 0, 0, 0 };
const vec3_t up = { 0, 1, 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 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 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_t transforms[NELEMS(gpu_models)] = {
mat4_mul( mat4_mul(
MAT4_TRANSLATE(1, 0, 0), MAT4_TRANSLATE(1, 0, 0),
@@ -92,6 +101,9 @@ int main(int argc, char *argv[])
animation_state_t anim = animation_init(&model); animation_state_t anim = animation_init(&model);
animation_start(&anim, &model, 0); animation_start(&anim, &model, 0);
light_params.sun_dir = vec3_norm((vec3_t) { 2, 4, -1 });
light_params.cloudiness = 0.0;
while (!glfwWindowShouldClose(window)) { while (!glfwWindowShouldClose(window)) {
glfwPollEvents(); glfwPollEvents();
@@ -105,7 +117,7 @@ int main(int argc, char *argv[])
anim.bone_matrices, anim.bone_matrices,
}; };
gpu_render( gpu_render(
&gpu, vp, light_vp, NELEMS(gpu_models), gpu_models, &gpu, vp, light_params, NELEMS(gpu_models), gpu_models,
bone_matrices, transforms); bone_matrices, transforms);
} }
+4 -4
View File
@@ -8,10 +8,10 @@ layout(location = 3) in vec4 weights;
layout(location = 0) out vec3 frag_normal; layout(location = 0) out vec3 frag_normal;
layout(location = 1) out vec4 light_pos; layout(location = 1) out vec4 light_pos;
layout(push_constant) uniform push_t { layout(push_constant) uniform vps_t {
mat4 vp; mat4 vp;
mat4 light_vp; mat4 light_vp;
} push; } vps;
layout(binding = 0) uniform transform_t { layout(binding = 0) uniform transform_t {
mat4 model; mat4 model;
@@ -29,8 +29,8 @@ void main() {
+ weights.w * bones.matrices[bone_indices.w]; + weights.w * bones.matrices[bone_indices.w];
const vec4 world_pos const vec4 world_pos
= transform.model * skinning_matrix * vec4(position, 1.0); = transform.model * skinning_matrix * vec4(position, 1.0);
gl_Position = push.vp * world_pos; gl_Position = vps.vp * world_pos;
frag_normal = transpose( frag_normal = transpose(
inverse(mat3(transform.model * skinning_matrix))) * normal; inverse(mat3(transform.model * skinning_matrix))) * normal;
light_pos = push.light_vp * world_pos; light_pos = vps.light_vp * world_pos;
} }