Load and convert skybox to cubemap
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler _EquirectangularSampler;
|
||||
|
||||
layout(set = 1, binding = 0) uniform texture2D _EquirectangularTexture;
|
||||
layout(set = 2, binding = 1, rgba16f) uniform writeonly restrict imageCube _CubemapImage;
|
||||
layout(set = 0, binding = 1) uniform texture2D _EquirectangularTexture;
|
||||
layout(set = 0, binding = 2, rgba16f) uniform writeonly restrict imageCube _CubemapImage;
|
||||
|
||||
const float INV_PI = 0.31830987;
|
||||
const float HALF_INV_PI = 0.15915494;
|
||||
|
||||
@@ -16,6 +16,7 @@ const Materials = @import("assets/Materials.zig");
|
||||
const Matrix4x4 = math.Matrix4x4;
|
||||
const Player = @import("Player.zig");
|
||||
const Quaternion = math.Quaternion;
|
||||
const Skybox = @import("engine/Skybox.zig");
|
||||
const StagingBuffer = @import("engine/StagingBuffer.zig");
|
||||
const Swapchain = @import("engine/Swapchain.zig");
|
||||
const Textures = @import("assets/Textures.zig");
|
||||
@@ -48,6 +49,7 @@ blocks: Blocks,
|
||||
materials: Materials,
|
||||
textures: Textures,
|
||||
chunks: std.AutoHashMapUnmanaged([3]i16, Chunk),
|
||||
skybox: Skybox,
|
||||
|
||||
player: Player,
|
||||
|
||||
@@ -616,6 +618,9 @@ 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);
|
||||
errdefer skybox.deinit(engine);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.engine = engine,
|
||||
@@ -642,6 +647,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
|
||||
.materials = materials,
|
||||
.textures = textures,
|
||||
.chunks = chunks,
|
||||
.skybox = skybox,
|
||||
|
||||
.player = .init(player_position, 0, 0),
|
||||
};
|
||||
@@ -658,6 +664,7 @@ pub fn deinit(self: *Game) void {
|
||||
chunk.deinit(self.engine, self.descriptor_pool);
|
||||
}
|
||||
self.chunks.deinit(self.allocator);
|
||||
self.skybox.deinit(self.engine);
|
||||
|
||||
self.global_uniforms.deinit(self.engine);
|
||||
self.global_uniforms_staging_buffer.deinit(self.engine);
|
||||
|
||||
372
src/engine/Skybox.zig
Normal file
372
src/engine/Skybox.zig
Normal file
@@ -0,0 +1,372 @@
|
||||
const Skybox = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const shaders = @import("../shaders.zig");
|
||||
const stbi = @import("zstbi");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
const CommandBuffer = @import("CommandBuffer.zig");
|
||||
const Engine = @import("Engine.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 {
|
||||
std.log.debug("Loading skybox \"{s}\"...", .{filename});
|
||||
|
||||
// --- LOAD IMAGE FROM MEMORY ----------------------------------------------
|
||||
|
||||
const cwd = std.fs.cwd();
|
||||
|
||||
var dir = try cwd.openDir("assets", .{});
|
||||
defer dir.close();
|
||||
|
||||
const file_buf = try dir.readFileAlloc(temp_allocator, filename, std.math.maxInt(usize));
|
||||
defer temp_allocator.free(file_buf);
|
||||
|
||||
var img = try stbi.Image.loadFromMemory(file_buf, 4);
|
||||
defer img.deinit();
|
||||
std.debug.assert(img.num_components == 4);
|
||||
|
||||
// --- CREATE EQUIRECTANGULAR IMAGE AND COPY INTO IT -----------------------
|
||||
|
||||
const equirect_image = try engine.createImage(.{
|
||||
.image_type = .@"2d",
|
||||
.format = .r16g16b16a16_sfloat,
|
||||
.extent = .{
|
||||
.width = img.width,
|
||||
.height = img.height,
|
||||
.depth = 1,
|
||||
},
|
||||
.mip_levels = 1,
|
||||
.array_layers = 1,
|
||||
.samples = .{ .@"1_bit" = true },
|
||||
.tiling = .linear,
|
||||
.usage = .{
|
||||
.transfer_dst_bit = true,
|
||||
.sampled_bit = true,
|
||||
},
|
||||
.queue_family_indices = &.{
|
||||
engine.compute_queue.allocation.family,
|
||||
},
|
||||
.initial_layout = .preinitialized,
|
||||
});
|
||||
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,
|
||||
});
|
||||
defer engine.freeMemory(equirect_device_memory);
|
||||
|
||||
try engine.bindImageMemory(equirect_image, equirect_device_memory, 0);
|
||||
|
||||
const equirect_image_view = try engine.createImageView(.{
|
||||
.image = equirect_image,
|
||||
.view_type = .@"2d",
|
||||
.format = .r16g16b16a16_sfloat,
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
});
|
||||
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);
|
||||
|
||||
// --- CREATE CUBEMAP IMAGE ------------------------------------------------
|
||||
|
||||
const cubemap_image = try engine.createImage(.{
|
||||
.flags = .{ .cube_compatible_bit = true },
|
||||
.image_type = .@"2d",
|
||||
.format = .r16g16b16a16_sfloat,
|
||||
.extent = .{
|
||||
.width = cube_size,
|
||||
.height = cube_size,
|
||||
.depth = 1,
|
||||
},
|
||||
.mip_levels = 1,
|
||||
.array_layers = 6,
|
||||
.samples = .{ .@"1_bit" = true },
|
||||
.tiling = .optimal,
|
||||
.usage = .{
|
||||
.sampled_bit = true,
|
||||
.storage_bit = true,
|
||||
},
|
||||
.queue_family_indices = &.{
|
||||
engine.graphics_queue.allocation.family,
|
||||
engine.compute_queue.allocation.family,
|
||||
},
|
||||
.initial_layout = .undefined,
|
||||
});
|
||||
errdefer engine.destroyImage(cubemap_image);
|
||||
|
||||
const cubemap_memory_requirements = engine.getImageMemoryRequirements(cubemap_image);
|
||||
const cubemap_device_memory = try engine.allocate(cubemap_memory_requirements, .{ .device_local_bit = true });
|
||||
errdefer engine.freeMemory(cubemap_device_memory);
|
||||
|
||||
try engine.bindImageMemory(cubemap_image, cubemap_device_memory, 0);
|
||||
|
||||
const cubemap_image_view = try engine.createImageView(.{
|
||||
.image = cubemap_image,
|
||||
.view_type = .cube,
|
||||
.format = .r16g16b16a16_sfloat,
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 6,
|
||||
},
|
||||
});
|
||||
errdefer engine.destroyImageView(cubemap_image_view);
|
||||
|
||||
// --- PIPELINE AND DESCRIPTORS --------------------------------------------
|
||||
|
||||
const sampler = try engine.createSampler(.{
|
||||
.mag_filter = .linear,
|
||||
.min_filter = .linear,
|
||||
.mipmap_mode = .linear,
|
||||
.address_mode_u = .repeat,
|
||||
.address_mode_v = .repeat,
|
||||
.address_mode_w = .repeat,
|
||||
.mip_lod_bias = 0,
|
||||
.anisotropy_enable = .false,
|
||||
.max_anisotropy = 0,
|
||||
.compare_enable = .false,
|
||||
.compare_op = .always,
|
||||
.min_lod = 0,
|
||||
.max_lod = vk.LOD_CLAMP_NONE,
|
||||
.border_color = .float_transparent_black,
|
||||
.unnormalized_coordinates = .false,
|
||||
});
|
||||
defer engine.destroySampler(sampler);
|
||||
|
||||
const descriptor_set_layout = try engine.createDescriptorSetLayout(.{
|
||||
.bindings = &.{
|
||||
.{
|
||||
.binding = 0,
|
||||
.descriptor_type = .sampler,
|
||||
.descriptor_count = 1,
|
||||
.stage_flags = .{ .compute_bit = true },
|
||||
.immutable_samplers = &.{sampler},
|
||||
},
|
||||
.{
|
||||
.binding = 1,
|
||||
.descriptor_type = .sampled_image,
|
||||
.descriptor_count = 1,
|
||||
.stage_flags = .{ .compute_bit = true },
|
||||
},
|
||||
.{
|
||||
.binding = 2,
|
||||
.descriptor_type = .storage_image,
|
||||
.descriptor_count = 1,
|
||||
.stage_flags = .{ .compute_bit = true },
|
||||
},
|
||||
},
|
||||
});
|
||||
defer engine.destroyDescriptorSetLayout(descriptor_set_layout);
|
||||
|
||||
const pipeline_layout = try engine.createPipelineLayout(.{
|
||||
.set_layouts = &.{descriptor_set_layout},
|
||||
});
|
||||
defer engine.destroyPipelineLayout(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;
|
||||
_ = try engine.device.createComputePipelines(.null_handle, 1, &.{
|
||||
.{
|
||||
.stage = .{
|
||||
.stage = .{ .compute_bit = true },
|
||||
.module = compute_shader,
|
||||
.p_name = "main",
|
||||
},
|
||||
.layout = pipeline_layout,
|
||||
.base_pipeline_handle = .null_handle,
|
||||
.base_pipeline_index = -1,
|
||||
},
|
||||
}, &engine.vk_allocator.interface, @ptrCast(&pipeline));
|
||||
defer engine.destroyPipeline(pipeline);
|
||||
|
||||
const descriptor_pool = try engine.createDescriptorPool(.{
|
||||
.max_sets = 1,
|
||||
.pool_sizes = &.{
|
||||
.{
|
||||
.type = .sampler,
|
||||
.descriptor_count = 1,
|
||||
},
|
||||
.{
|
||||
.type = .sampled_image,
|
||||
.descriptor_count = 1,
|
||||
},
|
||||
.{
|
||||
.type = .storage_image,
|
||||
.descriptor_count = 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
defer engine.destroyDescriptorPool(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 = 1,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_type = .sampled_image,
|
||||
.descriptor_infos = .{
|
||||
.image = &.{
|
||||
.{
|
||||
.sampler = .null_handle,
|
||||
.image_view = equirect_image_view,
|
||||
.image_layout = .shader_read_only_optimal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
.{
|
||||
.dst_set = descriptor_set,
|
||||
.dst_binding = 2,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_type = .storage_image,
|
||||
.descriptor_infos = .{
|
||||
.image = &.{
|
||||
.{
|
||||
.sampler = .null_handle,
|
||||
.image_view = cubemap_image_view,
|
||||
.image_layout = .general,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// --- 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);
|
||||
defer compute_command_buffer.deinit(engine);
|
||||
|
||||
try compute_command_buffer.beginCommandBuffer();
|
||||
compute_command_buffer.pipelineBarrier(.{
|
||||
.src_stage_mask = .{ .top_of_pipe_bit = true },
|
||||
.dst_stage_mask = .{ .compute_shader_bit = true },
|
||||
.image_memory_barriers = &.{
|
||||
.{
|
||||
.src_access_mask = .{},
|
||||
.dst_access_mask = .{ .shader_read_bit = true },
|
||||
.old_layout = .preinitialized,
|
||||
.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,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.src_access_mask = .{},
|
||||
.dst_access_mask = .{ .shader_write_bit = true },
|
||||
.old_layout = .undefined,
|
||||
.new_layout = .general,
|
||||
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
|
||||
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
|
||||
.image = cubemap_image,
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
compute_command_buffer.bindPipeline(.compute, pipeline);
|
||||
compute_command_buffer.bindDescriptorSet(.compute, pipeline_layout, 0, 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},
|
||||
});
|
||||
|
||||
// 2. Transition cubemap
|
||||
|
||||
var transition_command_buffer = try CommandBuffer.init(engine, .graphics, .transient);
|
||||
defer transition_command_buffer.deinit(engine);
|
||||
|
||||
try transition_command_buffer.beginCommandBuffer();
|
||||
transition_command_buffer.pipelineBarrier(.{
|
||||
.src_stage_mask = .{ .compute_shader_bit = true },
|
||||
.dst_stage_mask = .{ .top_of_pipe_bit = true },
|
||||
.image_memory_barriers = &.{
|
||||
.{
|
||||
.src_access_mask = .{ .shader_write_bit = true },
|
||||
.dst_access_mask = .{ .shader_read_bit = true },
|
||||
.old_layout = .general,
|
||||
.new_layout = .shader_read_only_optimal,
|
||||
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
|
||||
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
|
||||
.image = cubemap_image,
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
try transition_command_buffer.endCommandBuffer();
|
||||
|
||||
try transition_command_buffer.submit(engine, .{
|
||||
.wait_semaphores = &.{.{ .semaphore = semaphore }},
|
||||
.fence = fence,
|
||||
});
|
||||
|
||||
// 3. Synchronize
|
||||
|
||||
try engine.waitForFence(fence);
|
||||
|
||||
return .{
|
||||
.image = cubemap_image,
|
||||
.image_view = cubemap_image_view,
|
||||
.device_memory = cubemap_device_memory,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Skybox, engine: *Engine) void {
|
||||
engine.destroyImageView(self.image_view);
|
||||
engine.freeMemory(self.device_memory);
|
||||
engine.destroyImage(self.image);
|
||||
}
|
||||
@@ -102,5 +102,6 @@ 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").*;
|
||||
|
||||
Reference in New Issue
Block a user