17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-09 00:17:39 +01:00

Shade the sky and the sun by the scenery around the camera instead of the track environment flags

This commit is contained in:
2026-10-27 16:02:36 +01:00
parent 9dde5b0e5e
commit 2a876befb0
35 changed files with 2351 additions and 55 deletions

View File

@@ -80,7 +80,11 @@ constexpr std::uint32_t SCENE_AO_TEX = 27;
constexpr std::uint32_t CAB_PROBE_TEX = 28;
constexpr std::uint32_t CAB_PROBE_ALBEDO_TEX = 29;
constexpr std::uint32_t CAB_PROBE_SSBO = 30;
constexpr std::uint32_t PASS_BINDING_COUNT = 31;
// the occlusion of the sky and of the sun by the scenery around the camera (gfx::sky_occlusion, the track T): the height of the top of
// the scenery and the shares of the sky and the limits of the key light, read texel by texel
constexpr std::uint32_t SKY_OCCLUSION_HEIGHT_TEX = 31;
constexpr std::uint32_t SKY_OCCLUSION_TEX = 32;
constexpr std::uint32_t PASS_BINDING_COUNT = 33;
// the offsets of the bindings above with dynamic offsets, in the order of the bindings
constexpr std::uint32_t PASS_DYNAMIC_OFFSETS = 6;
// the textures of a material take the bindings of their units (#pragma texture(name, unit, ...)), 0 .. MAX_TEXTURES - 1

View File

@@ -29,6 +29,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/vulkan14/vulkan14cull.h"
#include "rendering/vulkan14/vulkan14shadows.h"
#include "rendering/vulkan14/vulkan14cabprobe.h"
#include "rendering/vulkan14/vulkan14skyocclusion.h"
#include "rendering/vulkan14/vulkan14terrain.h"
#include "rendering/vulkan14/vulkan14grass.h"
#include "rendering/vulkan14/vulkan14particles.h"
@@ -399,6 +400,9 @@ struct scene_state
compute_kernel cabprobe_sh;
buffer cabprobe_bounce;
std::shared_ptr<image> cabprobe_empty;
// the occlusion of the sky by the scenery around the camera (the track T): its maps, and their stand-ins while there are none
sky_occlusion_maps skyocclusion;
std::shared_ptr<image> skyocclusion_heights_empty, skyocclusion_empty;
// the materials of the heightmap terrain (the stage P6), made with the bank of the textures
std::unique_ptr<terrain_materials> terrain;
// the grass of the heightmap terrain, and its program, made when it's first drawn

View File

@@ -168,6 +168,10 @@ std::string vulkan14_renderer::scene_info()
{
text += " " + scene.cabprobe.info() + "\n";
}
if (Global.gfx_skyvisibility > 0)
{
text += " " + scene.skyocclusion.info() + "\n";
}
text += cascades + line(" submodels:", colour.submodels, shadowstats.submodels) + line(" paths: ", colour.paths, shadowstats.paths) +
line(" shapes: ", colour.shapes, shadowstats.shapes) + " traction: " + to_string(colour.traction, 7) + "\n" + " lines: " + to_string(colour.lines, 7) + "\n" +
"particles: " + to_string(colour.particles, 7) + "\nlights: " + to_string(static_cast<int>(m_lights_lit), 7) + " (" + std::to_string(m_lights_dropped) + " over capacity), " +
@@ -333,6 +337,8 @@ void vulkan14_renderer::draw_debug_ui()
}
}
ImGui::Separator();
scene.skyocclusion.draw_debug_ui();
ImGui::Separator();
std::string raytracing;
if (m_context->scene.raytracing)
{

View File

@@ -610,6 +610,7 @@ bool vulkan14_renderer::Render()
}
scene.lamps.begin_frame(ctx.commands, value);
scene.cabprobe.begin_frame(value);
scene.skyocclusion.begin_frame(value);
if (scene.terrain)
{
// textures of new heightmap terrain layers go to the arrays the terrain shader reads them from
@@ -680,8 +681,9 @@ bool vulkan14_renderer::Render()
if (false == scene.images_ready)
{
scene.images_ready = true;
std::vector<vk14::image *> const standins{scene.shadow_empty.get(), scene.env_empty.get(), scene.volume_empty.get(), scene.cube_shadow_empty.get(),
scene.headlight_shadow_empty.get(), scene.depth_empty.get(), scene.cabprobe_empty.get()};
std::vector<vk14::image *> const standins{scene.shadow_empty.get(), scene.env_empty.get(), scene.volume_empty.get(), scene.cube_shadow_empty.get(),
scene.headlight_shadow_empty.get(), scene.depth_empty.get(), scene.cabprobe_empty.get(), scene.skyocclusion_heights_empty.get(),
scene.skyocclusion_empty.get()};
std::vector<vk14::image_use> clears;
for (auto *image : standins)
{
@@ -769,11 +771,17 @@ bool vulkan14_renderer::Render()
{
m_locallights.gather(Global.pCamera.Pos, *simulation::Region, m_geometry, m_framestamp);
}
// the occlusion of the sky by the scenery around the camera: the tiles which came in, what the background worked out
if (false == Global.gfx_skiprendering)
{
scene.skyocclusion.update(simulation::Region, Global.pCamera.Pos, -m_sunlight.direction);
}
}
else
{
// a scenery is being loaded: the cells, instances and models the lights were taken from may be gone
m_locallights.reset();
scene.skyocclusion.clear();
scene.lamps.reset_lamps();
scene.lamps.reset_headlights();
// and so may the tracks the sleeper records were made for, and the models drawn on their own
@@ -791,6 +799,8 @@ bool vulkan14_renderer::Render()
++m_instancerecordsversion;
m_nodestateversion = scene::node_state_version;
}
// what the maps of the occlusion of the sky were given, copied to them
scene.skyocclusion.flush(ctx.commands);
// the sleeper records of tracks out of sight for a while, and the records of the models drawn on their own
Expire_Indirect_Records();
// scenery lamps which get shadows in this frame. their maps hold the scenery as of the version of the instance records
@@ -1547,6 +1557,8 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
// the clouds of the view and the fog volume, which take the lights of the clusters and the shadow map of the pass
{
auto const fogtarget{glm::ivec2{vp.width, vp.height}};
// the occlusion of the sky by the scenery, seen from the camera of the view
scene.skyocclusion.setup_scene(scene_ubs, m_colorpass.pass_camera.position());
scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION);
scene_ubs.inv_view = glm::inverse(glm::mat4{glm::mat3{m_colorpass.pass_camera.modelview()}});
setup_shadow_matrices();
@@ -3737,12 +3749,7 @@ void vulkan14_renderer::update_lights(light_array &Lights, bool const Headlights
}
renderlight.position = lightoffset;
renderlight.direction = scenelight.direction;
auto luminance = static_cast<float>(Global.fLuminance);
auto const environment = scenelight.owner->fShade;
if (environment > 0.f)
{
luminance *= environment;
}
auto const luminance = static_cast<float>(Global.fLuminance) * gfx::vehicle_light_level(scenelight.owner);
renderlight.diffuse = glm::vec4{glm::max(glm::vec3{colors::none}, scenelight.color - glm::vec3{luminance}), renderlight.diffuse[3]};
renderlight.ambient = glm::vec4{glm::max(glm::vec3{colors::none}, scenelight.color * glm::vec3{scenelight.intensity} - glm::vec3{luminance}), renderlight.ambient[3]};
renderlight.factor = (scenelight.count * 0.5) * (scenelight.owner->DimHeadlights ? 0.5 : 1.0);

View File

@@ -202,6 +202,11 @@ std::array<std::array<VkImageView, vk14::bindings::PASS_BINDING_COUNT>, 3> pass_
auto const probeempty{scene.cabprobe_empty ? scene.cabprobe_empty->view() : scene.env_empty->view()};
views[CAB_PROBE_TEX] = (colour && (probelight != nullptr) ? probelight->view() : probeempty);
views[CAB_PROBE_ALBEDO_TEX] = (colour && (probealbedo != nullptr) ? probealbedo->view() : probeempty);
// the occlusion of the sky by the scenery (the track T), for every pass which shades
auto const *occlusionheights{scene.skyocclusion.heights()};
auto const *occlusion{scene.skyocclusion.occlusion()};
views[SKY_OCCLUSION_HEIGHT_TEX] = (occlusionheights != nullptr ? occlusionheights->view() : scene.skyocclusion_heights_empty->view());
views[SKY_OCCLUSION_TEX] = (occlusion != nullptr ? occlusion->view() : scene.skyocclusion_empty->view());
}
return result;
}
@@ -663,6 +668,29 @@ bool vulkan14_renderer::init_scene(std::string &Reason)
{
return false;
}
// the occlusion of the sky by the scenery: the stand-ins of its maps of one texel, cleared with the other stand-ins in the first
// frame (nothing reads them: the block of the scene tells there are no maps)
{
auto standin = [&](std::shared_ptr<vk14::image> &Image, VkFormat const Format, char const *Name) {
vk14::image_desc desc;
desc.format = Format;
desc.extent = {1, 1, 1};
desc.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
Image = std::make_shared<vk14::image>();
if (false == Image->create(ctx.memory, desc, Name))
{
Image.reset();
return false;
}
return true;
};
if ((false == standin(scene.skyocclusion_heights_empty, VK_FORMAT_R32_SFLOAT, "empty sky occlusion heights")) || (false == standin(scene.skyocclusion_empty, VK_FORMAT_R16G16B16A16_SFLOAT, "empty sky occlusion")))
{
Reason = "no stand-ins of the maps of the occlusion of the sky";
return false;
}
scene.skyocclusion.init(ctx.memory, ctx.deletions);
}
scene.lamps.init(device, ctx.memory, ctx.deletions);
// the layer maps of the heightmap terrain drawn into the layers of its arrays by the copy shader
@@ -832,6 +860,9 @@ void vulkan14_renderer::destroy_scene()
destroy_weather();
destroy_ao();
destroy_cab_probe();
scene.skyocclusion.destroy();
scene.skyocclusion_heights_empty.reset();
scene.skyocclusion_empty.reset();
for (auto *pool : scene.occlusion_pools)
{
vkDestroyQueryPool(device, pool, nullptr);
@@ -1032,6 +1063,8 @@ void vulkan14_renderer::write_pass_sets(vk14::instance_cull *Cull)
write(handle, CAB_PROBE_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_TEX]));
write(handle, CAB_PROBE_ALBEDO_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_ALBEDO_TEX]));
write(handle, CAB_PROBE_SSBO, &cabprobe, nullptr);
write(handle, SKY_OCCLUSION_HEIGHT_TEX, nullptr, image(ctx.sampling.repeat_nearest, views[SKY_OCCLUSION_HEIGHT_TEX]));
write(handle, SKY_OCCLUSION_TEX, nullptr, image(ctx.sampling.repeat_nearest, views[SKY_OCCLUSION_TEX]));
}
vkUpdateDescriptorSets(device, static_cast<std::uint32_t>(writes.size()), writes.data(), 0, nullptr);
}

View File

@@ -807,6 +807,8 @@ std::string binding_preamble(bool const Raytracing)
define("CAB_PROBE_TEX", CAB_PROBE_TEX);
define("CAB_PROBE_ALBEDO_TEX", CAB_PROBE_ALBEDO_TEX);
define("CAB_PROBE_SSBO", CAB_PROBE_SSBO);
define("SKY_OCCLUSION_HEIGHT_TEX", SKY_OCCLUSION_HEIGHT_TEX);
define("SKY_OCCLUSION_TEX", SKY_OCCLUSION_TEX);
define("PASS_BINDING_COUNT", PASS_BINDING_COUNT);
define("MATERIAL_TEXTURE_FIRST", MATERIAL_TEXTURE_FIRST);
define("CL_CLUSTER_UBO", CL_CLUSTER_UBO);

View File

@@ -0,0 +1,160 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "rendering/vulkan14/vulkan14skyocclusion.h"
#include "rendering/vulkan14/vulkan14commands.h"
#include "utilities/Logs.h"
namespace vk14
{
namespace
{
// the shading and the compute passes (the fog, the particles) read them
VkPipelineStageFlags2 const READ_STAGES{VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT};
} // namespace
sky_occlusion_maps::~sky_occlusion_maps()
{
let_go();
}
void sky_occlusion_maps::init(allocator &Memory, deletion_queue &Deletions)
{
m_memory = &Memory;
m_deletions = &Deletions;
}
void sky_occlusion_maps::destroy()
{
// the device is idle: the maps go at once
m_deletions = nullptr;
let_go();
m_memory = nullptr;
}
bool sky_occlusion_maps::gpu_allocate(int const Side)
{
let_go();
if ((m_memory == nullptr) || (Side <= 0))
{
return false;
}
auto make = [&](std::shared_ptr<image> &Map, VkFormat const Format, char const *Name) {
image_desc desc;
desc.format = Format;
desc.extent = {static_cast<std::uint32_t>(Side), static_cast<std::uint32_t>(Side), 1};
desc.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
Map = std::make_shared<image>();
if (false == Map->create(*m_memory, desc, Name))
{
Map.reset();
return false;
}
return true;
};
if ((false == make(m_heights, VK_FORMAT_R32_SFLOAT, "sky occlusion heights")) || (false == make(m_occlusion, VK_FORMAT_R16G16B16A16_SFLOAT, "sky occlusion")))
{
let_go();
return false;
}
m_ready = false;
return true;
}
void sky_occlusion_maps::gpu_release()
{
let_go();
}
void sky_occlusion_maps::let_go()
{
m_ready = false;
m_squares.clear();
m_data.clear();
if ((m_deletions != nullptr) && (m_heights || m_occlusion))
{
// the frames in flight may still read them
m_deletions->push(m_value, [heights = std::move(m_heights), occlusion = std::move(m_occlusion)]() {});
}
m_heights.reset();
m_occlusion.reset();
}
void sky_occlusion_maps::gpu_upload(int const X, int const Y, int const Size, float const *Heights, std::uint16_t const *Occlusion)
{
if (false == bool(m_heights))
{
return;
}
auto const texels{static_cast<std::size_t>(Size) * Size};
auto const offset{m_data.size()};
m_data.resize(offset + texels * (sizeof(float) + 4 * sizeof(std::uint16_t)));
std::memcpy(m_data.data() + offset, Heights, texels * sizeof(float));
std::memcpy(m_data.data() + offset + texels * sizeof(float), Occlusion, texels * 4 * sizeof(std::uint16_t));
m_squares.push_back(square{X, Y, Size, offset});
}
void sky_occlusion_maps::flush(command_stream *Commands)
{
if ((Commands == nullptr) || (false == bool(m_heights)) || m_squares.empty())
{
return;
}
auto staging{std::make_shared<buffer>()};
if ((false == staging->create(*m_memory, static_cast<VkDeviceSize>(m_data.size()), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, memory_kind::upload, "sky occlusion upload")) || (staging->mapped() == nullptr))
{
ErrorLog("sky occlusion: no staging buffer for the maps");
return;
}
std::memcpy(staging->mapped(), m_data.data(), m_data.size());
staging->flush(0, static_cast<VkDeviceSize>(m_data.size()));
transition(Commands, *m_heights, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);
transition(Commands, *m_occlusion, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);
std::vector<VkBufferImageCopy2> heights, occlusion;
for (auto const &entry : m_squares)
{
VkBufferImageCopy2 region{};
region.sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2;
region.bufferOffset = static_cast<VkDeviceSize>(entry.offset);
region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.imageOffset = {entry.x, entry.y, 0};
region.imageExtent = {static_cast<std::uint32_t>(entry.size), static_cast<std::uint32_t>(entry.size), 1};
heights.push_back(region);
region.bufferOffset += static_cast<VkDeviceSize>(entry.size) * entry.size * sizeof(float);
occlusion.push_back(region);
}
auto copy = [&](image &Target, std::vector<VkBufferImageCopy2> const &Regions) {
VkCopyBufferToImageInfo2 info{};
info.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2;
info.srcBuffer = staging->handle();
info.dstImage = Target.handle();
info.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
info.regionCount = static_cast<std::uint32_t>(Regions.size());
info.pRegions = Regions.data();
Commands->copy_buffer_to_image2(&info);
};
copy(*m_heights, heights);
copy(*m_occlusion, occlusion);
transition(Commands, *m_heights, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, READ_STAGES, VK_ACCESS_2_SHADER_SAMPLED_READ_BIT);
transition(Commands, *m_occlusion, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, READ_STAGES, VK_ACCESS_2_SHADER_SAMPLED_READ_BIT);
if (m_deletions != nullptr)
{
m_deletions->push(m_value, [staging]() {});
}
m_squares.clear();
m_data.clear();
m_ready = true;
}
} // namespace vk14

View File

@@ -0,0 +1,79 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
// the maps of the occlusion of the sky by the scenery in the vulkan renderer (the track T, opengl46_sky_occlusion): the images of
// gfx::sky_occlusion, which works out what goes into them. the squares of the tiles it gives are gathered in a frame and copied at
// once from one staging buffer, with the commands of the frame (flush())
#pragma once
#include "rendering/common/skyocclusion.h"
#include "rendering/vulkan14/vulkan14memory.h"
#include <memory>
namespace vk14
{
class command_stream;
class sky_occlusion_maps : public gfx::sky_occlusion
{
public:
sky_occlusion_maps() = default;
~sky_occlusion_maps() override;
// Memory, Deletions: what the maps are made and let go with; called once
void init(allocator &Memory, deletion_queue &Deletions);
// the maps go (the device is about to)
void destroy();
// the value of the timeline the gpu reaches when it's done with the frame being recorded
void begin_frame(std::uint64_t const Value)
{
m_value = Value;
}
// the squares given in the frame copied to the maps, which are left in the layout the shading reads them in
void flush(command_stream *Commands);
// the maps once they're filled, for the pass sets; null: the stand-ins are read
image *heights() const
{
return m_ready ? m_heights.get() : nullptr;
}
image *occlusion() const
{
return m_ready ? m_occlusion.get() : nullptr;
}
protected:
bool gpu_allocate(int const Side) override;
void gpu_release() override;
void gpu_upload(int const X, int const Y, int const Size, float const *Heights, std::uint16_t const *Occlusion) override;
private:
void let_go();
allocator *m_memory{nullptr};
deletion_queue *m_deletions{nullptr};
std::uint64_t m_value{0};
std::shared_ptr<image> m_heights;
std::shared_ptr<image> m_occlusion;
bool m_ready{false};
// the squares waiting for the copy: where, and their data one after another (the heights, then the occlusion of each)
struct square
{
int x;
int y;
int size;
std::size_t offset; // of the heights in m_data
};
std::vector<square> m_squares;
std::vector<std::uint8_t> m_data;
};
} // namespace vk14