From 7c438d128428c2043ffa786644a46b426386f7aa Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Sun, 7 Dec 2025 23:27:42 +0100 Subject: [PATCH] Sketchy skybox --- .vscode/launch.json | 6 + assets/shaders/skybox.frag | 29 +++ assets/shaders/skybox.vert | 22 ++ compile_shaders.sh | 2 + src/Game.zig | 5 +- src/engine/Skybox.zig | 515 +++++++++++++++++++++++++++++++++---- src/shaders.zig | 2 + 7 files changed, 534 insertions(+), 47 deletions(-) create mode 100644 assets/shaders/skybox.frag create mode 100644 assets/shaders/skybox.vert diff --git a/.vscode/launch.json b/.vscode/launch.json index 81b47b8..314e30d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,5 +8,11 @@ "program": "${workspaceFolder}/zig-out/bin/voxel-game", "cwd": "${workspaceFolder}", }, + { + "name": "Attach", + "type": "lldb", + "request": "attach", + "program": "${workspaceFolder}/zig-out/bin/voxel-game", + }, ], } diff --git a/assets/shaders/skybox.frag b/assets/shaders/skybox.frag new file mode 100644 index 0000000..bd9bf02 --- /dev/null +++ b/assets/shaders/skybox.frag @@ -0,0 +1,29 @@ +#version 460 + +in Varyings { + layout(location = 0) vec3 texCoord; +} var; + +layout(set = 0, binding = 1) uniform sampler _Sampler; +layout(set = 0, binding = 2) uniform textureCube _Texture; + +layout(location = 0) out vec4 fragColor; + +vec3 toneMapAcesNarkowicz(vec3 color) { + const float A = 2.51; + const float B = 0.03; + const float C = 2.43; + const float D = 0.59; + const float E = 0.14; + return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0); +} + +void main() { + vec4 texel = texture(samplerCube(_Texture, _Sampler), var.texCoord); + vec3 outgoingRadiance = texel.rgb; + + vec3 toneMappedLinearColor = toneMapAcesNarkowicz(outgoingRadiance); + vec3 toneMappedSrgbColor = pow(toneMappedLinearColor, vec3(1.0 / 2.2)); + + fragColor = vec4(toneMappedSrgbColor, 1.0); +} diff --git a/assets/shaders/skybox.vert b/assets/shaders/skybox.vert new file mode 100644 index 0000000..87635f8 --- /dev/null +++ b/assets/shaders/skybox.vert @@ -0,0 +1,22 @@ +#version 460 +#extension GL_EXT_scalar_block_layout : require + +layout(location = 0) in vec3 directionWS; + +out Varyings { + layout(location = 0) vec3 texCoord; +} var; + +layout(set = 0, binding = 0, scalar) uniform GlobalUniforms { + mat4 matrixWStoVS; + mat4 matrixVStoCS; + vec3 ambientLight; +} _Global; + +void main() { + vec3 directionVS = (_Global.matrixWStoVS * vec4(directionWS, 0.0)).xyz; + vec4 directionCS = _Global.matrixVStoCS * vec4(directionVS, 0.0); + + gl_Position = vec4(directionCS.xy, 0.0, directionCS.w); + var.texCoord = -directionWS; +} diff --git a/compile_shaders.sh b/compile_shaders.sh index 0360f13..6f0dd06 100644 --- a/compile_shaders.sh +++ b/compile_shaders.sh @@ -3,3 +3,5 @@ glslc --target-env=vulkan1.2 assets/shaders/equirect_to_cube.comp -o src/shaders/equirect_to_cube_comp.spv glslc --target-env=vulkan1.2 assets/shaders/main.frag -o src/shaders/main_frag.spv glslc --target-env=vulkan1.2 assets/shaders/main.vert -o src/shaders/main_vert.spv +glslc --target-env=vulkan1.2 assets/shaders/skybox.frag -o src/shaders/skybox_frag.spv +glslc --target-env=vulkan1.2 assets/shaders/skybox.vert -o src/shaders/skybox_vert.spv diff --git a/src/Game.zig b/src/Game.zig index 264ff79..07b5db1 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -618,7 +618,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .elements = directional_lights_data, }); - var skybox = try Skybox.load("skybox.hdr", engine, 512, allocator); + var skybox = try Skybox.load("skybox.hdr", engine, 512, global_uniforms.buffer, swapchain.render_pass, allocator); errdefer skybox.deinit(engine); return .{ @@ -849,6 +849,9 @@ fn render(self: *Game) !void { while (it.next()) |chunk| { chunk.draw(self.pipeline_layout, command_buffer); } + + try self.skybox.bind(command_buffer, extent); + self.skybox.draw(command_buffer); } try command_buffer.endCommandBuffer(); diff --git a/src/engine/Skybox.zig b/src/engine/Skybox.zig index d2deb47..54ffa9e 100644 --- a/src/engine/Skybox.zig +++ b/src/engine/Skybox.zig @@ -7,12 +7,24 @@ const vk = @import("vulkan"); const CommandBuffer = @import("CommandBuffer.zig"); const Engine = @import("Engine.zig"); +const GenericBuffer = @import("GenericBuffer.zig").GenericBuffer; +const StagingBuffer = @import("StagingBuffer.zig"); image: vk.Image, image_view: vk.ImageView, device_memory: vk.DeviceMemory, -pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocator: std.mem.Allocator) !Skybox { +vertex_buffer: GenericBuffer(void, [3]f32), +index_buffer: shaders.IndexBuffer, +sampler: vk.Sampler, + +descriptor_set_layout: vk.DescriptorSetLayout, +descriptor_pool: vk.DescriptorPool, +descriptor_set: vk.DescriptorSet, +pipeline_layout: vk.PipelineLayout, +pipeline: vk.Pipeline, + +pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, global_uniforms_buffer: vk.Buffer, render_pass: vk.RenderPass, temp_allocator: std.mem.Allocator) !Skybox { std.log.debug("Loading skybox \"{s}\"...", .{filename}); // --- LOAD IMAGE FROM MEMORY ---------------------------------------------- @@ -29,7 +41,33 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato defer img.deinit(); std.debug.assert(img.num_components == 4); - // --- CREATE EQUIRECTANGULAR IMAGE AND COPY INTO IT ----------------------- + // --- SYNCHRONIZATION PRIMITIVES ------------------------------------------ + + const semaphore_transfer_transition = try engine.createSemaphore(); + defer engine.destroySemaphore(semaphore_transfer_transition); + + const semaphore_transition_compute = try engine.createSemaphore(); + defer engine.destroySemaphore(semaphore_transition_compute); + + const semaphore_compute_transition = try engine.createSemaphore(); + defer engine.destroySemaphore(semaphore_compute_transition); + + const fence = try engine.createFence(.{}); + defer engine.destroyFence(fence); + + // --- LOAD IMAGE INTO STAGING BUFFER -------------------------------------- + + var staging_buffer = try StagingBuffer.init(engine, .{ + .capacity = @intCast(img.data.len), + .target_queue = .compute, + }); + defer staging_buffer.deinit(engine); + + const staging_memory = try staging_buffer.map(engine); + @memcpy(staging_memory, img.data); + staging_buffer.unmap(engine); + + // --- CREATE EQUIRECTANGULAR IMAGE ---------------------------------------- const equirect_image = try engine.createImage(.{ .image_type = .@"2d", @@ -42,23 +80,21 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato .mip_levels = 1, .array_layers = 1, .samples = .{ .@"1_bit" = true }, - .tiling = .linear, + .tiling = .optimal, .usage = .{ .transfer_dst_bit = true, .sampled_bit = true, }, .queue_family_indices = &.{ engine.compute_queue.allocation.family, + engine.transfer_queue.allocation.family, }, - .initial_layout = .preinitialized, + .initial_layout = .undefined, }); defer engine.destroyImage(equirect_image); const equirect_memory_requirements = engine.getImageMemoryRequirements(equirect_image); - const equirect_device_memory = try engine.allocate(equirect_memory_requirements, .{ - .host_visible_bit = true, - .host_coherent_bit = true, - }); + const equirect_device_memory = try engine.allocate(equirect_memory_requirements, .{ .device_local_bit = true }); defer engine.freeMemory(equirect_device_memory); try engine.bindImageMemory(equirect_image, equirect_device_memory, 0); @@ -77,9 +113,94 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato }); defer engine.destroyImageView(equirect_image_view); - const data = try engine.mapMemory(equirect_device_memory, 0, equirect_memory_requirements.size, .{}); - @memcpy(data, img.data); - engine.unmapMemory(equirect_device_memory); + // --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY ------------------------- + + var transfer_command_buffer = try CommandBuffer.init(engine, .transfer, .transient); + defer transfer_command_buffer.deinit(engine); + + try transfer_command_buffer.beginCommandBuffer(); + transfer_command_buffer.pipelineBarrier(.{ + .src_stage_mask = .{ .top_of_pipe_bit = true }, + .dst_stage_mask = .{ .transfer_bit = true }, + .image_memory_barriers = &.{ + .{ + .src_access_mask = .{}, + .dst_access_mask = .{ .transfer_write_bit = true }, + .old_layout = .undefined, + .new_layout = .transfer_dst_optimal, + .src_queue_family_index = vk.QUEUE_FAMILY_IGNORED, + .dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED, + .image = equirect_image, + .subresource_range = .{ + .aspect_mask = .{ .color_bit = true }, + .base_mip_level = 0, + .level_count = 1, + .base_array_layer = 0, + .layer_count = 1, + }, + }, + }, + }); + transfer_command_buffer.copyBufferToImage( + staging_buffer.buffer, + equirect_image, + .transfer_dst_optimal, + &.{ + .{ + .buffer_offset = 0, + .buffer_row_length = img.width, + .buffer_image_height = img.height, + .image_subresource = .{ + .aspect_mask = .{ .color_bit = true }, + .mip_level = 0, + .base_array_layer = 0, + .layer_count = 1, + }, + .image_offset = .{ .x = 0, .y = 0, .z = 0 }, + .image_extent = .{ .width = img.width, .height = img.height, .depth = 1 }, + }, + }, + ); + try transfer_command_buffer.endCommandBuffer(); + + try transfer_command_buffer.submit(engine, .{ + .signal_semaphores = &.{semaphore_transfer_transition}, + }); + + // --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ------------------------------ + + var transition1_command_buffer = try CommandBuffer.init(engine, .graphics, .transient); + defer transition1_command_buffer.deinit(engine); + + try transition1_command_buffer.beginCommandBuffer(); + transition1_command_buffer.pipelineBarrier(.{ + .src_stage_mask = .{ .transfer_bit = true }, + .dst_stage_mask = .{ .compute_shader_bit = true }, + .image_memory_barriers = &.{ + .{ + .src_access_mask = .{ .transfer_write_bit = true }, + .dst_access_mask = .{ .shader_read_bit = true }, + .old_layout = .transfer_dst_optimal, + .new_layout = .shader_read_only_optimal, + .src_queue_family_index = vk.QUEUE_FAMILY_IGNORED, + .dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED, + .image = equirect_image, + .subresource_range = .{ + .aspect_mask = .{ .color_bit = true }, + .base_mip_level = 0, + .level_count = 1, + .base_array_layer = 0, + .layer_count = 1, + }, + }, + }, + }); + try transition1_command_buffer.endCommandBuffer(); + + try transition1_command_buffer.submit(engine, .{ + .wait_semaphores = &.{.{ .semaphore = semaphore_transfer_transition }}, + .signal_semaphores = &.{semaphore_transition_compute}, + }); // --- CREATE CUBEMAP IMAGE ------------------------------------------------ @@ -147,9 +268,9 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato .border_color = .float_transparent_black, .unnormalized_coordinates = .false, }); - defer engine.destroySampler(sampler); + errdefer engine.destroySampler(sampler); - const descriptor_set_layout = try engine.createDescriptorSetLayout(.{ + const compute_descriptor_set_layout = try engine.createDescriptorSetLayout(.{ .bindings = &.{ .{ .binding = 0, @@ -172,17 +293,17 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato }, }, }); - defer engine.destroyDescriptorSetLayout(descriptor_set_layout); + defer engine.destroyDescriptorSetLayout(compute_descriptor_set_layout); - const pipeline_layout = try engine.createPipelineLayout(.{ - .set_layouts = &.{descriptor_set_layout}, + const compute_pipeline_layout = try engine.createPipelineLayout(.{ + .set_layouts = &.{compute_descriptor_set_layout}, }); - defer engine.destroyPipelineLayout(pipeline_layout); + defer engine.destroyPipelineLayout(compute_pipeline_layout); const compute_shader = try engine.createShaderModule(.{ .code = &shaders.equirect_to_cube_comp_spv }); defer engine.destroyShaderModule(compute_shader); - var pipeline: vk.Pipeline = undefined; + var compute_pipeline: vk.Pipeline = undefined; _ = try engine.device.createComputePipelines(.null_handle, 1, &.{ .{ .stage = .{ @@ -190,14 +311,14 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato .module = compute_shader, .p_name = "main", }, - .layout = pipeline_layout, + .layout = compute_pipeline_layout, .base_pipeline_handle = .null_handle, .base_pipeline_index = -1, }, - }, &engine.vk_allocator.interface, @ptrCast(&pipeline)); - defer engine.destroyPipeline(pipeline); + }, &engine.vk_allocator.interface, @ptrCast(&compute_pipeline)); + defer engine.destroyPipeline(compute_pipeline); - const descriptor_pool = try engine.createDescriptorPool(.{ + const compute_descriptor_pool = try engine.createDescriptorPool(.{ .max_sets = 1, .pool_sizes = &.{ .{ @@ -214,17 +335,17 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato }, }, }); - defer engine.destroyDescriptorPool(descriptor_pool); + defer engine.destroyDescriptorPool(compute_descriptor_pool); - const descriptor_set = try engine.allocateDescriptorSet(.{ - .descriptor_pool = descriptor_pool, - .set_layout = descriptor_set_layout, + const compute_descriptor_set = try engine.allocateDescriptorSet(.{ + .descriptor_pool = compute_descriptor_pool, + .set_layout = compute_descriptor_set_layout, }); try engine.updateDescriptorSets(.{ .writes = &.{ .{ - .dst_set = descriptor_set, + .dst_set = compute_descriptor_set, .dst_binding = 1, .dst_array_element = 0, .descriptor_type = .sampled_image, @@ -239,7 +360,7 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato }, }, .{ - .dst_set = descriptor_set, + .dst_set = compute_descriptor_set, .dst_binding = 2, .dst_array_element = 0, .descriptor_type = .storage_image, @@ -258,14 +379,6 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato // --- COMMAND BUFFERS ----------------------------------------------------- - // 0. Synchronization primitives - - const semaphore = try engine.createSemaphore(); - defer engine.destroySemaphore(semaphore); - - const fence = try engine.createFence(.{}); - defer engine.destroyFence(fence); - // 1. Run compute shader var compute_command_buffer = try CommandBuffer.init(engine, .compute, .transient); @@ -310,22 +423,28 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato }, }, }); - compute_command_buffer.bindPipeline(.compute, pipeline); - compute_command_buffer.bindDescriptorSet(.compute, pipeline_layout, 0, descriptor_set, null); + compute_command_buffer.bindPipeline(.compute, compute_pipeline); + compute_command_buffer.bindDescriptorSet(.compute, compute_pipeline_layout, 0, compute_descriptor_set, null); compute_command_buffer.proxy.dispatch(@divExact(cube_size, 8), @divExact(cube_size, 8), 6); try compute_command_buffer.endCommandBuffer(); try compute_command_buffer.submit(engine, .{ - .signal_semaphores = &.{semaphore}, + .wait_semaphores = &.{ + .{ + .semaphore = semaphore_transition_compute, + .stage_flags = .{ .compute_shader_bit = true }, + }, + }, + .signal_semaphores = &.{semaphore_compute_transition}, }); // 2. Transition cubemap - var transition_command_buffer = try CommandBuffer.init(engine, .graphics, .transient); - defer transition_command_buffer.deinit(engine); + var transition2_command_buffer = try CommandBuffer.init(engine, .graphics, .transient); + defer transition2_command_buffer.deinit(engine); - try transition_command_buffer.beginCommandBuffer(); - transition_command_buffer.pipelineBarrier(.{ + try transition2_command_buffer.beginCommandBuffer(); + transition2_command_buffer.pipelineBarrier(.{ .src_stage_mask = .{ .compute_shader_bit = true }, .dst_stage_mask = .{ .top_of_pipe_bit = true }, .image_memory_barriers = &.{ @@ -347,10 +466,10 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato }, }, }); - try transition_command_buffer.endCommandBuffer(); + try transition2_command_buffer.endCommandBuffer(); - try transition_command_buffer.submit(engine, .{ - .wait_semaphores = &.{.{ .semaphore = semaphore }}, + try transition2_command_buffer.submit(engine, .{ + .wait_semaphores = &.{.{ .semaphore = semaphore_compute_transition }}, .fence = fence, }); @@ -358,15 +477,319 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, temp_allocato try engine.waitForFence(fence); + // --- SKYBOX PIPELINE ----------------------------------------------------- + + var vertex_buffer = try GenericBuffer(void, [3]f32).init(engine, .{ + .usage = .vertex, + .target_queue = .graphics, + .array_capacity = 8, + }); + errdefer vertex_buffer.deinit(engine); + + // 6━━━━7 + // ╱│ ╱┃ + // 4━┿━━5 ┃ Z + // ┃ │ ┃ ┃ │ + // ┃ 2──╂─3 │ Y + // ┃╱ ┃╱ │╱ + // 0━━━━1 O────X + + try vertex_buffer.write(engine, .{ + .elements = &.{ + .{ -1, -1, -1 }, + .{ 1, -1, -1 }, + .{ -1, 1, -1 }, + .{ 1, 1, -1 }, + .{ -1, -1, 1 }, + .{ 1, -1, 1 }, + .{ -1, 1, 1 }, + .{ 1, 1, 1 }, + }, + }); + + var index_buffer = try GenericBuffer(void, u16).init(engine, .{ + .usage = .index, + .target_queue = .graphics, + .array_capacity = 36, + }); + errdefer index_buffer.deinit(engine); + + try index_buffer.write(engine, .{ + .elements = &.{ + // Positive X + 3, 1, 7, + 7, 1, 5, + // Negative X + 0, 2, 4, + 4, 2, 6, + // Positive Y + 2, 3, 6, + 6, 3, 7, + // Negative Y + 1, 0, 5, + 5, 0, 4, + // Positive Z + 6, 7, 4, + 4, 7, 5, + // Negative Z + 0, 1, 2, + 2, 1, 3, + }, + }); + + const descriptor_set_layout = try engine.createDescriptorSetLayout(.{ + .bindings = &.{ + .{ + .binding = 0, + .descriptor_type = .uniform_buffer, + .descriptor_count = 1, + .stage_flags = .{ .vertex_bit = true }, + }, + .{ + .binding = 1, + .descriptor_type = .sampler, + .descriptor_count = 1, + .stage_flags = .{ .fragment_bit = true }, + .immutable_samplers = &.{sampler}, + }, + .{ + .binding = 2, + .descriptor_type = .sampled_image, + .descriptor_count = 1, + .stage_flags = .{ .fragment_bit = true }, + }, + }, + }); + errdefer engine.destroyDescriptorSetLayout(descriptor_set_layout); + + const pipeline_layout = try engine.createPipelineLayout(.{ + .set_layouts = &.{descriptor_set_layout}, + }); + errdefer engine.destroyPipelineLayout(pipeline_layout); + + const descriptor_pool = try engine.createDescriptorPool(.{ + .max_sets = 1, + .pool_sizes = &.{ + .{ + .type = .uniform_buffer, + .descriptor_count = 1, + }, + .{ + .type = .sampler, + .descriptor_count = 1, + }, + .{ + .type = .sampled_image, + .descriptor_count = 1, + }, + }, + }); + errdefer engine.destroyDescriptorPool(compute_descriptor_pool); + + const descriptor_set = try engine.allocateDescriptorSet(.{ + .descriptor_pool = descriptor_pool, + .set_layout = descriptor_set_layout, + }); + + try engine.updateDescriptorSets(.{ + .writes = &.{ + .{ + .dst_set = descriptor_set, + .dst_binding = 0, + .dst_array_element = 0, + .descriptor_type = .uniform_buffer, + .descriptor_infos = .{ + .buffer = &.{ + .{ + .buffer = global_uniforms_buffer, + .offset = 0, + .range = vk.WHOLE_SIZE, + }, + }, + }, + }, + .{ + .dst_set = descriptor_set, + .dst_binding = 2, + .dst_array_element = 0, + .descriptor_type = .sampled_image, + .descriptor_infos = .{ + .image = &.{ + .{ + .sampler = .null_handle, + .image_view = cubemap_image_view, + .image_layout = .shader_read_only_optimal, + }, + }, + }, + }, + }, + }); + + const vertex_shader = try engine.createShaderModule(.{ .code = &shaders.skybox_vert_spv }); + defer engine.destroyShaderModule(vertex_shader); + + const fragment_shader = try engine.createShaderModule(.{ .code = &shaders.skybox_frag_spv }); + defer engine.destroyShaderModule(fragment_shader); + + const pipeline = try engine.createGraphicsPipeline(.{ + .stages = &.{ + .{ + .stage = .{ .vertex_bit = true }, + .module = vertex_shader, + .name = "main", + }, + .{ + .stage = .{ .fragment_bit = true }, + .module = fragment_shader, + .name = "main", + }, + }, + .vertex_input_state = .{ + .vertex_binding_descriptions = &.{ + .{ + .binding = 0, + .stride = @sizeOf([3]f32), + .input_rate = .vertex, + }, + }, + .vertex_attribute_descriptions = &.{ + .{ + .location = 0, + .binding = 0, + .format = .r32g32b32_sfloat, + .offset = 0, + }, + }, + }, + .input_assembly_state = .{ + .topology = .triangle_list, + .primitive_restart_enable = .false, + }, + .viewport_state = .{ + .viewports = &.{undefined}, // dynamic + .scissors = &.{undefined}, // dynamic + }, + .rasterization_state = .{ + .depth_clamp_enable = .false, + .rasterizer_discard_enable = .false, + .polygon_mode = .fill, + .cull_mode = .{ .back_bit = true }, + .front_face = .counter_clockwise, + .depth_bias_enable = .false, + .depth_bias_constant_factor = 0, + .depth_bias_clamp = 0, + .depth_bias_slope_factor = 0, + .line_width = 1, + }, + .multisample_state = .{ + .rasterization_samples = .{ .@"1_bit" = true }, + .sample_shading_enable = .false, + .min_sample_shading = 1, + .alpha_to_coverage_enable = .false, + .alpha_to_one_enable = .false, + }, + .depth_stencil_state = .{ + .depth_test_enable = .true, + .depth_write_enable = .false, + .depth_compare_op = .equal, + .depth_bounds_test_enable = .false, + .stencil_test_enable = .false, + .front = undefined, + .back = undefined, + .min_depth_bounds = 0, + .max_depth_bounds = 1, + }, + .color_blend_state = .{ + .logic_op_enable = .false, + .logic_op = .copy, + .attachments = &.{ + .{ + .blend_enable = .false, + .src_color_blend_factor = .one, + .dst_color_blend_factor = .zero, + .color_blend_op = .add, + .src_alpha_blend_factor = .one, + .dst_alpha_blend_factor = .zero, + .alpha_blend_op = .add, + .color_write_mask = .{ + .r_bit = true, + .g_bit = true, + .b_bit = true, + .a_bit = true, + }, + }, + }, + .blend_constants = .{ 0, 0, 0, 0 }, + }, + .dynamic_state = .{ + .dynamic_states = &.{ .viewport, .scissor }, + }, + .layout = pipeline_layout, + .render_pass = render_pass, + .subpass = 0, + }); + return .{ .image = cubemap_image, .image_view = cubemap_image_view, .device_memory = cubemap_device_memory, + + .vertex_buffer = vertex_buffer, + .index_buffer = index_buffer, + .sampler = sampler, + + .descriptor_set_layout = descriptor_set_layout, + .descriptor_pool = descriptor_pool, + .descriptor_set = descriptor_set, + .pipeline_layout = pipeline_layout, + .pipeline = pipeline, }; } pub fn deinit(self: *Skybox, engine: *Engine) void { + engine.destroyPipeline(self.pipeline); + engine.destroyPipelineLayout(self.pipeline_layout); + engine.destroyDescriptorPool(self.descriptor_pool); + engine.destroyDescriptorSetLayout(self.descriptor_set_layout); + + engine.destroySampler(self.sampler); + self.index_buffer.deinit(engine); + self.vertex_buffer.deinit(engine); + engine.destroyImageView(self.image_view); engine.freeMemory(self.device_memory); engine.destroyImage(self.image); } + +pub fn bind(self: *const Skybox, command_buffer: CommandBuffer, extent: vk.Extent2D) !void { + command_buffer.setViewport(0, &.{ + .{ + .x = 0, + .y = 0, + .width = @floatFromInt(extent.width), + .height = @floatFromInt(extent.height), + .min_depth = 0, + .max_depth = 1, + }, + }); + command_buffer.setScissor(0, &.{ + .{ + .offset = .{ .x = 0, .y = 0 }, + .extent = extent, + }, + }); + command_buffer.bindPipeline(.graphics, self.pipeline); + try command_buffer.bindVertexBuffers(0, &.{ + .{ + .buffer = self.vertex_buffer.buffer, + .offset = 0, + }, + }); + command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16); +} + +pub fn draw(self: *const Skybox, command_buffer: CommandBuffer) void { + command_buffer.bindDescriptorSet(.graphics, self.pipeline_layout, 0, self.descriptor_set, null); + command_buffer.drawIndexed(.{ .index_count = 36 }); +} diff --git a/src/shaders.zig b/src/shaders.zig index 09b2cd2..c8c6af4 100644 --- a/src/shaders.zig +++ b/src/shaders.zig @@ -105,3 +105,5 @@ pub const ObjectUniforms = extern struct { pub const equirect_to_cube_comp_spv align(4) = @embedFile("shaders/equirect_to_cube_comp.spv").*; pub const main_vert_spv align(4) = @embedFile("shaders/main_vert.spv").*; pub const main_frag_spv align(4) = @embedFile("shaders/main_frag.spv").*; +pub const skybox_vert_spv align(4) = @embedFile("shaders/skybox_vert.spv").*; +pub const skybox_frag_spv align(4) = @embedFile("shaders/skybox_frag.spv").*;