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 = 0, binding = 0) uniform sampler _EquirectangularSampler;
|
||||||
|
|
||||||
layout(set = 1, binding = 0) uniform texture2D _EquirectangularTexture;
|
layout(set = 0, binding = 1) uniform texture2D _EquirectangularTexture;
|
||||||
layout(set = 2, binding = 1, rgba16f) uniform writeonly restrict imageCube _CubemapImage;
|
layout(set = 0, binding = 2, rgba16f) uniform writeonly restrict imageCube _CubemapImage;
|
||||||
|
|
||||||
const float INV_PI = 0.31830987;
|
const float INV_PI = 0.31830987;
|
||||||
const float HALF_INV_PI = 0.15915494;
|
const float HALF_INV_PI = 0.15915494;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const Materials = @import("assets/Materials.zig");
|
|||||||
const Matrix4x4 = math.Matrix4x4;
|
const Matrix4x4 = math.Matrix4x4;
|
||||||
const Player = @import("Player.zig");
|
const Player = @import("Player.zig");
|
||||||
const Quaternion = math.Quaternion;
|
const Quaternion = math.Quaternion;
|
||||||
|
const Skybox = @import("engine/Skybox.zig");
|
||||||
const StagingBuffer = @import("engine/StagingBuffer.zig");
|
const StagingBuffer = @import("engine/StagingBuffer.zig");
|
||||||
const Swapchain = @import("engine/Swapchain.zig");
|
const Swapchain = @import("engine/Swapchain.zig");
|
||||||
const Textures = @import("assets/Textures.zig");
|
const Textures = @import("assets/Textures.zig");
|
||||||
@@ -48,6 +49,7 @@ blocks: Blocks,
|
|||||||
materials: Materials,
|
materials: Materials,
|
||||||
textures: Textures,
|
textures: Textures,
|
||||||
chunks: std.AutoHashMapUnmanaged([3]i16, Chunk),
|
chunks: std.AutoHashMapUnmanaged([3]i16, Chunk),
|
||||||
|
skybox: Skybox,
|
||||||
|
|
||||||
player: Player,
|
player: Player,
|
||||||
|
|
||||||
@@ -616,6 +618,9 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
|
|||||||
.elements = directional_lights_data,
|
.elements = directional_lights_data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var skybox = try Skybox.load("skybox.hdr", engine, 512, allocator);
|
||||||
|
errdefer skybox.deinit(engine);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.engine = engine,
|
.engine = engine,
|
||||||
@@ -642,6 +647,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
|
|||||||
.materials = materials,
|
.materials = materials,
|
||||||
.textures = textures,
|
.textures = textures,
|
||||||
.chunks = chunks,
|
.chunks = chunks,
|
||||||
|
.skybox = skybox,
|
||||||
|
|
||||||
.player = .init(player_position, 0, 0),
|
.player = .init(player_position, 0, 0),
|
||||||
};
|
};
|
||||||
@@ -658,6 +664,7 @@ pub fn deinit(self: *Game) void {
|
|||||||
chunk.deinit(self.engine, self.descriptor_pool);
|
chunk.deinit(self.engine, self.descriptor_pool);
|
||||||
}
|
}
|
||||||
self.chunks.deinit(self.allocator);
|
self.chunks.deinit(self.allocator);
|
||||||
|
self.skybox.deinit(self.engine);
|
||||||
|
|
||||||
self.global_uniforms.deinit(self.engine);
|
self.global_uniforms.deinit(self.engine);
|
||||||
self.global_uniforms_staging_buffer.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_vert_spv align(4) = @embedFile("shaders/main_vert.spv").*;
|
||||||
pub const main_frag_spv align(4) = @embedFile("shaders/main_frag.spv").*;
|
pub const main_frag_spv align(4) = @embedFile("shaders/main_frag.spv").*;
|
||||||
|
|||||||
Reference in New Issue
Block a user