mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
add refraction support for materials; two pass rain shader
This commit is contained in:
@@ -190,6 +190,7 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry {
|
||||
std::shared_ptr<struct Bloom> m_bloom;
|
||||
std::shared_ptr<struct Sky> m_sky;
|
||||
std::shared_ptr<struct MaAutoExposure> m_auto_exposure;
|
||||
std::shared_ptr<struct WindshieldRain> m_windshield_rain;
|
||||
|
||||
std::unordered_map<TModel3d const *, std::shared_ptr<Rt::IRtModel>> rt_models;
|
||||
std::shared_ptr<Rt::IRtModel> GetRtModel(TModel3d const *);
|
||||
|
||||
@@ -85,6 +85,15 @@ void GbufferBlitPass::Init() {
|
||||
.setKeepInitialState(true));
|
||||
RegisterResource(true, "scene_lit_texture", m_output,
|
||||
nvrhi::ResourceType::Texture_SRV);
|
||||
m_output_copy = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setWidth(m_gbuffer->m_framebuffer->getFramebufferInfo().width)
|
||||
.setHeight(m_gbuffer->m_framebuffer->getFramebufferInfo().height)
|
||||
.setFormat(nvrhi::Format::RGBA16_FLOAT)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
RegisterResource(true, "scene_lit_texture_copy", m_output_copy,
|
||||
nvrhi::ResourceType::Texture_SRV);
|
||||
m_binding_layout = m_backend->GetDevice()->createBindingLayout(
|
||||
nvrhi::BindingLayoutDesc()
|
||||
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(2))
|
||||
@@ -187,6 +196,8 @@ void GbufferBlitPass::UpdateConstants(nvrhi::ICommandList* command_list,
|
||||
constants.m_light_color);
|
||||
constants.m_altitude = Global.pCamera.Pos.y;
|
||||
constants.m_time = Timer::GetTime();
|
||||
constants.m_vertical_fov =
|
||||
glm::radians(Global.FieldOfView / Global.ZoomFactor);
|
||||
|
||||
{
|
||||
float percipitation_intensity = glm::saturate(Global.Overcast - 1.);
|
||||
@@ -238,6 +249,10 @@ void GbufferBlitPass::Render(nvrhi::ICommandList* command_list,
|
||||
m_scene_depth, nvrhi::TextureSlice().resolve(m_scene_depth->getDesc()),
|
||||
m_gbuffer->m_gbuffer_depth,
|
||||
nvrhi::TextureSlice().resolve(m_scene_depth->getDesc()));
|
||||
command_list->copyTexture(
|
||||
m_output_copy, nvrhi::TextureSlice().resolve(m_output_copy->getDesc()),
|
||||
m_output,
|
||||
nvrhi::TextureSlice().resolve(m_output->getDesc()));
|
||||
}
|
||||
|
||||
void GbufferBlitPass::Render(nvrhi::ICommandList* command_list) {
|
||||
|
||||
@@ -22,7 +22,7 @@ struct GbufferBlitPass : public FullScreenPass, public MaResourceRegistry {
|
||||
const glm::dmat4& projection);
|
||||
virtual void Render(nvrhi::ICommandList* command_list) override;
|
||||
|
||||
struct DrawConstants {
|
||||
struct alignas(16) DrawConstants {
|
||||
glm::mat4 m_inverse_model_view;
|
||||
glm::mat4 m_inverse_projection;
|
||||
glm::vec3 m_light_dir;
|
||||
@@ -33,6 +33,7 @@ struct GbufferBlitPass : public FullScreenPass, public MaResourceRegistry {
|
||||
glm::vec4 m_wiper_pos;
|
||||
glm::vec4 m_wiper_timer_out;
|
||||
glm::vec4 m_wiper_timer_return;
|
||||
float m_vertical_fov;
|
||||
};
|
||||
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
@@ -53,5 +54,6 @@ struct GbufferBlitPass : public FullScreenPass, public MaResourceRegistry {
|
||||
nvrhi::ComputePipelineHandle m_pso;
|
||||
|
||||
nvrhi::TextureHandle m_output;
|
||||
nvrhi::TextureHandle m_output_copy;
|
||||
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
|
||||
};
|
||||
@@ -121,13 +121,18 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) {
|
||||
binding.disable_anisotropy = it.second["no_anisotropy"].as<bool>(false);
|
||||
binding.disable_filter = it.second["no_filter"].as<bool>(false);
|
||||
binding.disable_mip_bias = it.second["no_mip_bias"].as<bool>(false);
|
||||
size_t default_texture = m_renderer->GetTextureManager()->FetchTexture(
|
||||
it.second["default"].as<std::string>(""), binding.m_hint, 0, false);
|
||||
if (!default_texture) {
|
||||
default_texture = 1;
|
||||
}
|
||||
|
||||
texture_mappings.emplace_back(
|
||||
MaResourceMapping::Texture_SRV(index, binding.m_name.c_str()));
|
||||
sampler_mappings.emplace_back(
|
||||
MaResourceMapping::Sampler(index, binding.m_sampler_name.c_str()));
|
||||
|
||||
RegisterTexture(binding.m_name.c_str(), 1);
|
||||
RegisterTexture(binding.m_name.c_str(), default_texture);
|
||||
RegisterResource(
|
||||
false, "masked_shadow_sampler",
|
||||
GetTextureManager()->GetSamplerForTraits(0, RenderPassType::ShadowMap),
|
||||
@@ -236,6 +241,7 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) {
|
||||
.Add(MaResourceMapping::Texture_SRV(10, "env_brdf_lut"))
|
||||
.Add(MaResourceMapping::Texture_SRV(11, "shadow_depths"))
|
||||
.Add(MaResourceMapping::Texture_SRV(12, "gbuffer_depth"))
|
||||
.Add(MaResourceMapping::Texture_SRV(13, "scene_lit_texture_copy"))
|
||||
.Add(MaResourceMapping::Texture_SRV(14, "sky_aerial_lut"))
|
||||
.Add(MaResourceMapping::Texture_SRV(
|
||||
16, "forwardplus_index_grid_transparent"))
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#include "rt_model.h"
|
||||
#include "tinyexr.h"
|
||||
#include "windshield_rain.h"
|
||||
|
||||
bool NvRenderer::Init(GLFWwindow *Window) {
|
||||
m_message_callback = std::make_shared<NvRendererMessageCallback>();
|
||||
@@ -92,6 +93,7 @@ bool NvRenderer::Init(GLFWwindow *Window) {
|
||||
m_auto_exposure = std::make_shared<MaAutoExposure>(this);
|
||||
m_fsr = std::make_shared<NvFSR>(this);
|
||||
m_bloom = std::make_shared<Bloom>(GetBackend());
|
||||
m_windshield_rain = std::make_shared<WindshieldRain>();
|
||||
|
||||
// protect from undefined framebuffer size in ini (default -1)
|
||||
int w = Global.gfx_framebuffer_width, h = Global.gfx_framebuffer_height;
|
||||
@@ -136,7 +138,10 @@ bool NvRenderer::Init(GLFWwindow *Window) {
|
||||
// RegisterResource(true, "gbuffer_depth", m_gbuffer->m_gbuffer_depth,
|
||||
// nvrhi::ResourceType::Texture_SRV);
|
||||
|
||||
m_windshield_rain->Init(this);
|
||||
|
||||
if (!InitMaterials()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -631,6 +636,7 @@ bool NvRenderer::Render() {
|
||||
command_list->endMarker();
|
||||
|
||||
if (true) {
|
||||
m_windshield_rain->Render(pass);
|
||||
command_list->beginMarker("Forward pass");
|
||||
pass.m_framebuffer = m_framebuffer_forward;
|
||||
pass.m_type = RenderPassType::Forward;
|
||||
|
||||
@@ -373,6 +373,19 @@ size_t NvTextureManager::FetchTexture(std::string path, int format_hint,
|
||||
return index;
|
||||
}
|
||||
|
||||
size_t NvTextureManager::RegisterExternalTexture(std::string const& path,
|
||||
nvrhi::ITexture *texture) {
|
||||
auto [it, added] = m_texture_map.emplace(path, -1);
|
||||
if (added) {
|
||||
m_texture_cache.emplace_back(std::make_shared<NvTexture>());
|
||||
it->second = m_texture_cache.size();
|
||||
}
|
||||
auto cache = m_texture_cache[it->second - 1];
|
||||
cache->m_rhi_texture = texture;
|
||||
cache->m_last_change = ++NvTexture::s_change_counter;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool NvTexture::CreateRhiTexture() {
|
||||
if (m_rhi_texture) return true;
|
||||
m_last_change = ++s_change_counter;
|
||||
@@ -495,8 +508,14 @@ nvrhi::SamplerHandle NvTextureManager::GetSamplerForTraits(
|
||||
: nvrhi::SamplerAddressMode::Wrap)
|
||||
.setAllFilters(!traits[MaTextureTraits_NoFilter])
|
||||
.setMipFilter(false)
|
||||
.setMaxAnisotropy(traits[MaTextureTraits_NoAnisotropy] || traits[MaTextureTraits_NoFilter] ? 0.f : 16.f)
|
||||
.setMipBias(traits[MaTextureTraits_NoMipBias] || traits[MaTextureTraits_NoFilter] ? 0.f : -1.76f);
|
||||
.setMaxAnisotropy(traits[MaTextureTraits_NoAnisotropy] ||
|
||||
traits[MaTextureTraits_NoFilter]
|
||||
? 0.f
|
||||
: 16.f)
|
||||
.setMipBias(traits[MaTextureTraits_NoMipBias] ||
|
||||
traits[MaTextureTraits_NoFilter]
|
||||
? 0.f
|
||||
: -1.76f);
|
||||
sampler = m_backend->GetDevice()->createSampler(desc);
|
||||
}
|
||||
return sampler;
|
||||
|
||||
@@ -83,6 +83,7 @@ class NvTextureManager {
|
||||
NvTextureManager(class NvRenderer* renderer);
|
||||
size_t FetchTexture(std::string path, int format_hint, int size_bias,
|
||||
bool unload_on_location);
|
||||
size_t RegisterExternalTexture(std::string const& path, nvrhi::ITexture *texture);
|
||||
void UpdateLastUse(size_t handle, const glm::dvec3& location);
|
||||
bool IsValidHandle(size_t handle);
|
||||
NvTexture* GetTexture(size_t handle);
|
||||
|
||||
246
betterRenderer/renderer/source/windshield_rain.cpp
Normal file
246
betterRenderer/renderer/source/windshield_rain.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#include "windshield_rain.h"
|
||||
|
||||
#include "gbuffer.h"
|
||||
#include "gbufferblitpass.h"
|
||||
#include "nvrendererbackend.h"
|
||||
#include "nvrhi/utils.h"
|
||||
#include "nvtexture.h"
|
||||
|
||||
void WindshieldRain::Init(NvRenderer* in_renderer) {
|
||||
renderer = in_renderer;
|
||||
{
|
||||
auto const src_desc = renderer->m_gbuffer->m_gbuffer_depth->getDesc();
|
||||
width = src_desc.width / 2;
|
||||
height = src_desc.height / 2;
|
||||
}
|
||||
auto backend = renderer->GetBackend();
|
||||
tex_droplets = backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("Raindrop buffer")
|
||||
.setWidth(width)
|
||||
.setHeight(height)
|
||||
.setFormat(nvrhi::Format::R32_FLOAT)
|
||||
.setIsRenderTarget(true)
|
||||
.setInitialState(nvrhi::ResourceStates::RenderTarget)
|
||||
.setClearValue(nvrhi::Color(0.))
|
||||
.setUseClearValue(true)
|
||||
.setKeepInitialState(true));
|
||||
tex_depth_temp = backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("Raindrop depths temp")
|
||||
.setWidth(width)
|
||||
.setHeight(height)
|
||||
.setFormat(nvrhi::Format::D32)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setKeepInitialState(true));
|
||||
tex_depth = backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("Raindrop depths")
|
||||
.setWidth(width)
|
||||
.setHeight(height)
|
||||
.setFormat(nvrhi::Format::D32)
|
||||
.setIsRenderTarget(true)
|
||||
.setInitialState(nvrhi::ResourceStates::DepthWrite)
|
||||
.setClearValue(nvrhi::Color(0.))
|
||||
.setUseClearValue(true)
|
||||
.setKeepInitialState(true));
|
||||
framebuffer = backend->GetDevice()->createFramebuffer(
|
||||
nvrhi::FramebufferDesc()
|
||||
.addColorAttachment(tex_droplets)
|
||||
.setDepthAttachment(tex_depth));
|
||||
|
||||
ps_rain_anim =
|
||||
backend->CreateShader("windshield_rain_anim", nvrhi::ShaderType::Pixel);
|
||||
|
||||
vs_rain_anim =
|
||||
backend->CreateShader("default_vertex_no_jitter", nvrhi::ShaderType::Vertex);
|
||||
|
||||
cs_max_depth =
|
||||
backend->CreateShader("max_depth_4x4", nvrhi::ShaderType::Compute);
|
||||
|
||||
auto const texture_manager = renderer->GetTextureManager();
|
||||
|
||||
texture_handle_droplets = texture_manager->FetchTexture(
|
||||
"textures/fx/raindrops_height", GL_R, 0, false);
|
||||
|
||||
texture_handle_wipermask = texture_manager->FetchTexture(
|
||||
"dynamic/pkp/ep09_v1/104ec/szyby_wipermask", GL_SRGB, 0, false);
|
||||
|
||||
texture_manager->RegisterExternalTexture("system/raindrops_buffer",
|
||||
tex_droplets);
|
||||
|
||||
{
|
||||
nvrhi::SamplerHandle sampler = backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc().setAllFilters(true).setAllAddressModes(
|
||||
nvrhi::SamplerAddressMode::Clamp));
|
||||
nvrhi::BindingLayoutHandle binding_layout;
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
0, renderer->m_gbuffer->m_gbuffer_depth))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, tex_depth_temp))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler)),
|
||||
binding_layout, bindings_max_depth);
|
||||
pso_max_depth = backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.addBindingLayout(binding_layout)
|
||||
.setComputeShader(cs_max_depth));
|
||||
}
|
||||
}
|
||||
|
||||
void WindshieldRain::Resize(int width, int height) {}
|
||||
|
||||
nvrhi::ITexture* WindshieldRain::GetTexture() const { return tex_droplets; }
|
||||
|
||||
void WindshieldRain::Render(NvRenderer::RenderPass const& pass) {
|
||||
if (!pso_droplets) {
|
||||
CreatePso(pass.m_command_list_draw);
|
||||
}
|
||||
if (renderer->m_dynamic_with_kabina < renderer->m_dynamics.size()) {
|
||||
const auto& dynamic = renderer->m_dynamics[renderer->m_dynamic_with_kabina];
|
||||
pass.m_command_list_draw->beginMarker("Render cab rain buffer");
|
||||
pass.m_command_list_draw->clearTextureFloat(
|
||||
tex_droplets, nvrhi::AllSubresources, nvrhi::Color(0.));
|
||||
{
|
||||
nvrhi::ComputeState compute_state;
|
||||
compute_state.setPipeline(pso_max_depth);
|
||||
compute_state.addBindingSet(bindings_max_depth);
|
||||
pass.m_command_list_draw->setComputeState(compute_state);
|
||||
pass.m_command_list_draw->dispatch((width + 7) / 8, (height + 7) / 8);
|
||||
}
|
||||
pass.m_command_list_draw->copyTexture(tex_depth, nvrhi::TextureSlice(),
|
||||
tex_depth_temp, nvrhi::TextureSlice());
|
||||
for (auto const& item : dynamic.m_renderable_kabina.m_items) {
|
||||
auto const& material = renderer->m_material_cache[item.m_material - 1];
|
||||
if (!IsRainShader(material.m_template)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nvrhi::GraphicsState gfx_state{};
|
||||
nvrhi::DrawArguments draw_arguments;
|
||||
bool indexed;
|
||||
|
||||
if (!renderer->BindGeometry(item.m_geometry, pass, gfx_state,
|
||||
draw_arguments, indexed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto transform = item.m_transform;
|
||||
transform[3] -= pass.m_origin;
|
||||
|
||||
auto& binding_set = binding_sets_per_material[item.m_material];
|
||||
if (!binding_set) {
|
||||
auto const& material = renderer->m_material_cache[item.m_material - 1];
|
||||
auto texture_wipermask = renderer->GetTextureManager()->GetRhiTexture(
|
||||
material.m_texture_handles[2], pass.m_command_list_draw);
|
||||
binding_set = renderer->m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
0, renderer->m_drawconstant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
2, renderer->m_gbuffer_blit->m_draw_constants))
|
||||
.addItem(nvrhi::BindingSetItem::PushConstants(
|
||||
1, sizeof(NvRenderer::PushConstantsDraw)))
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::Texture_SRV(0, texture_droplets))
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::Texture_SRV(1, texture_wipermask))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(1, sampler_point)),
|
||||
binding_layout);
|
||||
}
|
||||
|
||||
gfx_state.setFramebuffer(framebuffer);
|
||||
gfx_state.setPipeline(pso_droplets);
|
||||
gfx_state.addBindingSet(binding_set);
|
||||
gfx_state.setViewport(nvrhi::ViewportState().addViewportAndScissorRect(
|
||||
framebuffer->getFramebufferInfo().getViewport()));
|
||||
|
||||
pass.m_command_list_draw->beginMarker(item.m_name.data());
|
||||
|
||||
pass.m_command_list_draw->setGraphicsState(gfx_state);
|
||||
|
||||
{
|
||||
NvRenderer::PushConstantsDraw data{};
|
||||
data.m_modelview = static_cast<glm::mat3x4>(
|
||||
transpose(static_cast<glm::dmat4>(pass.m_transform) *
|
||||
static_cast<glm::dmat4>(transform)));
|
||||
data.m_modelview_history = data.m_modelview;
|
||||
pass.m_command_list_draw->setPushConstants(&data, sizeof(data));
|
||||
}
|
||||
|
||||
if (indexed)
|
||||
pass.m_command_list_draw->drawIndexed(draw_arguments);
|
||||
else
|
||||
pass.m_command_list_draw->draw(draw_arguments);
|
||||
pass.m_command_list_draw->endMarker();
|
||||
}
|
||||
pass.m_command_list_draw->endMarker();
|
||||
}
|
||||
std::erase_if(binding_sets_per_material, [this](auto const& kv) {
|
||||
auto const& material = renderer->m_material_cache[kv.first - 1];
|
||||
return renderer->GetCurrentFrame() - material.m_last_frame_requested > 100;
|
||||
});
|
||||
}
|
||||
|
||||
bool WindshieldRain::IsRainShader(
|
||||
NvRenderer::MaterialTemplate const* mt) const {
|
||||
return mt->m_name == "windshield_rain";
|
||||
}
|
||||
|
||||
void WindshieldRain::CreatePso(nvrhi::ICommandList* command_list) {
|
||||
auto backend = renderer->GetBackend();
|
||||
|
||||
texture_droplets = renderer->GetTextureManager()->GetRhiTexture(
|
||||
texture_handle_droplets, command_list);
|
||||
|
||||
sampler = backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setAllFilters(true)
|
||||
.setMipFilter(false)
|
||||
.setAllAddressModes(nvrhi::SamplerAddressMode::Repeat));
|
||||
|
||||
sampler_point = backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc().setAllFilters(false).setAllAddressModes(
|
||||
nvrhi::SamplerAddressMode::Repeat));
|
||||
|
||||
binding_layout = backend->GetDevice()->createBindingLayout(
|
||||
nvrhi::BindingLayoutDesc()
|
||||
.setVisibility(nvrhi::ShaderType::AllGraphics)
|
||||
.setRegisterSpace(0)
|
||||
.addItem(nvrhi::BindingLayoutItem::ConstantBuffer(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(2))
|
||||
.addItem(nvrhi::BindingLayoutItem::PushConstants(
|
||||
1, sizeof(NvRenderer::PushConstantsDraw)))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(1))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(1)));
|
||||
|
||||
pso_droplets = backend->GetDevice()->createGraphicsPipeline(
|
||||
nvrhi::GraphicsPipelineDesc()
|
||||
.addBindingLayout(binding_layout)
|
||||
.setVertexShader(vs_rain_anim)
|
||||
.setInputLayout(renderer->m_input_layout[static_cast<size_t>(
|
||||
RendererEnums::DrawType::Model)])
|
||||
.setPixelShader(ps_rain_anim)
|
||||
.setRenderState(
|
||||
nvrhi::RenderState()
|
||||
.setDepthStencilState(
|
||||
nvrhi::DepthStencilState()
|
||||
.enableDepthTest()
|
||||
.enableDepthWrite()
|
||||
.disableStencil()
|
||||
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
|
||||
.setRasterState(nvrhi::RasterState()
|
||||
.setFillSolid()
|
||||
.enableDepthClip()
|
||||
.disableScissor()
|
||||
.setCullFront())
|
||||
.setBlendState(nvrhi::BlendState().setRenderTarget(
|
||||
0, nvrhi::BlendState::RenderTarget().disableBlend())))
|
||||
.setPrimType(nvrhi::PrimitiveType::TriangleList),
|
||||
framebuffer);
|
||||
}
|
||||
46
betterRenderer/renderer/source/windshield_rain.h
Normal file
46
betterRenderer/renderer/source/windshield_rain.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
struct WindshieldRain {
|
||||
void Init(NvRenderer *in_renderer);
|
||||
void Resize(int width, int height);
|
||||
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
|
||||
[[nodiscard]] nvrhi::ITexture *GetTexture() const;
|
||||
|
||||
void Render(NvRenderer::RenderPass const &pass);
|
||||
bool IsRainShader(NvRenderer::MaterialTemplate const *mt) const;
|
||||
|
||||
size_t texture_handle_droplets;
|
||||
size_t texture_handle_wipermask;
|
||||
|
||||
nvrhi::TextureHandle texture_droplets;
|
||||
|
||||
nvrhi::SamplerHandle sampler;
|
||||
nvrhi::SamplerHandle sampler_point;
|
||||
|
||||
void CreatePso(nvrhi::ICommandList *command_list);
|
||||
|
||||
NvRenderer *renderer;
|
||||
nvrhi::FramebufferHandle framebuffer;
|
||||
nvrhi::TextureHandle tex_droplets;
|
||||
nvrhi::TextureHandle tex_depth;
|
||||
nvrhi::TextureHandle tex_depth_temp;
|
||||
|
||||
nvrhi::ShaderHandle ps_rain_anim;
|
||||
nvrhi::ShaderHandle vs_rain_anim;
|
||||
nvrhi::ShaderHandle cs_max_depth;
|
||||
|
||||
nvrhi::BindingSetHandle bindings;
|
||||
nvrhi::BindingSetHandle bindings_max_depth;
|
||||
|
||||
nvrhi::GraphicsPipelineHandle pso_droplets;
|
||||
nvrhi::ComputePipelineHandle pso_max_depth;
|
||||
|
||||
nvrhi::BindingLayoutHandle binding_layout;
|
||||
|
||||
std::unordered_map<size_t, nvrhi::BindingSetHandle> binding_sets_per_material;
|
||||
};
|
||||
15
betterRenderer/shaders/cs_downsample_depth.hlsl
Normal file
15
betterRenderer/shaders/cs_downsample_depth.hlsl
Normal file
@@ -0,0 +1,15 @@
|
||||
Texture2D<float> g_DepthTexture : register(t0);
|
||||
RWTexture2D<float> g_Output : register(u0);
|
||||
sampler depth_sampler : register(s0);
|
||||
|
||||
[numthreads(8, 8, 1)]
|
||||
void main(uint3 PixCoord : SV_DispatchThreadID) {
|
||||
uint2 dimensions;
|
||||
g_Output.GetDimensions(dimensions.x, dimensions.y);
|
||||
|
||||
float2 co = float2(PixCoord.xy) / float2(dimensions);
|
||||
|
||||
float4 depths = g_DepthTexture.GatherRed(depth_sampler, co);
|
||||
g_Output[PixCoord.xy] = min(min(depths.x, depths.y), min(depths.z, depths.w));
|
||||
}
|
||||
|
||||
@@ -32,13 +32,19 @@ cbuffer VertexConstants : register(b0) {
|
||||
|
||||
#include "manul/draw_constants.hlsli"
|
||||
|
||||
#ifdef NO_JITTER
|
||||
#define PROJECTION g_Projection
|
||||
#else
|
||||
#define PROJECTION g_JitteredProjection
|
||||
#endif
|
||||
|
||||
VertexOutput main(in VertexInput vs_in) {
|
||||
VertexOutput result;
|
||||
float4x3 model_view = GetModelView();
|
||||
float4x3 model_view_history = GetModelViewHistory();
|
||||
float3 view_space_position = mul(float4(vs_in.m_Position, 1.), model_view).xyz;
|
||||
result.m_TexCoord = vs_in.m_TexCoord;
|
||||
result.m_PositionSV = mul(g_JitteredProjection, float4(view_space_position, 1.));
|
||||
result.m_PositionSV = mul(PROJECTION, float4(view_space_position, 1.));
|
||||
#ifndef PREPASS
|
||||
result.m_Normal = mul(float4(vs_in.m_Normal, 0.), model_view).xyz;
|
||||
result.m_Position = view_space_position;
|
||||
|
||||
@@ -48,6 +48,7 @@ struct PixelInput {
|
||||
#include "sky.hlsli"
|
||||
|
||||
Texture2D<float> g_GbufferDepth : register(t12);
|
||||
Texture2D<float3> g_LitScene : register(t13);
|
||||
#endif
|
||||
|
||||
void MaterialPass(inout MaterialData material);
|
||||
@@ -61,12 +62,14 @@ PixelOutput main(in PixelInput ps_in) {
|
||||
material.m_Tangent = ps_in.m_Tangent.xyz;
|
||||
material.m_Bitangent = ps_in.m_Tangent.w * cross(ps_in.m_Normal, ps_in.m_Tangent.xyz);
|
||||
material.m_TexCoord = ps_in.m_TexCoord;
|
||||
material.m_ScreenCoord = uv;
|
||||
material.m_PixelCoord = ps_in.m_PositionSV.xy;
|
||||
material.m_PositionNDC = ps_in.m_PositionCS / ps_in.m_PositionCS.w;
|
||||
material.m_MaterialAlbedoAlpha = float4(1., 1., 1., 1.);
|
||||
material.m_MaterialEmission = float3(0., 0., 0.);
|
||||
material.m_MaterialParams = float4(0., .5, 1., .5); // Metalness.Roughness.Occlusion.Specular
|
||||
material.m_MaterialNormal = float3(0., 0., 1.);
|
||||
material.m_RefractionOffset = float2(0., 0.);
|
||||
MaterialPass(material);
|
||||
material.m_MaterialAlbedoAlpha.rgb = saturate(material.m_MaterialAlbedoAlpha.rgb);
|
||||
material.m_MaterialEmission = max(material.m_MaterialEmission, 0.);
|
||||
@@ -95,6 +98,12 @@ PixelOutput main(in PixelInput ps_in) {
|
||||
ps_out.m_Motion = (ps_in.m_HistoryPositionCS.xy / ps_in.m_HistoryPositionCS.w) - (ps_in.m_PositionCS.xy / ps_in.m_PositionCS.w);
|
||||
ps_out.m_Motion = ps_out.m_Motion * float2(.5, -.5);
|
||||
#endif
|
||||
|
||||
#if (PASS & FORWARD_LIGHTING) && defined(REFRACTION)
|
||||
float3 scene_color = g_LitScene.Sample(g_SkySampler, material.m_ScreenCoord + material.m_RefractionOffset * float2(1., -1.));
|
||||
ps_out.m_Color.rgb += (1. - ps_out.m_Color.a) * scene_color;
|
||||
ps_out.m_Color.a = 1.;
|
||||
#endif
|
||||
|
||||
return ps_out;
|
||||
}
|
||||
|
||||
@@ -9,12 +9,14 @@ struct MaterialData {
|
||||
float3 m_Bitangent;
|
||||
float3 m_Normal;
|
||||
float2 m_TexCoord;
|
||||
float2 m_ScreenCoord;
|
||||
uint2 m_PixelCoord;
|
||||
float4 m_PositionNDC;
|
||||
float4 m_MaterialAlbedoAlpha;
|
||||
float3 m_MaterialEmission;
|
||||
float4 m_MaterialParams; // Metalness.Roughness.Occlusion.Specular
|
||||
float3 m_MaterialNormal;
|
||||
float2 m_RefractionOffset;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,7 @@ cbuffer DrawConstants : register(b2) {
|
||||
float4 g_WiperPos;
|
||||
float4 g_WiperTimerOut;
|
||||
float4 g_WiperTimerReturn;
|
||||
float g_VerticalFov;
|
||||
}
|
||||
|
||||
float2 PixelToCS(in float2 pixel, in float2 size) {
|
||||
|
||||
@@ -141,6 +141,10 @@ shaders:
|
||||
hint: color
|
||||
default: white
|
||||
no_filter: true
|
||||
rain:
|
||||
binding: 3
|
||||
hint: linear
|
||||
default: system/raindrops_buffer
|
||||
masked_shadow_texture: diffuse
|
||||
source: ps_windshield_rain
|
||||
utility:
|
||||
@@ -165,6 +169,14 @@ shaders:
|
||||
entrypoint: main
|
||||
definitions:
|
||||
PREPASS: 1
|
||||
windshield_rain_anim:
|
||||
source: ps_windshield_rain_anim
|
||||
target: pixel
|
||||
entrypoint: main
|
||||
max_depth_4x4:
|
||||
source: cs_downsample_depth
|
||||
target: compute
|
||||
entrypoint: main
|
||||
# Contact shadows
|
||||
# TODO Depth conversion is broken since converting to reversed depth buffer
|
||||
contact_shadows:
|
||||
@@ -181,6 +193,12 @@ shaders:
|
||||
source: default_vertex
|
||||
target: vertex
|
||||
entrypoint: main
|
||||
default_vertex_no_jitter:
|
||||
source: default_vertex
|
||||
target: vertex
|
||||
entrypoint: main
|
||||
definitions:
|
||||
NO_JITTER: 1
|
||||
default_prepass_vertex:
|
||||
source: default_vertex
|
||||
target: vertex
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#define REFRACTION 1
|
||||
|
||||
#include "manul/math.hlsli"
|
||||
#include "manul/material.hlsli"
|
||||
#include "manul/color_transform.hlsli"
|
||||
#include "manul/random.hlsli"
|
||||
|
||||
sampler diffuse_sampler : register(s0);
|
||||
sampler raindrop_sampler : register(s1);
|
||||
@@ -9,163 +10,76 @@ sampler wipermask_sampler : register(s2);
|
||||
Texture2D<float4> diffuse : register(t0);
|
||||
Texture2D<float4> raindropsatlas : register(t1);
|
||||
Texture2D<float4> wipermask : register(t2);
|
||||
Texture2D<float> rain : register(t3);
|
||||
|
||||
float4 getDropTex(float choice, float2 uv) {
|
||||
float2 offset;
|
||||
if (choice < .25) offset = float2(0.0, 0.0);
|
||||
else if (choice < .5) offset = float2(0.5, 0.0);
|
||||
else if (choice < .75) offset = float2(0.0, 0.5);
|
||||
else offset = float2(0.5, 0.5);
|
||||
return raindropsatlas.Sample(raindrop_sampler, offset + uv * 0.5);
|
||||
// Project the surface gradient (dhdx, dhdy) onto the surface (n, dpdx, dpdy)
|
||||
float3 CalculateSurfaceGradient(float3 n, float3 dpdx, float3 dpdy, float dhdx, float dhdy)
|
||||
{
|
||||
float3 r1 = cross(dpdy, n);
|
||||
float3 r2 = cross(n, dpdx);
|
||||
|
||||
return (r1 * dhdx + r2 * dhdy) / dot(dpdx, r1);
|
||||
}
|
||||
|
||||
float GetMixFactor(in float2 co, out float side);
|
||||
// Move the normal away from the surface normal in the opposite surface gradient direction
|
||||
float3 PerturbNormal(float3 n, float3 dpdx, float3 dpdy, float dhdx, float dhdy)
|
||||
{
|
||||
return normalize(n - CalculateSurfaceGradient(n, dpdx, dpdy, dhdx, dhdy));
|
||||
}
|
||||
|
||||
// Calculate the surface normal using screen-space partial derivatives of the height field
|
||||
float3 CalculateSurfaceNormal(float3 position, float3 normal, float2 gradient)
|
||||
{
|
||||
float3 dpdx = ddx(position);
|
||||
float3 dpdy = ddy(position);
|
||||
|
||||
float dhdx = gradient.x;
|
||||
float dhdy = gradient.y;
|
||||
|
||||
return PerturbNormal(normal, dpdx, dpdy, dhdx, dhdy);
|
||||
}
|
||||
|
||||
void MaterialPass(inout MaterialData material) {
|
||||
#if PASS & FORWARD_LIGHTING
|
||||
const float specular_intensity = 1.;
|
||||
const float wobble_strength = .002;
|
||||
const float wobble_speed = 30.;
|
||||
|
||||
MaterialData material_glass = material;
|
||||
float4 tex_color = diffuse.Sample(diffuse_sampler, material.m_TexCoord);
|
||||
if (tex_color.a < .01) discard;
|
||||
uint2 size;
|
||||
rain.GetDimensions(size.x, size.y);
|
||||
float droplet_distance = rain.Sample(raindrop_sampler, material.m_ScreenCoord);
|
||||
float droplet_distance_x = rain.Sample(raindrop_sampler, material.m_ScreenCoord, int2(1, 0));
|
||||
float droplet_distance_y = rain.Sample(raindrop_sampler, material.m_ScreenCoord, int2(0, 1));
|
||||
float2 gradient = float2(droplet_distance_x - droplet_distance, droplet_distance_y - droplet_distance);
|
||||
material_glass.m_MaterialAlbedoAlpha.xyz = 0.;
|
||||
material_glass.m_MaterialNormal = material.m_Normal;
|
||||
material_glass.m_MaterialParams.g = .2;
|
||||
float3 normal = CalculateSurfaceNormal(material_glass.m_Position, material_glass.m_Normal, gradient * -.005);
|
||||
material_glass.m_MaterialNormal = normal;
|
||||
float cosTheta = saturate(dot(-normalize(material_glass.m_Position), normal));
|
||||
material.m_MaterialAlbedoAlpha.a = lerp(.1, FresnelSchlickRoughness(cosTheta, .04, 0.), smoothstep(0., .15, droplet_distance));
|
||||
|
||||
float2 rainCoord = material.m_TexCoord;
|
||||
float gridSize = ceil(200.);
|
||||
float3 normal_world = mul((float3x3)g_InverseModelView, material_glass.m_MaterialNormal);
|
||||
|
||||
const float numDrops = 20000.;
|
||||
const float cycleDuration = 4.;
|
||||
float4 glass_lit;
|
||||
ApplyMaterialLighting(glass_lit, material_glass, material_glass.m_PixelCoord);
|
||||
|
||||
float squareMin = .5 / gridSize;
|
||||
float squareMax = 1.2 / gridSize;
|
||||
material.m_MaterialEmission = glass_lit * smoothstep(0., .15, droplet_distance) * saturate(normal_world.y * .5 + .5);
|
||||
|
||||
float2 cell = floor(rainCoord * gridSize);
|
||||
material.m_MaterialAlbedoAlpha.xyz = 0.;
|
||||
material.m_RefractionOffset = normal.xy * (.005 / (length(material.m_Position) * tan(.5 * g_VerticalFov))) * smoothstep(0., .15, droplet_distance);
|
||||
|
||||
float3 dropLayer = 0.;
|
||||
float dropMaskSum = 0.;
|
||||
float glass_opacity = FresnelSchlickRoughness(saturate(dot(-normalize(material.m_Position), material.m_Normal)), .2, 0.);
|
||||
material.m_MaterialEmission = lerp(material.m_MaterialEmission, 0., glass_opacity);
|
||||
material.m_MaterialAlbedoAlpha.a = lerp(material.m_MaterialAlbedoAlpha.a, 1., glass_opacity);
|
||||
material.m_MaterialParams.g = .05;
|
||||
material.m_MaterialNormal = material.m_Normal;
|
||||
|
||||
// Grid of 9 droplets in immediate neighbourhood
|
||||
[unroll]
|
||||
for (int oy = -1; oy <= 1; ++oy) {
|
||||
|
||||
[unroll]
|
||||
for (int ox = -1; ox <= 1; ++ox) {
|
||||
|
||||
float2 neighborCell = cell + float2(ox, oy);
|
||||
float2 neighborCenter = (neighborCell + .5) / gridSize;
|
||||
|
||||
float side;
|
||||
float mixFactor = GetMixFactor(neighborCenter, side);
|
||||
|
||||
uint seed = Hash(uint3(neighborCell, side));
|
||||
|
||||
if(mixFactor < RandF(seed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Show a percentage of droplets given by rain intensity param
|
||||
float activationSeed = RandF(seed);
|
||||
if (activationSeed > g_RainParams.x)
|
||||
continue; // kropla nieaktywna
|
||||
|
||||
// Randomly modulate droplet center & size
|
||||
float2 dropCenter = (neighborCell + float2(RandF(seed), RandF(seed))) / gridSize;
|
||||
float squareSize = lerp(squareMin, squareMax, RandF(seed));
|
||||
|
||||
float lifeTime = g_Time + RandF(seed) * cycleDuration;
|
||||
float phase = frac(lifeTime / cycleDuration);
|
||||
float active = saturate(1. - phase);
|
||||
|
||||
// Gravity influence (TODO add vehicle speed & wind here!)
|
||||
float gravityStart = .5;
|
||||
float gravityPhase = smoothstep(gravityStart, 1., phase);
|
||||
float dropMass = lerp(.3, 1.2, RandF(seed));
|
||||
float gravitySpeed = .15 * dropMass;
|
||||
float2 gravityOffset = float2(0., gravityPhase * gravitySpeed * phase);
|
||||
|
||||
// Random wobble
|
||||
bool hasWobble = (RandF(seed) < .10);
|
||||
float2 wobbleOffset = 0.;
|
||||
if (hasWobble && gravityPhase > 0.) {
|
||||
float intensity = sin(g_Time * wobble_speed + RandF(seed) * 100.) * wobble_strength * gravityPhase;
|
||||
wobbleOffset = float2(intensity, 0.);
|
||||
}
|
||||
|
||||
float2 slideOffset = gravityOffset + wobbleOffset;
|
||||
|
||||
// Flatten droplets influenced by gravity
|
||||
float flattenAmount = smoothstep(0.1, 0.5, gravityPhase);
|
||||
float flattenX = lerp(1.0, 0.4, flattenAmount);
|
||||
float stretchY = lerp(1.0, 1.6, flattenAmount);
|
||||
|
||||
// Droplet local position & mask
|
||||
float2 diff = (rainCoord + slideOffset) - dropCenter;
|
||||
diff.x *= 1.0 / flattenX;
|
||||
diff.y *= 1.0 / stretchY;
|
||||
float mask = smoothstep(squareSize * 0.5, squareSize * 0.45, max(abs(diff.x), abs(diff.y)));
|
||||
|
||||
if (mask > .001) {
|
||||
float2 localUV = (diff + squareSize * 0.5) / squareSize;
|
||||
float choice = RandF(seed);
|
||||
float4 dropTex = getDropTex(choice, localUV);
|
||||
float sharpAlpha = smoothstep(0.3, 0.9, dropTex.a);
|
||||
|
||||
float colorLuma = length(dropTex.rgb);
|
||||
float alphaRange = smoothstep(0.1, 0.3, colorLuma);
|
||||
float blackAlpha = lerp(0.25, 0.85, alphaRange);
|
||||
|
||||
dropLayer += dropTex.rgb * sharpAlpha * active * blackAlpha * mask;
|
||||
dropMaskSum += sharpAlpha * active * blackAlpha * mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
float3 finalMix = dropLayer;
|
||||
float alphaOut = clamp(dropMaskSum, 0.0, 1.0);
|
||||
material.m_MaterialAlbedoAlpha = float4(finalMix, alphaOut);
|
||||
|
||||
{ // Overlay windshield texture with alpha
|
||||
material.m_MaterialAlbedoAlpha.xyz = lerp(material.m_MaterialAlbedoAlpha.xyz, tex_color.xyz, tex_color.a);
|
||||
material.m_MaterialAlbedoAlpha.a = lerp(material.m_MaterialAlbedoAlpha.a, 1., tex_color.a);
|
||||
material.m_MaterialEmission.xyz = lerp(material.m_MaterialEmission.xyz, 0., tex_color.a);
|
||||
material.m_MaterialParams.g = lerp(material.m_MaterialParams.g, float4(0., .5, 1., .5), tex_color.a);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PASS & FORWARD_LIGHTING
|
||||
float GetMixFactor(in float2 co, out float side) {
|
||||
float4 movePhase = g_WiperPos;
|
||||
bool4 is_out = movePhase <= 1.;
|
||||
movePhase = select(is_out, movePhase, 2. - movePhase);
|
||||
|
||||
float4 mask = wipermask.Sample(wipermask_sampler, co);
|
||||
|
||||
float4 areaMask = step(.001, mask);
|
||||
|
||||
float4 maskVal = select(is_out, mask, 1. - mask);
|
||||
float4 wipeWidth = smoothstep(1., .9, movePhase) * .25;
|
||||
float4 cleaned = smoothstep(movePhase - wipeWidth, movePhase, maskVal) * areaMask;
|
||||
float4 side_v = step(maskVal, movePhase);
|
||||
cleaned *= side_v;
|
||||
side_v = select(is_out, 1. - side_v, side_v);
|
||||
|
||||
// "regeneration", raindrops gradually returning after wiper pass:
|
||||
float4 regenPhase = saturate((g_Time - lerp(g_WiperTimerOut, g_WiperTimerReturn, side_v) - .2) / g_RainParams.y);
|
||||
|
||||
side_v = lerp(0., 1. - side_v, areaMask);
|
||||
|
||||
float4 factor_v = lerp(1., regenPhase * (1. - cleaned), areaMask);
|
||||
|
||||
side = 0.;
|
||||
float out_factor = 1.;
|
||||
|
||||
// Find out the wiper blade that influences given grid cell the most
|
||||
[unroll]
|
||||
for(int i = 0; i < 4; ++i)
|
||||
{
|
||||
bool is_candidate = factor_v[i] < out_factor;
|
||||
out_factor = select(is_candidate, factor_v[i], out_factor);
|
||||
side = select(is_candidate, side_v[i], side);
|
||||
}
|
||||
|
||||
return out_factor;
|
||||
}
|
||||
#endif
|
||||
|
||||
162
betterRenderer/shaders/ps_windshield_rain_anim.hlsl
Normal file
162
betterRenderer/shaders/ps_windshield_rain_anim.hlsl
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "manul/draw_constants.hlsli"
|
||||
#include "manul/random.hlsli"
|
||||
#include "manul/view_data.hlsli"
|
||||
|
||||
struct PixelInput {
|
||||
float3 m_Position : Position;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
float4 m_PositionCS : PositionCS;
|
||||
};
|
||||
|
||||
sampler raindrop_sampler : register(s0);
|
||||
sampler wipermask_sampler : register(s1);
|
||||
Texture2D<float> raindropsatlas : register(t0);
|
||||
Texture2D<float4> wipermask : register(t1);
|
||||
|
||||
float getDropTex(float choice, float2 uv) {
|
||||
float2 offset;
|
||||
if (choice < .25) offset = float2(0.0, 0.0);
|
||||
else if (choice < .5) offset = float2(0.5, 0.0);
|
||||
else if (choice < .75) offset = float2(0.0, 0.5);
|
||||
else offset = float2(0.5, 0.5);
|
||||
return raindropsatlas.Sample(raindrop_sampler, offset + uv * 0.5);
|
||||
}
|
||||
|
||||
float GetMixFactor(in float2 co, out float side);
|
||||
|
||||
float main(in PixelInput input) : SV_Target0 {
|
||||
const float specular_intensity = 1.;
|
||||
const float wobble_strength = .002;
|
||||
const float wobble_speed = 30.;
|
||||
|
||||
//float4 tex_color = diffuse.Sample(diffuse_sampler, material.m_TexCoord);
|
||||
//if (tex_color.a < .01) discard;
|
||||
|
||||
float2 rainCoord = input.m_TexCoord;
|
||||
float gridSize = ceil(200.);
|
||||
|
||||
const float numDrops = 20000.;
|
||||
const float cycleDuration = 4.;
|
||||
|
||||
float squareMin = 1. / gridSize;
|
||||
float squareMax = 2.5 / gridSize;
|
||||
|
||||
float2 cell = floor(rainCoord * gridSize);
|
||||
|
||||
float3 dropLayer = 0.;
|
||||
float dropMaskSum = 0.;
|
||||
|
||||
float output = 0.;
|
||||
|
||||
// Grid of 9 droplets in immediate neighbourhood
|
||||
[unroll]
|
||||
for (int oy = -1; oy <= 1; ++oy) {
|
||||
|
||||
[unroll]
|
||||
for (int ox = -1; ox <= 1; ++ox) {
|
||||
|
||||
float2 neighborCell = cell + float2(ox, oy);
|
||||
float2 neighborCenter = (neighborCell + .5) / gridSize;
|
||||
|
||||
float side;
|
||||
float mixFactor = GetMixFactor(neighborCenter, side);
|
||||
|
||||
uint seed = Hash(uint3(neighborCell, side));
|
||||
|
||||
if(mixFactor < RandF(seed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Show a percentage of droplets given by rain intensity param
|
||||
float activationSeed = RandF(seed);
|
||||
if (activationSeed > g_RainParams.x)
|
||||
continue; // kropla nieaktywna
|
||||
|
||||
// Randomly modulate droplet center & size
|
||||
float2 dropCenter = (neighborCell + float2(RandF(seed), RandF(seed))) / gridSize;
|
||||
float squareSize = lerp(squareMin, squareMax, RandF(seed));
|
||||
|
||||
float lifeTime = g_Time + RandF(seed) * cycleDuration;
|
||||
float phase = frac(lifeTime / cycleDuration);
|
||||
float active = saturate(1. - phase);
|
||||
|
||||
// Gravity influence (TODO add vehicle speed & wind here!)
|
||||
float gravityStart = .5;
|
||||
float gravityPhase = smoothstep(gravityStart, 1., phase);
|
||||
float dropMass = lerp(.3, 1.2, RandF(seed));
|
||||
float gravitySpeed = .15 * dropMass;
|
||||
float2 gravityOffset = float2(0., gravityPhase * gravitySpeed * phase);
|
||||
|
||||
// Random wobble
|
||||
bool hasWobble = (RandF(seed) < .10);
|
||||
float2 wobbleOffset = 0.;
|
||||
if (hasWobble && gravityPhase > 0.) {
|
||||
float intensity = sin(g_Time * wobble_speed + RandF(seed) * 100.) * wobble_strength * gravityPhase;
|
||||
wobbleOffset = float2(intensity, 0.);
|
||||
}
|
||||
|
||||
float2 slideOffset = gravityOffset + wobbleOffset;
|
||||
|
||||
// Flatten droplets influenced by gravity
|
||||
float flattenAmount = smoothstep(0.1, 0.5, gravityPhase);
|
||||
float flattenX = lerp(1.0, 0.4, flattenAmount);
|
||||
float stretchY = lerp(1.0, 1.6, flattenAmount);
|
||||
|
||||
// Droplet local position & mask
|
||||
float2 diff = (rainCoord + slideOffset) - dropCenter;
|
||||
diff.x *= 1.0 / flattenX;
|
||||
diff.y *= 1.0 / stretchY;
|
||||
float mask = smoothstep(squareSize * 0.5, squareSize * 0.45, max(abs(diff.x), abs(diff.y)));
|
||||
|
||||
if (mask > .001) {
|
||||
float2 localUV = (diff + squareSize * 0.5) / squareSize;
|
||||
float choice = RandF(seed);
|
||||
float dropTex = getDropTex(choice, localUV) * mask;
|
||||
output = max(output, dropTex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float GetMixFactor(in float2 co, out float side) {
|
||||
float4 movePhase = g_WiperPos;
|
||||
bool4 is_out = movePhase <= 1.;
|
||||
movePhase = select(is_out, movePhase, 2. - movePhase);
|
||||
|
||||
float4 mask = wipermask.Sample(wipermask_sampler, co);
|
||||
|
||||
float4 areaMask = step(.001, mask);
|
||||
|
||||
float4 maskVal = select(is_out, mask, 1. - mask);
|
||||
float4 wipeWidth = smoothstep(1., .9, movePhase) * .25;
|
||||
float4 cleaned = smoothstep(movePhase - wipeWidth, movePhase, maskVal) * areaMask;
|
||||
float4 side_v = step(maskVal, movePhase);
|
||||
cleaned *= side_v;
|
||||
side_v = select(is_out, 1. - side_v, side_v);
|
||||
|
||||
// "regeneration", raindrops gradually returning after wiper pass:
|
||||
float4 regenPhase = saturate((g_Time - lerp(g_WiperTimerOut, g_WiperTimerReturn, side_v) - .2) / g_RainParams.y);
|
||||
|
||||
side_v = lerp(0., 1. - side_v, areaMask);
|
||||
|
||||
float4 factor_v = lerp(1., regenPhase * (1. - cleaned), areaMask);
|
||||
|
||||
side = 0.;
|
||||
float out_factor = 1.;
|
||||
|
||||
// Find out the wiper blade that influences given grid cell the most
|
||||
[unroll]
|
||||
for(int i = 0; i < 4; ++i)
|
||||
{
|
||||
bool is_candidate = factor_v[i] < out_factor;
|
||||
out_factor = select(is_candidate, factor_v[i], out_factor);
|
||||
side = select(is_candidate, side_v[i], side);
|
||||
}
|
||||
|
||||
return out_factor;
|
||||
}
|
||||
Reference in New Issue
Block a user