diff --git a/res/dpr/utility/light_library.glsl b/res/dpr/utility/light_library.glsl index 1e5e64604110602ed7b41cb79494027149dc4d3e..4e40506eafc62c74c711d14ae669959ef5fa1184 100644 --- a/res/dpr/utility/light_library.glsl +++ b/res/dpr/utility/light_library.glsl @@ -282,7 +282,8 @@ vec3 compute_tone_mapping(vec3 lighting) vec3 apply_local_lighting(vec3 view_position, vec3 surface_position, vec3 surface_normal, Material material) { - vec3 lighting = material.base_color * light_parameter.ambient; + vec3 lighting = material.base_color * light_parameter.ambient; //where ambient is in watts/meter^2 *str + lighting += material.emissive; //where emissive is in watts/meter^2 *str for (uint light = 0; light < light_parameter.count; light++) { diff --git a/res/dpr/utility/material_library.glsl b/res/dpr/utility/material_library.glsl index 8afb064b84fb21a75c3253dd4257a48115662c9d..1ba7d28330253ebc73a045a032355515b6ab79f9 100644 --- a/res/dpr/utility/material_library.glsl +++ b/res/dpr/utility/material_library.glsl @@ -38,11 +38,13 @@ layout(set = MATERIAL_DESCRIPTOR_SET, binding = 0) uniform sampler2D sampler_color_opacity; layout(set = MATERIAL_DESCRIPTOR_SET, binding = 1) uniform sampler2D sampler_material_factor; layout(set = MATERIAL_DESCRIPTOR_SET, binding = 2) uniform sampler2D sampler_normal; +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 3) uniform sampler2D sampler_emissive; Material lookup_material(vec2 uv_coord) { vec4 color_opacity = texture(sampler_color_opacity, uv_coord); vec4 material_factor = texture(sampler_material_factor, uv_coord); + vec3 emissive_color = texture(sampler_emissive, uv_coord).xyz; Material material; material.base_color = color_opacity.xyz; @@ -50,6 +52,7 @@ Material lookup_material(vec2 uv_coord) material.occlusion = material_factor.x; material.roughness = material_factor.y; material.metallic = material_factor.z; + material.emissive = emissive_color; return material; } diff --git a/res/dpr/utility/material_struct.glsl b/res/dpr/utility/material_struct.glsl index 16a048144a424ea290435be96ab3ca2e6922661b..4d6d7a73867bd254d7ec89310c2f283978d504a4 100644 --- a/res/dpr/utility/material_struct.glsl +++ b/res/dpr/utility/material_struct.glsl @@ -9,6 +9,8 @@ struct Material float occlusion; float roughness; float metallic; + + vec3 emissive; }; #endif \ No newline at end of file diff --git a/src/scene.cpp b/src/scene.cpp index f6bae6881d2f79783e854ae2b9a918eb1e2c39b2..561ba946cab001ce054b7ebdec023087bc24e2de 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -51,6 +51,17 @@ bool Scene::create(const SceneConfig& config, lava::device_ptr device, lava::sta this->controller_right_alignment = config.controller_right_alignment; } + if (!config.sky_sphere_file.empty()) + { + //NOTE: Need to compute scene bounds for sky-sphere + this->reset_transforms(); + + if (!this->load_sky_sphere(config.sky_sphere_file, config.sky_sphere_rings, config.sky_sphere_segments, device, staging)) + { + return false; + } + } + if (this->lights.empty()) { this->create_default_light(); @@ -76,6 +87,7 @@ bool Scene::create(const SceneConfig& config, lava::device_ptr device, lava::sta return false; } + //NOTE: Recompute all transfromations for final scene bounds and matrices this->reset_transforms(); return true; @@ -467,13 +479,15 @@ void Scene::set_vertex_input_only_position(lava::graphics_pipeline* pipeline) void Scene::create_dummy_textures(lava::device_ptr device, lava::staging& staging) { - this->dummy_diffuse_texture = create_default_texture(device, { 1, 1 }, glm::vec3(1.0, 1.0, 1.0), 1.0); //Diffuse: (1.0, 1.0, 1.0) Opacity: 1.0 - this->dummy_specular_texture = create_default_texture(device, { 1, 1 }, glm::vec3(0.0, 1.0, 0.0), 0.0); //Occlusion: 0.0 Roughness: 1.0 Metallic: 0.0 - this->dummy_normal_texture = create_default_texture(device, { 1, 1 }, glm::vec3(0.5, 0.5, 0), 0.0); //Normal: (0.5, 0.5) -> Vertex Normal + this->dummy_diffuse_texture = lava::create_default_texture(device, { 1, 1 }, glm::vec3(1.0, 1.0, 1.0), 1.0); //Diffuse: (1.0, 1.0, 1.0) Opacity: 1.0 + this->dummy_specular_texture = lava::create_default_texture(device, { 1, 1 }, glm::vec3(0.0, 1.0, 0.0), 0.0); //Occlusion: 0.0 Roughness: 1.0 Metallic: 0.0 + this->dummy_normal_texture = lava::create_default_texture(device, { 1, 1 }, glm::vec3(0.5, 0.5, 0), 0.0); //Normal: (0.5, 0.5) -> Vertex Normal + this->dummy_emissive_texture = lava::create_default_texture(device, { 1, 1 }, glm::vec3(0.0, 0.0, 0), 0.0); //Emissive: (0.0, 0.0, 0.0) staging.add(this->dummy_diffuse_texture); staging.add(this->dummy_specular_texture); staging.add(this->dummy_normal_texture); + staging.add(this->dummy_emissive_texture); } void Scene::create_default_light() @@ -515,7 +529,7 @@ bool Scene::create_descriptor_layouts(lava::device_ptr device, uint32_t frame_co uint32_t descriptor_count = /*Materials*/ this->materials.size() + /*Meshes*/ this->meshes.size() * frame_count + /*Lights*/ frame_count; lava::VkDescriptorPoolSizes descriptor_type_count = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, /*Materials*/ (uint32_t)this->materials.size() * 3 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, /*Materials*/ (uint32_t)this->materials.size() * 4 }, { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, /*Meshes*/ (uint32_t)this->meshes.size() * frame_count + /*Lights*/ frame_count }, { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, /*Lights*/ frame_count }, }; @@ -554,6 +568,7 @@ bool Scene::create_descriptor_layouts(lava::device_ptr device, uint32_t frame_co this->material_descriptor->add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); this->material_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); this->material_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); + this->material_descriptor->add_binding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); if (!this->material_descriptor->create(device)) { @@ -568,7 +583,7 @@ bool Scene::create_descriptor_layouts(lava::device_ptr device, uint32_t frame_co bool Scene::create_material_descriptors(lava::device_ptr device) { std::vector<VkWriteDescriptorSet> descriptor_writes; - descriptor_writes.reserve(this->materials.size() * 3); + descriptor_writes.reserve(this->materials.size() * 4); for (SceneMaterial& material : this->materials) { @@ -578,6 +593,7 @@ bool Scene::create_material_descriptors(lava::device_ptr device) textures.push_back(material.diffuse); textures.push_back(material.specular); textures.push_back(material.normal); + textures.push_back(material.emissive); for (uint32_t binding = 0; binding < textures.size(); binding++) { @@ -856,6 +872,123 @@ bool Scene::load_controller(const std::string& file_name, SceneUnit unit, SceneO return true; } +bool Scene::load_sky_sphere(const std::string& file_name, uint32_t ring_count, uint32_t segment_count, lava::device_ptr device, lava::staging& staging) +{ + lava::log()->info("Loading sky-sphere file '{}'", file_name.c_str()); + lava::texture::ptr sky_sphere = lava::load_texture(device, file_name, VK_FORMAT_UNDEFINED); + + if (sky_sphere == nullptr) + { + lava::log()->error("Faild to load sky-sphere file '{}'", file_name.c_str()); + + return false; + } + + staging.add(sky_sphere); + + uint32_t sky_material_index = this->materials.size(); + SceneMaterial& sky_material = this->materials.emplace_back(); + sky_material.diffuse = this->dummy_diffuse_texture; + sky_material.specular = this->dummy_specular_texture; + sky_material.normal = this->dummy_normal_texture; + sky_material.emissive = sky_sphere; + + if (ring_count < 3) + { + lava::log()->error("Invalid number of rings for sky-sphere. The number of rings needs to be at least 3!"); + + return false; + } + + if (segment_count < 3) + { + lava::log()->error("Invalid number of segments for sky-sphere. The number of segments needs to be at least 3!"); + + return false; + } + + glm::vec3 center = (this->scene_min + this->scene_max) / 2.0f; + float radius = glm::distance(center, this->scene_max) * 10.0f; + + uint32_t vertex_count = ring_count * segment_count; + uint32_t index_count = (ring_count - 1) * segment_count * 2; + + lava::mesh_data geometry; + geometry.vertices.reserve(vertex_count); + geometry.indices.reserve(index_count); + + for (uint32_t ring = 0; ring < ring_count; ring++) + { + float coord_v = (float)ring / (float)(ring_count - 1); + float latitude = glm::pi<float>() * coord_v; + + for (uint32_t segment = 0; segment < segment_count; segment++) + { + float coord_u = (float)segment / (float)(segment_count - 1); + float longitude = glm::two_pi<float>() * coord_u; + + glm::vec3 position = center; + position.x += radius * glm::cos(longitude) * glm::sin(latitude); + position.y -= radius * glm::cos(latitude); + position.z += radius * glm::sin(longitude) * glm::sin(latitude); + + glm::vec2 uv = glm::vec2(coord_u, 1.0 - coord_v); + glm::vec3 normal = glm::normalize(center - position); + glm::vec3 tangent = glm::vec3(1.0f, 0.0f, 0.0f); + + if (glm::abs(glm::dot(normal, glm::vec3(0.0f, 1.0f, 0.0f))) < 0.99f) + { + tangent = glm::normalize(glm::cross(normal, glm::vec3(0.0f, 1.0f, 0.0f))); + } + + lava::vertex vertex; + vertex.position = position; + vertex.normal = normal; + vertex.color = glm::vec4(tangent, 0.0f); + vertex.uv = uv; + + geometry.vertices.push_back(vertex); + } + } + + for (uint32_t ring = 0; ring < (ring_count - 1); ring++) + { + uint32_t lower_ring_offset = ring * segment_count; + uint32_t upper_ring_offset = lower_ring_offset + segment_count; + + for (uint32_t segment = 0; segment < segment_count; segment++) + { + uint32_t current_index = segment; + uint32_t next_index = (segment + 1) % segment_count; + + geometry.indices.push_back(lower_ring_offset + current_index); + geometry.indices.push_back(lower_ring_offset + next_index); + geometry.indices.push_back(upper_ring_offset + next_index); + + geometry.indices.push_back(upper_ring_offset + next_index); + geometry.indices.push_back(upper_ring_offset + current_index); + geometry.indices.push_back(lower_ring_offset + current_index); + } + } + + SceneMesh& sky_mesh = this->meshes.emplace_back(); + sky_mesh.node_index = INVALID_NODE; + sky_mesh.material_index = sky_material_index; + sky_mesh.local_min = center; //NOTE: The sky-sphere should not influence the scene bounds + sky_mesh.local_max = center; + sky_mesh.mesh = lava::make_mesh(); + sky_mesh.mesh->add_data(geometry); + + if (!sky_mesh.mesh->create(device)) + { + lava::log()->error("Can't create mesh for sky-sphere!"); + + return false; + } + + return true; +} + bool Scene::load_nodes(const aiNode* node, float scale, uint32_t base_mesh, SceneNodeIndex parent_index, std::map<std::string, SceneNodeIndex>& node_indices) { SceneNode scene_node; @@ -1083,7 +1216,8 @@ bool Scene::load_materials(const aiScene* scene, const std::string& base_name, l { const aiMaterial* material = scene->mMaterials[index]; SceneMaterial& scene_material = this->materials.emplace_back(); - + scene_material.emissive = this->dummy_emissive_texture; + if (!this->load_texture(material, base_name, aiTextureType_DIFFUSE, device, staging, scene_material.diffuse)) { scene_material.diffuse = this->dummy_diffuse_texture; @@ -1354,13 +1488,23 @@ void Scene::apply_transforms() for (SceneMesh& mesh : this->meshes) { - const SceneNode& node = this->nodes[mesh.node_index]; + bool visible = true; + glm::mat4 localToWorldSpace = glm::mat4(1.0f); + glm::mat4 vectorToWorldSpace = glm::mat4(1.0f); + + if (mesh.node_index >= 0) + { + const SceneNode& node = this->nodes[mesh.node_index]; + visible = node.global_visible; + localToWorldSpace = node.current_global_transform; + vectorToWorldSpace = glm::transpose(glm::inverse(node.current_global_transform)); + } - mesh.visible = node.global_visible; - mesh.data.localToWorldSpace = node.current_global_transform; - mesh.data.vectorToWorldSpace = glm::transpose(glm::inverse(node.current_global_transform)); + mesh.visible = visible; + mesh.data.localToWorldSpace = localToWorldSpace; + mesh.data.vectorToWorldSpace = vectorToWorldSpace; - this->compute_mesh_bounds(mesh, node.current_global_transform); + this->compute_mesh_bounds(mesh, localToWorldSpace); } this->compute_scene_bounds(); diff --git a/src/scene.hpp b/src/scene.hpp index ffbcbf92223e08c3f61a7a6d67461d023e3695f5..847d53ab647a3975041bf8777a5065e1426ee25e 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -36,6 +36,10 @@ struct SceneConfig std::string controller_left_file; std::string controller_right_file; + std::string sky_sphere_file; //NOTE: The given file has to be a .hdr file. + uint32_t sky_sphere_rings = 16; //NOTE: Defines the number of rings used in the mesh of the sky-sphere. Needs to be at least 3. + uint32_t sky_sphere_segments = 32; //NOTE: Defines the number of segments used in the mesh of the sky-sphere. Needs to be at least 3. + SceneUnit scene_unit = SCENE_UNIT_METERS; SceneUnit controller_left_unit = SCENE_UNIT_METERS; SceneUnit controller_right_unit = SCENE_UNIT_METERS; @@ -96,6 +100,7 @@ struct SceneMaterial lava::texture::ptr diffuse; lava::texture::ptr specular; lava::texture::ptr normal; + lava::texture::ptr emissive; VkDescriptorSet descriptor_set = VK_NULL_HANDLE; }; @@ -169,6 +174,7 @@ private: bool load_scene(const std::string& file_name, SceneUnit unit, SceneOrientation orientation, lava::device_ptr device, lava::staging& staging, std::map<std::string, SceneNodeIndex>& node_indices); bool load_controller(const std::string& file_name, SceneUnit unit, SceneOrientation orientation, lava::device_ptr device, lava::staging& staging, SceneNodeIndex& node_index, std::map<std::string, SceneNodeIndex>& node_indices); + bool load_sky_sphere(const std::string& file_name, uint32_t ring_count, uint32_t segment_count, lava::device_ptr device, lava::staging& staging); bool load_nodes(const aiNode* node, float scale, uint32_t base_mesh, SceneNodeIndex parent_index, std::map<std::string, SceneNodeIndex>& node_indices); bool load_cameras(const aiScene* scene, float scale, const std::map<std::string, SceneNodeIndex>& node_indices); @@ -230,6 +236,7 @@ private: lava::texture::ptr dummy_diffuse_texture; lava::texture::ptr dummy_specular_texture; lava::texture::ptr dummy_normal_texture; + lava::texture::ptr dummy_emissive_texture; }; Scene::Ptr make_scene(); \ No newline at end of file diff --git a/src/utility/command_parser.cpp b/src/utility/command_parser.cpp index 44c1294364a09becdde57e29591fb5db2445a2a3..d20aea60144c2dbea7bec8f9625127bd78a2267c 100644 --- a/src/utility/command_parser.cpp +++ b/src/utility/command_parser.cpp @@ -37,25 +37,38 @@ bool CommandParser::parse_command(const argh::parser& cmd_line) for (const std::pair<std::string, std::string>& parameter : cmd_line.params()) { - if (parameter.first == "headset") + if (parameter.first == "sky-sphere") { - if (!this->set_headset(parameter.second)) + std::string path = parameter.second; + std::string extension = path.substr(path.find_last_of('.')); + + if (extension != ".hdr") + { + std::cout << "Invalid file format for parameter 'sky-sphere'. Only .hdr images supported. Use option --help to get more information." << std::endl; + } + + this->sky_sphere_path = path; + } + + else if (parameter.first == "scene-unit") + { + if (!this->set_scene_unit(parameter.second)) { return false; } } - else if (parameter.first == "stereo-strategy") + else if (parameter.first == "headset") { - if (!this->set_stero_strategy(parameter.second)) + if (!this->set_headset(parameter.second)) { return false; } } - else if (parameter.first == "scene-unit") + else if (parameter.first == "stereo-strategy") { - if (!this->set_scene_unit(parameter.second)) + if (!this->set_stero_strategy(parameter.second)) { return false; } @@ -210,6 +223,11 @@ const std::string& CommandParser::get_scene_path() const return this->scene_path; } +std::optional<std::string> CommandParser::get_sky_sphere_path() const +{ + return this->sky_sphere_path; +} + std::optional<std::string> CommandParser::get_animation_name() const { return this->animation_name; @@ -256,6 +274,8 @@ void CommandParser::show_help() std::cout << " depth-peeling-reprojection.exe [Options] [Scene File]" << std::endl; std::cout << std::endl; std::cout << "Options:" << std::endl; + std::cout << " --sky-sphere={file} The sky sphere that should be shown. The given file has to be a .hdr file." << std::endl; + std::cout << " If no file is specified, sky stays black." << std::endl; std::cout << " --scene-unit={unit} The unit in which the geometry of the scene is defined." << std::endl; std::cout << " Options: meters (default), centimeters" << std::endl; std::cout << " --benchmark Play animation once and close program after completion." << std::endl; diff --git a/src/utility/command_parser.hpp b/src/utility/command_parser.hpp index e60027590817ad22e96cd8880248c9c776885623..8cc7ea9509db65a27dc2c3b222e3e9f96bc9d477 100644 --- a/src/utility/command_parser.hpp +++ b/src/utility/command_parser.hpp @@ -24,6 +24,7 @@ public: SceneUnit get_scene_unit() const; const std::string& get_scene_path() const; + std::optional<std::string> get_sky_sphere_path() const; std::optional<std::string> get_animation_name() const; std::optional<uint32_t> get_animation_index() const; @@ -50,6 +51,7 @@ private: SceneUnit scene_unit = SCENE_UNIT_METERS; std::string scene_path = ""; + std::optional<std::string> sky_sphere_path; std::optional<std::string> animation_name; std::optional<uint32_t> animation_index; diff --git a/src/vr_application.cpp b/src/vr_application.cpp index 59230bfa97e774a758186dae1f53bc91406f7cba..57cd864e689b680e77702bb8830621962c498e01 100644 --- a/src/vr_application.cpp +++ b/src/vr_application.cpp @@ -298,6 +298,11 @@ bool VRApplication::on_create() config.scene_file = this->command_parser.get_scene_path(); config.scene_unit = this->command_parser.get_scene_unit(); config.scene_orientation = SCENE_ORIENTATION_RIGHT_HANDED; + + if (this->command_parser.get_sky_sphere_path().has_value()) + { + config.sky_sphere_file = this->command_parser.get_sky_sphere_path().value(); + } if (!this->command_parser.should_benchmark()) {