diff --git a/Model3d.cpp b/Model3d.cpp index ee367f6c..b732fb7f 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1866,7 +1866,8 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) { uint32_t type = sn_utils::ld_uint32(s); uint32_t size = sn_utils::ld_uint32(s) - 8; - std::streampos end = s.tellg() + (std::streampos)size; + size_t pos = s.tellg(); + std::streampos end = pos + (std::streampos)size; if ((type & 0x00FFFFFF) == MAKE_ID4('S', 'U', 'B', 0)) // submodels { @@ -1877,7 +1878,6 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) size_t sm_cnt = size / sm_size; iSubModelsCount = (int)sm_cnt; Root = new TSubModel[sm_cnt]; - size_t pos = s.tellg(); for (size_t i = 0; i < sm_cnt; ++i) { s.seekg(pos + sm_size * i); @@ -1906,7 +1906,31 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) size_t const vertextype { ( ( ( type & 0xFF000000 ) >> 24 ) - '0' ) }; hastangents = ( (vertextype & 3) > 0 ); hasuserdata = (vertextype & 4); + size_t vertex_size = 0; + switch (vertextype & 3) + { + case 0: + case 2: + { + vertex_size = 8; + if(hastangents) + vertex_size += 4; + if(hasuserdata) + vertex_size += 4; + break; + } + case 1: + { + vertex_size = 4; + if(hastangents) + vertex_size += 1; + if (hasuserdata) + vertex_size += 2; + break; + } + } for( auto const &submodeloffset : submodeloffsets ) { + s.seekg(pos + submodeloffset.first * vertex_size * sizeof(float)); // Ensure that we start reading from the correct offset even if geometry is not encoded contiguously auto &submodel { Root[ submodeloffset.second ] }; auto const &submodelgeometry { submodel.m_geometry }; submodel.Vertices.resize( submodelgeometry.vertex_count ); @@ -1978,6 +2002,7 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) // once sorted we can grab indices in a continuous read, and assign them to the chunks they belong to size_t const indexsize { ( ( ( type & 0xFF000000 ) >> 24 ) - '0' ) }; for( auto const &submodeloffset : submodeloffsets ) { + s.seekg(pos + submodeloffset.first * indexsize); auto &submodel { Root[ submodeloffset.second ] }; auto const &submodelgeometry { submodel.m_geometry }; submodel.Indices.resize( submodelgeometry.index_count ); diff --git a/Timer.h b/Timer.h index b0f1870d..5ac9cc3c 100644 --- a/Timer.h +++ b/Timer.h @@ -58,6 +58,7 @@ struct subsystem_stopwatches { stopwatch gfx_reflections; stopwatch gfx_swap; stopwatch gfx_gui; + stopwatch gfx_animate; stopwatch sim_total; stopwatch sim_dynamics; stopwatch sim_events; diff --git a/betterRenderer/mashadercompiler/src/shader_compiler.cpp b/betterRenderer/mashadercompiler/src/shader_compiler.cpp index 3424cf24..42cc96e5 100644 --- a/betterRenderer/mashadercompiler/src/shader_compiler.cpp +++ b/betterRenderer/mashadercompiler/src/shader_compiler.cpp @@ -274,6 +274,11 @@ bool MaShaderCompiler::CompileMaterial(YAML::Node &dest, const YAML::Node &src, local_definitions.insert(local_definitions.end(), definitions.begin(), definitions.end()); + if (pass == MaterialRenderPass::Forward && + TemplateOverride("refraction", src, templates).as(false)) { + local_definitions.emplace_back(L"REFRACTION", L"1"); + } + auto blob = CompileShaderToBlob(file_name, ToWide(entry_name), local_definitions, target, platform); diff --git a/betterRenderer/renderer/include/nvrenderer/nvrenderer.h b/betterRenderer/renderer/include/nvrenderer/nvrenderer.h index 9cded346..80a86d95 100644 --- a/betterRenderer/renderer/include/nvrenderer/nvrenderer.h +++ b/betterRenderer/renderer/include/nvrenderer/nvrenderer.h @@ -190,6 +190,7 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry { std::shared_ptr m_bloom; std::shared_ptr m_sky; std::shared_ptr m_auto_exposure; + std::shared_ptr m_windshield_rain; std::unordered_map> rt_models; std::shared_ptr GetRtModel(TModel3d const *); @@ -424,6 +425,7 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry { float m_opacity; float m_selfillum; glm::vec3 m_diffuse; + std::string_view m_name = ""; }; struct SpotLight { glm::vec3 m_color; @@ -497,8 +499,12 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry { std::string m_name; std::string m_sampler_name; int m_hint; + bool disable_anisotropy = false; + bool disable_filter = false; + bool disable_mip_bias = false; texture_handle m_default_texture; }; + bool m_enable_refraction = false; nvrhi::static_vector m_texture_bindings; std::array m_pipelines; @@ -610,16 +616,21 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry { nvrhi::BufferHandle m_cubedrawconstant_buffer; nvrhi::BufferHandle m_drawconstant_buffer; std::array m_vertex_shader; + std::array m_vertex_shader_prepass; std::array m_vertex_shader_cubemap; std::array m_vertex_shader_shadow; nvrhi::BindingLayoutHandle m_binding_layout_shadow_masked; + nvrhi::BindingLayoutHandle m_binding_layout_prepass; nvrhi::ShaderHandle m_pixel_shader_shadow_masked; + nvrhi::ShaderHandle m_pixel_shader_prepass_masked; std::array - m_input_layout; + m_input_layout; std::array m_pso_shadow; + std::array + m_pso_prepass; nvrhi::GraphicsPipelineHandle m_pso_line; std::condition_variable m_cv_next_frame; @@ -689,7 +700,7 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry { bool BindMaterial(material_handle handle, DrawType draw_type, const RenderPass &pass, nvrhi::GraphicsState &gfx_state, - float &alpha_threshold); + float &alpha_threshold, bool *out_is_refractive = nullptr); bool BindLineMaterial(DrawType draw_type, const RenderPass &pass, nvrhi::GraphicsState &gfx_state); diff --git a/betterRenderer/renderer/include/nvrenderer/nvrenderer_enums.h b/betterRenderer/renderer/include/nvrenderer/nvrenderer_enums.h index ac8d6e74..d78d2e67 100644 --- a/betterRenderer/renderer/include/nvrenderer/nvrenderer_enums.h +++ b/betterRenderer/renderer/include/nvrenderer/nvrenderer_enums.h @@ -3,6 +3,7 @@ namespace RendererEnums { enum class RenderPassType { + DepthOnly, Deferred, Forward, CubeMap, diff --git a/betterRenderer/renderer/source/XeGTAO.h b/betterRenderer/renderer/source/XeGTAO.h index 2abb5347..63caa4f6 100644 --- a/betterRenderer/renderer/source/XeGTAO.h +++ b/betterRenderer/renderer/source/XeGTAO.h @@ -161,21 +161,21 @@ namespace XeGTAO template inline T clamp( T const & v, T const & min, T const & max ) { assert( max >= min ); if( v < min ) return min; if( v > max ) return max; return v; } // If using TAA then set noiseIndex to frameIndex % 64 - otherwise use 0 - inline void GTAOUpdateConstants( XeGTAO::GTAOConstants& consts, int viewportWidth, int viewportHeight, const XeGTAO::GTAOSettings & settings, const float projMatrix[16], bool rowMajor, unsigned int frameCounter ) + inline void GTAOUpdateConstants( XeGTAO::GTAOConstants& consts, int viewportWidth, int viewportHeight, const XeGTAO::GTAOSettings & settings, const glm::mat4& projMatrix, bool rowMajor, unsigned int frameCounter ) { consts.ViewportSize = { viewportWidth, viewportHeight }; consts.ViewportPixelSize = { 1.0f / (float)viewportWidth, 1.0f / (float)viewportHeight }; - float depthLinearizeMul = (rowMajor)?(-projMatrix[3 * 4 + 2]):(-projMatrix[3 + 2 * 4]); // float depthLinearizeMul = ( clipFar * clipNear ) / ( clipFar - clipNear ); - float depthLinearizeAdd = (rowMajor)?( projMatrix[2 * 4 + 2]):( projMatrix[2 + 2 * 4]); // float depthLinearizeAdd = clipFar / ( clipFar - clipNear ); + float depthLinearizeMul = -projMatrix[3][2]; // float depthLinearizeMul = ( clipFar * clipNear ) / ( clipFar - clipNear ); + float depthLinearizeAdd = -projMatrix[2][2]; // float depthLinearizeAdd = clipFar / ( clipFar - clipNear ); // correct the handedness issue. need to make sure this below is correct, but I think it is. if( depthLinearizeMul * depthLinearizeAdd < 0 ) depthLinearizeAdd = -depthLinearizeAdd; - consts.DepthUnpackConsts = { -depthLinearizeMul, depthLinearizeAdd }; + consts.DepthUnpackConsts = { depthLinearizeMul, depthLinearizeAdd }; - float tanHalfFOVY = 1.0f / ((rowMajor)?(projMatrix[1 * 4 + 1]):(projMatrix[1 + 1 * 4])); // = tanf( drawContext.Camera.GetYFOV( ) * 0.5f ); - float tanHalfFOVX = 1.0F / ((rowMajor)?(projMatrix[0 * 4 + 0]):(projMatrix[0 + 0 * 4])); // = tanHalfFOVY * drawContext.Camera.GetAspect( ); + float tanHalfFOVY = 1.0f / projMatrix[1][1]; // = tanf( drawContext.Camera.GetYFOV( ) * 0.5f ); + float tanHalfFOVX = 1.0F / projMatrix[0][0]; // = tanHalfFOVY * drawContext.Camera.GetAspect( ); consts.CameraTanHalfFOV = { tanHalfFOVX, tanHalfFOVY }; consts.NDCToViewMul = { consts.CameraTanHalfFOV.x * 2.0f, consts.CameraTanHalfFOV.y * -2.0f }; diff --git a/betterRenderer/renderer/source/gbufferblitpass.cpp b/betterRenderer/renderer/source/gbufferblitpass.cpp index 8debef9a..42898ec5 100644 --- a/betterRenderer/renderer/source/gbufferblitpass.cpp +++ b/betterRenderer/renderer/source/gbufferblitpass.cpp @@ -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)) @@ -183,14 +192,65 @@ void GbufferBlitPass::UpdateConstants(nvrhi::ICommandList* command_list, const auto& daylight = Global.DayLight; - m_sky->CalcLighting(constants.m_light_dir, - constants.m_light_color); + m_sky->CalcLighting(constants.m_light_dir, 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.); + constants.m_rain_params.x = + percipitation_intensity; // % amount of droplets + constants.m_rain_params.y = + glm::mix(15., 1., percipitation_intensity); // Regeneration time + static glm::vec4 wiper_timer_out; + static glm::vec4 wiper_timer_return; + if (TDynamicObject const* owner = Global.pCamera.m_owner; + owner && !!owner->MoverParameters->CabActive) { + for (int i = 0; i < 4; ++i) { + if (i < owner->dWiperPos.size()) { + int index = owner->MoverParameters->CabActive > 0 + ? i + : static_cast(owner->dWiperPos.size() - 1) - i; + constants.m_wiper_pos[i] = owner->dWiperPos[index]; + if (owner->dWiperPos[index] > 0. && owner->wiperDirection[index]) { + constants.m_wiper_pos[i] += 1.; + } + if (owner->dWiperPos[index] < .025) { + wiper_timer_out[i] = constants.m_time; + } + if (owner->dWiperPos[index] > .975) { + wiper_timer_return[i] = constants.m_time; + } + constants.m_wiper_timer_out[i] = wiper_timer_out[i]; + constants.m_wiper_timer_return[i] = wiper_timer_return[i]; + } else { + constants.m_wiper_pos[i] = 0.; + wiper_timer_out[i] = constants.m_wiper_timer_out[i] = -1000.; + wiper_timer_return[i] = constants.m_wiper_timer_return[i] = -1000.; + } + } + } else { + constants.m_wiper_pos = glm::vec4{0.}; + wiper_timer_out = constants.m_wiper_timer_out = glm::vec4{-1000.}; + wiper_timer_return = constants.m_wiper_timer_return = glm::vec4{-1000.}; + } + } command_list->writeBuffer(m_draw_constants, &constants, sizeof(constants)); } +void GbufferBlitPass::UpdateSceneColorForRefraction( + nvrhi::ICommandList* command_list) const { + command_list->copyTexture( + m_output_copy, nvrhi::TextureSlice().resolve(m_output_copy->getDesc()), + m_output, nvrhi::TextureSlice().resolve(m_output->getDesc())); + command_list->setTextureState(m_output, nvrhi::AllSubresources, + nvrhi::ResourceStates::RenderTarget); + command_list->commitBarriers(); +} + void GbufferBlitPass::Render(nvrhi::ICommandList* command_list, glm::dmat4& view, const glm::dmat4& projection) { UpdateConstants(command_list, view, projection); diff --git a/betterRenderer/renderer/source/gbufferblitpass.h b/betterRenderer/renderer/source/gbufferblitpass.h index 3504f827..c3e1b392 100644 --- a/betterRenderer/renderer/source/gbufferblitpass.h +++ b/betterRenderer/renderer/source/gbufferblitpass.h @@ -18,17 +18,24 @@ struct GbufferBlitPass : public FullScreenPass, public MaResourceRegistry { void UpdateConstants(nvrhi::ICommandList* command_list, glm::dmat4& view, const glm::dmat4& projection); + void UpdateSceneColorForRefraction(nvrhi::ICommandList *command_list) const; + void Render(nvrhi::ICommandList* command_list, glm::dmat4& view, 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; float m_altitude; glm::vec3 m_light_color; float m_time; + glm::vec4 m_rain_params; + 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; @@ -49,5 +56,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; }; \ No newline at end of file diff --git a/betterRenderer/renderer/source/materialparser.cpp b/betterRenderer/renderer/source/materialparser.cpp index 6668fbf2..ed949bac 100644 --- a/betterRenderer/renderer/source/materialparser.cpp +++ b/betterRenderer/renderer/source/materialparser.cpp @@ -133,6 +133,9 @@ std::string_view MaterialAdapterLegacyMatFile::GetShader() const { if (m_shader == "water") { return "legacy_water"; } + if (m_shader == "rain_windscreen") { + return "windshield_rain"; + } if (IsSpecGlossShader() && HasSpecGlossMap()) { if (IsNormalMapShader() && HasNormalMap()) { return "legacy_normalmap_specgloss"; diff --git a/betterRenderer/renderer/source/nvmaterial.cpp b/betterRenderer/renderer/source/nvmaterial.cpp index ec611fe0..434b3a06 100644 --- a/betterRenderer/renderer/source/nvmaterial.cpp +++ b/betterRenderer/renderer/source/nvmaterial.cpp @@ -94,6 +94,10 @@ nvrhi::IGraphicsPipeline *NvRenderer::MaterialTemplate::GetPipeline( return m_renderer->m_pso_shadow[Constants::GetDefaultShadowPipelineIndex( draw_type, alpha_masked)]; } + if (pass_type == RenderPassType::DepthOnly) { + return m_renderer->m_pso_prepass[Constants::GetDefaultShadowPipelineIndex( + draw_type, alpha_masked)]; + } return nullptr; } @@ -105,22 +109,31 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) { MaResourceMapping sampler_mapping_shadow{}; m_pipelines.fill(nullptr); m_masked_shadow_texture = conf["masked_shadow_texture"].as(); + m_enable_refraction = conf["refraction"].as(false); for (const auto it : conf["textures"]) { auto index = it.second["binding"].as(); m_texture_bindings.resize( glm::max(m_texture_bindings.size(), static_cast(index) + 1)); - auto &[name, sampler_name, hint, default_texture] = - m_texture_bindings[index]; + auto &binding = m_texture_bindings[index]; - name = it.first.as(); - sampler_name = fmt::format("{:s}_sampler", name.c_str()); + binding.m_name = it.first.as(); + binding.m_sampler_name = fmt::format("{:s}_sampler", binding.m_name.c_str()); + + binding.disable_anisotropy = it.second["no_anisotropy"].as(false); + binding.disable_filter = it.second["no_filter"].as(false); + binding.disable_mip_bias = it.second["no_mip_bias"].as(false); + size_t default_texture = m_renderer->GetTextureManager()->FetchTexture( + it.second["default"].as(""), binding.m_hint, 0, false); + if (!default_texture) { + default_texture = 1; + } texture_mappings.emplace_back( - MaResourceMapping::Texture_SRV(index, name.c_str())); + MaResourceMapping::Texture_SRV(index, binding.m_name.c_str())); sampler_mappings.emplace_back( - MaResourceMapping::Sampler(index, sampler_name.c_str())); + MaResourceMapping::Sampler(index, binding.m_sampler_name.c_str())); - RegisterTexture(name.c_str(), 1); + RegisterTexture(binding.m_name.c_str(), default_texture); RegisterResource( false, "masked_shadow_sampler", GetTextureManager()->GetSamplerForTraits(0, RenderPassType::ShadowMap), @@ -128,10 +141,10 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) { const static std::unordered_map hints{ {"color", GL_SRGB_ALPHA}, {"linear", GL_RGBA}, {"normalmap", GL_RG}}; - hint = hints.at(it.second["hint"].as()); + binding.m_hint = hints.at(it.second["hint"].as()); if (it.first.Scalar() == conf["masked_shadow_texture"].Scalar()) { - texture_mapping_shadow = MaResourceMapping::Texture_SRV(0, name.c_str()); + texture_mapping_shadow = MaResourceMapping::Texture_SRV(0, binding.m_name.c_str()); sampler_mapping_shadow = MaResourceMapping::Sampler(0, "masked_shadow_sampler"); } @@ -169,6 +182,14 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) { .Add(texture_mapping_shadow) .Add(sampler_mapping_shadow); } + if (pass == RenderPassType::DepthOnly) { + mappings + .Add(MaResourceMapping::ConstantBuffer( + 0, "cb_draw_constants_static")) + .Add(MaResourceMapping::PushConstants(1)) + .Add(texture_mapping_shadow) + .Add(sampler_mapping_shadow); + } continue; } pipeline_desc.setPixelShader(pixel_shader) @@ -181,6 +202,7 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) { pipeline_desc.setInputLayout( m_renderer->m_input_layout[static_cast(draw)]); switch (pass) { + case RenderPassType::DepthOnly: case RenderPassType::Deferred: case RenderPassType::Forward: pipeline_desc.setVertexShader( @@ -193,6 +215,7 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) { default:; } switch (pass) { + case RenderPassType::DepthOnly: case RenderPassType::Deferred: case RenderPassType::Forward: mappings.Add(MaResourceMapping::PushConstants(1)) @@ -219,13 +242,18 @@ 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")) - .Add(MaResourceMapping::StructuredBuffer_SRV(17, "forwardplus_index_buffer_transparent")) - .Add(MaResourceMapping::StructuredBuffer_SRV(18, "forwardplus_light_buffer")) + .Add(MaResourceMapping::Texture_SRV( + 16, "forwardplus_index_grid_transparent")) + .Add(MaResourceMapping::StructuredBuffer_SRV( + 17, "forwardplus_index_buffer_transparent")) + .Add(MaResourceMapping::StructuredBuffer_SRV( + 18, "forwardplus_light_buffer")) .Add(MaResourceMapping::Sampler(8, "sampler_linear_wrap")) .Add(MaResourceMapping::Sampler(11, "shadow_sampler_comp")) - .Add(MaResourceMapping::Sampler(13, "sampler_linear_clamp_v_repeat_h")); + .Add(MaResourceMapping::Sampler( + 13, "sampler_linear_clamp_v_repeat_h")); break; default:; } @@ -243,7 +271,22 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) { } switch (pass) { case RenderPassType::Deferred: - case RenderPassType::CubeMap: + pipeline_desc.setRenderState( + nvrhi::RenderState() + .setDepthStencilState( + nvrhi::DepthStencilState() + .enableDepthTest() + .disableStencil() + .setDepthFunc(nvrhi::ComparisonFunc::Equal)) + .setRasterState(nvrhi::RasterState() + .setFillSolid() + .enableDepthClip() + .disableScissor() + .setCullFront()) + .setBlendState(nvrhi::BlendState().setRenderTarget( + 0, nvrhi::BlendState::RenderTarget().disableBlend()))); + break; + case RenderPassType::CubeMap: pipeline_desc.setRenderState( nvrhi::RenderState() .setDepthStencilState( @@ -260,8 +303,8 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) { .setBlendState(nvrhi::BlendState().setRenderTarget( 0, nvrhi::BlendState::RenderTarget().disableBlend()))); break; - case RenderPassType::Forward: - pipeline_desc.setRenderState( + case RenderPassType::Forward: { + auto render_state = nvrhi::RenderState() .setDepthStencilState( nvrhi::DepthStencilState() @@ -273,19 +316,25 @@ void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) { .setFillSolid() .enableDepthClip() .disableScissor() - .setCullFront()) - .setBlendState( - nvrhi::BlendState() - .setRenderTarget( - 0, nvrhi::BlendState::RenderTarget() - .enableBlend() - .setBlendOp(nvrhi::BlendOp::Add) - .setSrcBlend(nvrhi::BlendFactor::One) - .setDestBlend( - nvrhi::BlendFactor::OneMinusSrcAlpha)) - .setRenderTarget(1, nvrhi::BlendState::RenderTarget() - .disableBlend()))); - break; + .setCullFront()); + if (m_enable_refraction) { + render_state.setBlendState(nvrhi::BlendState().setRenderTarget( + 0, nvrhi::BlendState::RenderTarget().disableBlend())); + } else { + render_state.setBlendState( + nvrhi::BlendState() + .setRenderTarget( + 0, + nvrhi::BlendState::RenderTarget() + .enableBlend() + .setBlendOp(nvrhi::BlendOp::Add) + .setSrcBlend(nvrhi::BlendFactor::One) + .setDestBlend(nvrhi::BlendFactor::OneMinusSrcAlpha)) + .setRenderTarget( + 1, nvrhi::BlendState::RenderTarget().disableBlend())); + } + pipeline_desc.setRenderState(render_state); + } break; default:; } @@ -419,8 +468,14 @@ bool NvRenderer::InitMaterials() { m_vertex_shader[static_cast(DrawType::Model)] = m_backend->CreateShader("default_vertex", nvrhi::ShaderType::Vertex); + m_vertex_shader_prepass[static_cast(DrawType::Model)] = + m_backend->CreateShader("default_prepass_vertex", + nvrhi::ShaderType::Vertex); m_vertex_shader[static_cast(DrawType::InstancedModel)] = m_backend->CreateShader("instanced_vertex", nvrhi::ShaderType::Vertex); + m_vertex_shader_prepass[static_cast(DrawType::InstancedModel)] = + m_backend->CreateShader("instanced_prepass_vertex", + nvrhi::ShaderType::Vertex); m_vertex_shader_shadow[static_cast(DrawType::Model)] = m_backend->CreateShader("shadow_vertex", nvrhi::ShaderType::Vertex); m_vertex_shader_shadow[static_cast(DrawType::InstancedModel)] = @@ -433,6 +488,8 @@ bool NvRenderer::InitMaterials() { nvrhi::ShaderType::Vertex); m_pixel_shader_shadow_masked = m_backend->CreateShader("shadow_masked", nvrhi::ShaderType::Pixel); + m_pixel_shader_prepass_masked = + m_backend->CreateShader("prepass_masked", nvrhi::ShaderType::Pixel); nvrhi::VertexAttributeDesc desc[]{ nvrhi::VertexAttributeDesc() @@ -481,55 +538,82 @@ bool NvRenderer::InitMaterials() { m_vertex_shader[static_cast(DrawType::InstancedModel)]); { - m_binding_layout_shadow_masked = - m_backend->GetDevice()->createBindingLayout( - nvrhi::BindingLayoutDesc() - .setVisibility(nvrhi::ShaderType::Pixel) - .addItem(nvrhi::BindingLayoutItem::Texture_SRV(0)) - .addItem(nvrhi::BindingLayoutItem::Sampler(0))); + auto constexpr render_state_shadow = + nvrhi::RenderState() + .setDepthStencilState( + nvrhi::DepthStencilState() + .enableDepthTest() + .enableDepthWrite() + .disableStencil() + .setDepthFunc(nvrhi::ComparisonFunc::Greater)) + .setRasterState(nvrhi::RasterState() + .setFillSolid() + .disableDepthClip() + .disableScissor() + .setCullBack() + .setDepthBias(1) + .setSlopeScaleDepthBias(-4.f)) + .setBlendState(nvrhi::BlendState().setRenderTarget( + 0, nvrhi::BlendState::RenderTarget().disableBlend())); + auto constexpr render_state_depthonly = + 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())); for (int i = 0; i < Constants::NumDrawTypes(); ++i) { for (int j = 0; j < 2; ++j) { DrawType draw = static_cast(i); bool masked = j; - nvrhi::BindingLayoutDesc desc = + nvrhi::BindingLayoutDesc desc_shadow = *m_binding_layout_shadowdrawconstants->getDesc(); - desc.visibility = nvrhi::ShaderType::All; - if (masked) - desc.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0)) - .addItem(nvrhi::BindingLayoutItem::Sampler(0)); - auto binding_layout = - GetBackend()->GetDevice()->createBindingLayout(desc); - auto pipeline_desc = - nvrhi::GraphicsPipelineDesc() - .setRenderState( - nvrhi::RenderState() - .setDepthStencilState( - nvrhi::DepthStencilState() - .enableDepthTest() - .enableDepthWrite() - .disableStencil() - .setDepthFunc(nvrhi::ComparisonFunc::Greater)) - .setRasterState(nvrhi::RasterState() - .setFillSolid() - .disableDepthClip() - .disableScissor() - .setCullBack() - .setDepthBias(1) - .setSlopeScaleDepthBias(-4.f)) - .setBlendState(nvrhi::BlendState().setRenderTarget( - 0, - nvrhi::BlendState::RenderTarget().disableBlend()))) - .setVertexShader( - m_vertex_shader_shadow[static_cast(draw)]) - .setInputLayout(m_input_layout[static_cast(draw)]); + nvrhi::BindingLayoutDesc desc_prepass = + *m_binding_layout_drawconstants->getDesc(); + desc_shadow.visibility = nvrhi::ShaderType::All; + desc_prepass.visibility = nvrhi::ShaderType::All; if (masked) { - pipeline_desc.setPixelShader(m_pixel_shader_shadow_masked); + desc_shadow.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0)) + .addItem(nvrhi::BindingLayoutItem::Sampler(0)); + desc_prepass.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0)) + .addItem(nvrhi::BindingLayoutItem::Sampler(0)); } - pipeline_desc.addBindingLayout(binding_layout); + auto binding_layout_shadow = + GetBackend()->GetDevice()->createBindingLayout(desc_shadow); + auto binding_layout_prepass = + GetBackend()->GetDevice()->createBindingLayout(desc_prepass); + auto pipeline_desc = nvrhi::GraphicsPipelineDesc().setInputLayout( + m_input_layout[static_cast(draw)]); m_pso_shadow[Constants::GetDefaultShadowPipelineIndex(draw, masked)] = GetBackend()->GetDevice()->createGraphicsPipeline( - pipeline_desc, m_gbuffer_shadow->m_framebuffer); + nvrhi::GraphicsPipelineDesc(pipeline_desc) + .setRenderState(render_state_shadow) + .setVertexShader( + m_vertex_shader_shadow[static_cast(draw)]) + .setPixelShader(masked ? m_pixel_shader_shadow_masked + : nullptr) + .addBindingLayout(binding_layout_shadow), + m_gbuffer_shadow->m_framebuffer); + + m_pso_prepass[Constants::GetDefaultShadowPipelineIndex(draw, masked)] = + GetBackend()->GetDevice()->createGraphicsPipeline( + nvrhi::GraphicsPipelineDesc(pipeline_desc) + .setVertexShader( + m_vertex_shader_prepass[static_cast(draw)]) + .setPixelShader(masked ? m_pixel_shader_prepass_masked + : nullptr) + .setRenderState(render_state_depthonly) + .addBindingLayout(binding_layout_prepass), + m_gbuffer->m_framebuffer); } } } diff --git a/betterRenderer/renderer/source/nvrenderer.cpp b/betterRenderer/renderer/source/nvrenderer.cpp index b48eb18a..24531b31 100644 --- a/betterRenderer/renderer/source/nvrenderer.cpp +++ b/betterRenderer/renderer/source/nvrenderer.cpp @@ -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(); @@ -92,6 +93,7 @@ bool NvRenderer::Init(GLFWwindow *Window) { m_auto_exposure = std::make_shared(this); m_fsr = std::make_shared(this); m_bloom = std::make_shared(GetBackend()); + m_windshield_rain = std::make_shared(); // 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; } @@ -381,12 +386,21 @@ bool NvRenderer::Render() { (static_cast(Global.cursor_pos) / static_cast(Global.window_size) - .5); - m_mouse_ro = pass.m_origin; - glm::dvec4 mouse_rd_norm = - inverse(projection) * glm::dvec4(mousepos, 1., 1.); - mouse_rd_norm /= mouse_rd_norm.w; - m_mouse_rd = - normalize(inverse(transform) * glm::dvec4(mouse_rd_norm.xyz, 0.)); + { + glm::dvec4 mouse_ro_norm = + inverse(projection) * glm::dvec4(mousepos, 1., 1.); + mouse_ro_norm /= mouse_ro_norm.w; + m_mouse_ro = pass.m_origin + static_cast( + inverse(transform) * + glm::dvec4(mouse_ro_norm.xyz, 1.)); + } + { + glm::dvec4 mouse_rd_norm = + inverse(projection) * glm::dvec4(mousepos, 0., 1.); + mouse_rd_norm /= mouse_rd_norm.w; + m_mouse_rd = + normalize(inverse(transform) * glm::dvec4(mouse_rd_norm.xyz, 0.)); + } } m_picked_submodel = nullptr; @@ -413,6 +427,18 @@ bool NvRenderer::Render() { pass.m_history_transform = std::exchange(m_previous_view, pass.m_transform); + command_list->beginMarker("Depth only"); + pass.m_type = RenderPassType::DepthOnly; + RenderKabina(pass); + RenderShapes(pass); + RenderBatches(pass); + RenderTracks(pass); + RenderAnimateds(pass); + RenderLines(pass); + command_list->endMarker(); + + command_list->beginMarker("Gbuffer fill"); + pass.m_type = RenderPassType::Deferred; Timer::subsystem.gfx_color.start(); RenderKabina(pass); RenderShapes(pass); @@ -420,6 +446,7 @@ bool NvRenderer::Render() { RenderTracks(pass); RenderAnimateds(pass); RenderLines(pass); + command_list->endMarker(); GatherSpotLights(pass); @@ -618,6 +645,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; @@ -991,19 +1019,23 @@ material_handle NvRenderer::Fetch_Material(std::string const &Filename, auto texture_manager = GetTextureManager(); for (int i = 0; i < material_template->m_texture_bindings.size(); ++i) { - const auto &[key, sampler_key, hint, m_default_texture] = - material_template->m_texture_bindings[i]; + const auto &binding = material_template->m_texture_bindings[i]; auto handle = texture_manager->FetchTexture( - static_cast(adapter.GetTexturePathForEntry(key)), hint, - adapter.GetTextureSizeBiasForEntry(key), true); + static_cast( + adapter.GetTexturePathForEntry(binding.m_name)), + binding.m_hint, adapter.GetTextureSizeBiasForEntry(binding.m_name), + true); cache.m_texture_handles[i] = handle; - cache.RegisterTexture(key.c_str(), handle); - cache.RegisterResource(false, sampler_key.c_str(), + auto traits = texture_manager->GetTraits(handle); + traits[MaTextureTraits_NoFilter] = binding.disable_filter; + traits[MaTextureTraits_NoAnisotropy] = binding.disable_anisotropy; + traits[MaTextureTraits_NoMipBias] = binding.disable_mip_bias; + cache.RegisterTexture(binding.m_name.c_str(), handle); + cache.RegisterResource(false, binding.m_sampler_name.c_str(), texture_manager->GetSamplerForTraits( - texture_manager->GetTraits(handle), - NvRenderer::RenderPassType::Deferred), + traits, RenderPassType::Deferred), nvrhi::ResourceType::Sampler); - if (key == cache.m_template->m_masked_shadow_texture && + if (binding.m_name == cache.m_template->m_masked_shadow_texture && texture_manager->IsValidHandle(handle)) { cache.m_masked_shadow = texture_manager->GetTexture(handle)->m_has_alpha; @@ -1298,6 +1330,8 @@ void NvRenderer::DebugUi() { ImGui::SameLine(); if (ImGui::Button("Bloom Config")) m_bloom->ShowGui(); ImGui::SameLine(); + m_ssao->OnGui(ImGui::Button("GTAO Config")); + ImGui::SameLine(); if (ImGui::Button("Take Screenshot")) MakeScreenshot(); } ImGui::End(); @@ -1495,6 +1529,7 @@ void NvRenderer::UpdateDrawData(const RenderPass &pass, pass.m_command_list_draw->setPushConstants(&data, sizeof(data)); break; } + case RenderPassType::DepthOnly: case RenderPassType::Deferred: case RenderPassType::Forward: { PushConstantsDraw data{}; @@ -1560,13 +1595,17 @@ void NvRenderer::BindConstants(const RenderPass &pass, bool NvRenderer::BindMaterial(material_handle handle, DrawType draw_type, const RenderPass &pass, nvrhi::GraphicsState &gfx_state, - float &alpha_threshold) { + float &alpha_threshold, bool *out_is_refractive) { if (pass.m_type == RenderPassType::RendererWarmUp) return true; if (!handle || handle > m_material_cache.size()) { return false; } MaterialCache &cache = m_material_cache[handle - 1]; + if(out_is_refractive) { + *out_is_refractive = cache.m_template->m_enable_refraction; + } + if (!cache.ShouldRenderInPass(pass)) return false; auto pipeline_index = Constants::GetPipelineIndex(pass.m_type, draw_type); @@ -1967,6 +2006,8 @@ void NvRenderer::Animate(Renderable &renderable, TSubModel *Submodel, m_batched_instances.end()) { auto &item = renderable.m_items.emplace_back(); + item.m_name = Submodel->pName.c_str(); + auto &motion_cache = m_motion_cache->Get(Submodel); motion_cache.m_history_transform = diff --git a/betterRenderer/renderer/source/nvtexture.cpp b/betterRenderer/renderer/source/nvtexture.cpp index a6c58a9c..192a88cc 100644 --- a/betterRenderer/renderer/source/nvtexture.cpp +++ b/betterRenderer/renderer/source/nvtexture.cpp @@ -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()); + 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; @@ -493,10 +506,16 @@ nvrhi::SamplerHandle NvTextureManager::GetSamplerForTraits( .setAddressV(traits[MaTextureTraits_ClampT] ? nvrhi::SamplerAddressMode::Clamp : nvrhi::SamplerAddressMode::Wrap) - .setAllFilters(true) + .setAllFilters(!traits[MaTextureTraits_NoFilter]) .setMipFilter(false) - .setMaxAnisotropy(traits[MaTextureTraits_NoAnisotropy] ? 0.f : 16.f) - .setMipBias(traits[MaTextureTraits_NoMipBias] ? 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; diff --git a/betterRenderer/renderer/source/nvtexture.h b/betterRenderer/renderer/source/nvtexture.h index 7eab94ad..18bba50d 100644 --- a/betterRenderer/renderer/source/nvtexture.h +++ b/betterRenderer/renderer/source/nvtexture.h @@ -16,6 +16,7 @@ enum MaTextureTraits { MaTextureTraits_NoMipBias, MaTextureTraits_NoAnisotropy, + MaTextureTraits_NoFilter, MaTextureTraits_Num }; @@ -82,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); diff --git a/betterRenderer/renderer/source/rt_model.cpp b/betterRenderer/renderer/source/rt_model.cpp index 7bd2156f..e587348b 100644 --- a/betterRenderer/renderer/source/rt_model.cpp +++ b/betterRenderer/renderer/source/rt_model.cpp @@ -137,7 +137,9 @@ const TSubModel* Rt::RtModel::Intersect( [&](size_t const begin, size_t const end) { for (size_t i = begin; i < end; ++i) { size_t const j = submodel.bvh.prim_ids[i]; - if (auto hit = submodel.tris[j].intersect(ray)) { + if (auto hit = submodel.tris[j].intersect(ray); + !!hit && dot(rd_local, glm::make_vec3( + submodel.tris[j].n.values)) > 0.) { prim_id = i; std::tie(ray.tmax, u, v) = *hit; result = submodel.submodel; diff --git a/betterRenderer/renderer/source/ssao.cpp b/betterRenderer/renderer/source/ssao.cpp index b36db4ec..7a098703 100644 --- a/betterRenderer/renderer/source/ssao.cpp +++ b/betterRenderer/renderer/source/ssao.cpp @@ -78,7 +78,7 @@ void NvSsao::Init() { .setWidth(m_width) .setHeight(m_height) .setIsUAV(true) - .setFormat(Format::R8_UINT) + .setFormat(Format::R32_UINT) .setInitialState(ResourceStates::ShaderResource) .setKeepInitialState(true)); @@ -88,7 +88,7 @@ void NvSsao::Init() { .setWidth(m_width) .setHeight(m_height) .setIsUAV(true) - .setFormat(Format::R8_UINT) + .setFormat(Format::R32_UINT) .setInitialState(ResourceStates::ShaderResource) .setKeepInitialState(true)); @@ -98,15 +98,16 @@ void NvSsao::Init() { .setWidth(m_width) .setHeight(m_height) .setIsUAV(true) - .setFormat(Format::R8_UNORM) + .setFormat(Format::RGBA8_UNORM) .setIsTypeless(true) .setInitialState(ResourceStates::ShaderResource) .setKeepInitialState(true)); - SamplerHandle sampler_point = m_SamplerPoint = m_backend->GetDevice()->createSampler( - nvrhi::SamplerDesc() - .setAllAddressModes(SamplerAddressMode::Clamp) - .setAllFilters(false)); + SamplerHandle sampler_point = m_SamplerPoint = + m_backend->GetDevice()->createSampler( + nvrhi::SamplerDesc() + .setAllAddressModes(SamplerAddressMode::Clamp) + .setAllFilters(false)); { BindingLayoutHandle blPrefilterDepths; @@ -136,17 +137,19 @@ void NvSsao::Init() { BindingSetDesc() .addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer)) .addItem(BindingSetItem ::Texture_SRV(0, m_workingDepths)) - .addItem( - BindingSetItem::Texture_SRV(1, m_gbuffer->m_gbuffer_normal)) .addItem(BindingSetItem::Texture_UAV(0, m_workingAOTerm)) .addItem(BindingSetItem::Texture_UAV(1, m_workingEdges)) .addItem(BindingSetItem::Texture_UAV(2, m_debugImage)) .addItem(BindingSetItem::Sampler(0, sampler_point)), blGTAO, m_BSGTAO); - m_PSOGTAO = m_backend->GetDevice()->createComputePipeline( - ComputePipelineDesc() - .setComputeShader(m_CSGTAOHigh) - .addBindingLayout(blGTAO)); + std::array const shaders{m_CSGTAOLow, m_CSGTAOMedium, + m_CSGTAOHigh, m_CSGTAOUltra}; + for (int i = 0; i < 4; ++i) { + m_PSOGTAO[i] = m_backend->GetDevice()->createComputePipeline( + ComputePipelineDesc() + .setComputeShader(shaders[i]) + .addBindingLayout(blGTAO)); + } } { @@ -187,12 +190,8 @@ void NvSsao::Render(nvrhi::ICommandList* command_list, command_list->beginMarker("XeGTAO"); XeGTAO::GTAOConstants constants{}; - XeGTAO::GTAOSettings settings{}; - settings.QualityLevel = 2; - settings.Radius = 10.f; - settings.ThinOccluderCompensation = .7f; XeGTAO::GTAOUpdateConstants(constants, m_width, m_height, settings, - glm::value_ptr(projection), false, frame_index); + projection, false, frame_index); command_list->writeBuffer(m_constantBuffer, &constants, sizeof(constants)); { @@ -210,7 +209,7 @@ void NvSsao::Render(nvrhi::ICommandList* command_list, { command_list->beginMarker("Main Pass"); nvrhi::ComputeState state; - state.setPipeline(m_PSOGTAO); + state.setPipeline(m_PSOGTAO[glm::clamp(settings.QualityLevel, 0, 3)]); state.addBindingSet(m_BSGTAO); command_list->setComputeState(state); @@ -222,7 +221,7 @@ void NvSsao::Render(nvrhi::ICommandList* command_list, { command_list->beginMarker("Denoise Pass"); - int num_passes = 4; + int num_passes = settings.DenoisePasses; nvrhi::ITexture* ping = m_workingAOTerm; nvrhi::ITexture* pong = m_workingAOTermPong; for (int i = 0; i < num_passes; ++i) { @@ -233,11 +232,10 @@ void NvSsao::Render(nvrhi::ICommandList* command_list, .addItem( nvrhi::BindingSetItem::ConstantBuffer(0, m_constantBuffer)) .addItem(nvrhi::BindingSetItem::Texture_SRV(0, ping)) - .addItem(nvrhi::BindingSetItem::Texture_SRV( - 1, m_gbuffer->m_gbuffer_normal)) + .addItem(nvrhi::BindingSetItem::Texture_SRV(1, m_workingEdges)) .addItem(nvrhi::BindingSetItem::Texture_UAV( 0, last_pass ? m_outputAO.Get() : pong, - nvrhi::Format::R8_UINT)) + nvrhi::Format::R32_UINT)) .addItem(nvrhi::BindingSetItem::Texture_UAV(1, m_workingEdges)) .addItem(nvrhi::BindingSetItem::Texture_UAV(2, m_debugImage)) .addItem(nvrhi::BindingSetItem::Sampler(0, m_SamplerPoint)), @@ -260,3 +258,25 @@ void NvSsao::Render(nvrhi::ICommandList* command_list, } command_list->endMarker(); } + +void NvSsao::OnGui(bool const open_now) { + static bool open = false; + open |= open_now; + if (open && ImGui::Begin("GTAO Settings", &open)) { + ImGui::InputInt("Quality level", &settings.QualityLevel); + ImGui::InputInt("Denoise passes", &settings.DenoisePasses); + ImGui::InputFloat("Radius", &settings.Radius); + if (ImGui::CollapsingHeader("Advanced")) { + ImGui::InputFloat("Radius multiplier", &settings.RadiusMultiplier); + ImGui::InputFloat("Falloff range", &settings.FalloffRange); + ImGui::InputFloat("Sample distribution power", + &settings.SampleDistributionPower); + ImGui::InputFloat("Thin occluder compensation", + &settings.ThinOccluderCompensation); + ImGui::InputFloat("Final value power", &settings.FinalValuePower); + ImGui::InputFloat("Depth MIP sampling offset", + &settings.DepthMIPSamplingOffset); + } + ImGui::End(); + } +} diff --git a/betterRenderer/renderer/source/ssao.h b/betterRenderer/renderer/source/ssao.h index 2bdd539c..62ee6dd1 100644 --- a/betterRenderer/renderer/source/ssao.h +++ b/betterRenderer/renderer/source/ssao.h @@ -1,15 +1,18 @@ #pragma once -#include +#include #include +#include -#include +#include "XeGTAO.h" struct NvSsao { NvSsao(class NvRenderer* renderer); void Init(); + XeGTAO::GTAOSettings settings{}; + class NvRendererBackend* m_backend; struct NvGbuffer* m_gbuffer; @@ -23,7 +26,7 @@ struct NvSsao { nvrhi::ShaderHandle m_CSGTAOMedium; nvrhi::ShaderHandle m_CSGTAOHigh; nvrhi::ShaderHandle m_CSGTAOUltra; - nvrhi::ComputePipelineHandle m_PSOGTAO; + std::array m_PSOGTAO; nvrhi::BindingSetHandle m_BSGTAO; nvrhi::ShaderHandle m_CSDenoisePass; @@ -52,4 +55,6 @@ struct NvSsao { void Render(nvrhi::ICommandList* command_list, const glm::mat4& projection, size_t frame_index); + + void OnGui(bool open_now); }; \ No newline at end of file diff --git a/betterRenderer/renderer/source/track_batching.cpp b/betterRenderer/renderer/source/track_batching.cpp index 65317a4f..946e4599 100644 --- a/betterRenderer/renderer/source/track_batching.cpp +++ b/betterRenderer/renderer/source/track_batching.cpp @@ -1,8 +1,10 @@ #include "config.h" +#include "gbufferblitpass.h" #include "motioncache.h" #include "nvrenderer/nvrenderer.h" #include "nvrendererbackend.h" +#include #include #include @@ -959,13 +961,19 @@ void NvRenderer::RenderAnimateds(const RenderPass& pass) { switch (command.m_type) { case RenderCommand::ObjectType_Animated: m_drawcall_counter = &m_drawcalls_tanimobj; + pass.m_command_list_draw->beginMarker( + m_animateds[command.m_index].m_model->name().c_str()); Render(m_animateds[command.m_index].m_renderable, pass, history_origin, m_animateds[command.m_index].m_distance); + pass.m_command_list_draw->endMarker(); break; case RenderCommand::ObjectType_Dynamic: m_drawcall_counter = &m_drawcalls_dynamic; + pass.m_command_list_draw->beginMarker( + m_dynamics[command.m_index].m_dynamic->name().c_str()); Render(m_dynamics[command.m_index].m_renderable, pass, history_origin, m_dynamics[command.m_index].m_distance); + pass.m_command_list_draw->endMarker(); break; } } @@ -989,7 +997,9 @@ void NvRenderer::RenderKabina(const RenderPass& pass) { default: if (!dynamic.m_renderable_kabina.m_render_in_deferred) return; } + pass.m_command_list_draw->beginMarker("Render cab"); Render(dynamic.m_renderable_kabina, pass, history_origin, dynamic.m_distance); + pass.m_command_list_draw->endMarker(); } void NvRenderer::Render(const Renderable& renderable, const RenderPass& pass, @@ -1006,16 +1016,23 @@ void NvRenderer::Render(const Renderable& renderable, const RenderPass& pass, nvrhi::GraphicsState gfx_state; nvrhi::DrawArguments draw_arguments{}; bool indexed; + bool refractive; float alpha_threshold; if (!BindGeometry(item.m_geometry, pass, gfx_state, draw_arguments, indexed)) - return; + continue; if (!BindMaterial(item.m_material, DrawType::Model, pass, gfx_state, - alpha_threshold)) - return; + alpha_threshold, &refractive)) + continue; BindConstants(pass, gfx_state); + pass.m_command_list_draw->beginMarker(item.m_name.data()); + + if(refractive && pass.m_type == RenderPassType::Forward) { + m_gbuffer_blit->UpdateSceneColorForRefraction(pass.m_command_list_draw); + } + pass.m_command_list_draw->setGraphicsState(gfx_state); auto transform = item.m_transform; @@ -1034,11 +1051,14 @@ void NvRenderer::Render(const Renderable& renderable, const RenderPass& pass, else pass.m_command_list_draw->draw(draw_arguments); m_drawcall_counter->Draw(draw_arguments); + + pass.m_command_list_draw->endMarker(); } } void NvRenderer::Animate(const glm::dvec3& origin, double radius, uint64_t frame) { + Timer::subsystem.gfx_animate.start(); { auto motion_scope = m_motion_cache->SetInstance(nullptr); auto& motion_cache = m_motion_cache->Get(nullptr); @@ -1077,6 +1097,7 @@ void NvRenderer::Animate(const glm::dvec3& origin, double radius, if (!instance.m_animatable && instance.m_was_once_animated) return; Animate(instance, origin, frame); }); + Timer::subsystem.gfx_animate.stop(); } void NvRenderer::GatherSpotLights(const RenderPass& pass) { diff --git a/betterRenderer/renderer/source/windshield_rain.cpp b/betterRenderer/renderer/source/windshield_rain.cpp new file mode 100644 index 00000000..33af7b01 --- /dev/null +++ b/betterRenderer/renderer/source/windshield_rain.cpp @@ -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( + transpose(static_cast(pass.m_transform) * + static_cast(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( + 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); +} diff --git a/betterRenderer/renderer/source/windshield_rain.h b/betterRenderer/renderer/source/windshield_rain.h new file mode 100644 index 00000000..47ee2fc6 --- /dev/null +++ b/betterRenderer/renderer/source/windshield_rain.h @@ -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 binding_sets_per_material; +}; \ No newline at end of file diff --git a/betterRenderer/shaders/XeGTAO/GTAO.hlsl b/betterRenderer/shaders/XeGTAO/GTAO.hlsl index 9a69f3fd..ffd8c3e4 100644 --- a/betterRenderer/shaders/XeGTAO/GTAO.hlsl +++ b/betterRenderer/shaders/XeGTAO/GTAO.hlsl @@ -54,6 +54,7 @@ SamplerState g_samplerPointClamp : register( s0 ); // Engine-specific normal map loader lpfloat3 LoadNormal( int2 pos ) { + return (float3)0.; #if 0 // special decoding for external normals stored in 11_11_10 unorm - modify appropriately to support your own encoding uint packedInput = g_srcNormalmap.Load( int3(pos, 0) ).x; diff --git a/betterRenderer/shaders/XeGTAO/XeGTAO.hlsli b/betterRenderer/shaders/XeGTAO/XeGTAO.hlsli index cfb9566e..3bea2518 100644 --- a/betterRenderer/shaders/XeGTAO/XeGTAO.hlsli +++ b/betterRenderer/shaders/XeGTAO/XeGTAO.hlsli @@ -111,11 +111,10 @@ float3 XeGTAO_ComputeViewspacePosition( const float2 screenPos, const float view float XeGTAO_ScreenSpaceToViewSpaceDepth( const float screenDepth, const GTAOConstants consts ) { - float depthLinearizeMul = consts.DepthUnpackConsts.x; - float depthLinearizeAdd = consts.DepthUnpackConsts.y; - // Optimised version of "-cameraClipNear / (cameraClipFar - projDepth * (cameraClipFar - cameraClipNear)) * cameraClipFar" - //return 2500. / (.1 - screenDepth * (.1 - 2500.)) * .1; - return depthLinearizeMul / (depthLinearizeAdd - screenDepth); + float depthLinearizeMul = consts.DepthUnpackConsts.x; + float depthLinearizeAdd = consts.DepthUnpackConsts.y; + // Optimised version of "-cameraClipNear / (cameraClipFar - projDepth * (cameraClipFar - cameraClipNear)) * cameraClipFar" + return depthLinearizeMul / (depthLinearizeAdd - screenDepth); } lpfloat4 XeGTAO_CalculateEdges( const lpfloat centerZ, const lpfloat leftZ, const lpfloat rightZ, const lpfloat topZ, const lpfloat bottomZ ) diff --git a/betterRenderer/shaders/cs_downsample_depth.hlsl b/betterRenderer/shaders/cs_downsample_depth.hlsl new file mode 100644 index 00000000..f124c1a9 --- /dev/null +++ b/betterRenderer/shaders/cs_downsample_depth.hlsl @@ -0,0 +1,15 @@ +Texture2D g_DepthTexture : register(t0); +RWTexture2D 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)); +} + diff --git a/betterRenderer/shaders/default_vertex.hlsl b/betterRenderer/shaders/default_vertex.hlsl index b7db5b0b..793a0132 100644 --- a/betterRenderer/shaders/default_vertex.hlsl +++ b/betterRenderer/shaders/default_vertex.hlsl @@ -7,6 +7,12 @@ struct VertexInput { float4 m_Tangent : Tangent; }; +#ifdef PREPASS +struct VertexOutput { + float2 m_TexCoord : TexCoord; + float4 m_PositionSV : SV_Position; +}; +#else struct VertexOutput { float3 m_Position : Position; float3 m_Normal : Normal; @@ -16,6 +22,7 @@ struct VertexOutput { float4 m_PositionCS : PositionCS; float4 m_HistoryPositionCS : HistoryPositionCS; }; +#endif cbuffer VertexConstants : register(b0) { float4x4 g_JitteredProjection; @@ -25,18 +32,27 @@ 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(); - result.m_Position = mul(float4(vs_in.m_Position, 1.), model_view).xyz; - result.m_Normal = mul(float4(vs_in.m_Normal, 0.), model_view).xyz; + 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(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; result.m_Tangent.xyz = mul(float4(vs_in.m_Tangent.xyz, 0.), model_view).xyz; result.m_Tangent.w = vs_in.m_Tangent.w; - result.m_PositionSV = mul(g_JitteredProjection, float4(result.m_Position, 1.)); result.m_PositionCS = mul(g_Projection, float4(result.m_Position, 1.)); float3 history_position = mul(float4(vs_in.m_Position, 1.), model_view_history); result.m_HistoryPositionCS = mul(g_ProjectionHistory, float4(history_position, 1.)); +#endif return result; } diff --git a/betterRenderer/shaders/instanced_vertex.hlsl b/betterRenderer/shaders/instanced_vertex.hlsl index 9b29e67d..01f2aaf1 100644 --- a/betterRenderer/shaders/instanced_vertex.hlsl +++ b/betterRenderer/shaders/instanced_vertex.hlsl @@ -8,6 +8,12 @@ struct VertexInput { float4 m_InstanceTransform[3] : InstanceTransform; }; +#ifdef PREPASS +struct VertexOutput { + float2 m_TexCoord : TexCoord; + float4 m_PositionSV : SV_Position; +}; +#else struct VertexOutput { float3 m_Position : Position; float3 m_Normal : Normal; @@ -17,6 +23,7 @@ struct VertexOutput { float4 m_PositionCS : PositionCS; float4 m_HistoryPositionCS : HistoryPositionCS; }; +#endif cbuffer VertexConstants : register(b0) { float4x4 g_JitteredProjection; @@ -32,14 +39,17 @@ VertexOutput main(in VertexInput vs_in) { float4x3 model_view_history = GetModelViewHistory(); float4x4 instance_transform = transpose(float4x4(vs_in.m_InstanceTransform[0], vs_in.m_InstanceTransform[1], vs_in.m_InstanceTransform[2], float4(0., 0., 0., 1.))); float3 position = mul(float4(vs_in.m_Position, 1.), instance_transform).xyz; - result.m_Position = mul(float4(position, 1.), model_view).xyz; - result.m_Normal = mul(mul(float4(vs_in.m_Normal, 0.), instance_transform), model_view); + float3 view_space_position = mul(float4(position, 1.), model_view).xyz; + result.m_PositionSV = mul(g_JitteredProjection, float4(view_space_position, 1.)); result.m_TexCoord = vs_in.m_TexCoord; +#ifndef PREPASS + result.m_Position = view_space_position; + result.m_Normal = mul(mul(float4(vs_in.m_Normal, 0.), instance_transform), model_view); result.m_Tangent.xyz = mul(mul(float4(vs_in.m_Tangent.xyz, 0.), instance_transform), model_view); result.m_Tangent.w = vs_in.m_Tangent.w; - result.m_PositionSV = mul(g_JitteredProjection, float4(result.m_Position, 1.)); result.m_PositionCS = mul(g_Projection, float4(result.m_Position, 1.)); float3 history_position = mul(float4(position, 1.), model_view_history).xyz; result.m_HistoryPositionCS = mul(g_ProjectionHistory, float4(history_position, 1.)); +#endif return result; } diff --git a/betterRenderer/shaders/manul/alpha_mask.hlsli b/betterRenderer/shaders/manul/alpha_mask.hlsli index f23bc898..f890d7f8 100644 --- a/betterRenderer/shaders/manul/alpha_mask.hlsli +++ b/betterRenderer/shaders/manul/alpha_mask.hlsli @@ -2,7 +2,9 @@ #define ALPHA_MASK_HLSLI void AlphaMask(in float alpha) { +#if (PASS & FORWARD_LIGHTING) || defined (SHADOW) if(g_DrawConstants.m_AlphaThreshold >= 0. ? (alpha < g_DrawConstants.m_AlphaThreshold) : (alpha >= -g_DrawConstants.m_AlphaThreshold)) discard; +#endif } #endif diff --git a/betterRenderer/shaders/manul/gbuffer_lights.hlsl b/betterRenderer/shaders/manul/gbuffer_lights.hlsl deleted file mode 100644 index 35e9b727..00000000 --- a/betterRenderer/shaders/manul/gbuffer_lights.hlsl +++ /dev/null @@ -1,106 +0,0 @@ -#include "math.hlsli" - -#include "lighting_functions.hlsli" - -#include "color_transform.hlsli" - -#include "manul/gbuffer_ssao.hlsli" - -struct ViewConstants { - float4x4 m_inverse_model_view; - float4x4 m_inverse_projection; -}; - -struct PushConstantsSpotLight { - float3 m_origin; - float m_radius_sqr; - float3 m_direction; - float m_cos_inner_cone; - float3 m_color; - float m_cos_outer_cone; -}; - -cbuffer g_ViewConst : register(b0) { ViewConstants g_View; } - -#ifdef SPIRV -[[vk::push_constant]] ConstantBuffer g_SpotLight; -#else -cbuffer g_SpotLightConst : register(b1) { PushConstantsSpotLight g_SpotLight; } -#endif - -RWTexture2D g_OutDiffuse : register(u0); - -Texture2D g_GbufferDepth : register(t0); -Texture2D g_GbufferNormal : register(t1); -Texture2D g_GbufferParams : register(t2); -Texture2D g_GbufferColor : register(t3); - -float2 PixelToCS(in float2 pixel, in float2 size) { - return ((pixel + .5) / size - .5) * float2(2., -2.); -} - -float3 ReconstructPos(in float2 cs, in float depth) { - float4 ndc = float4(cs, depth, 1.); - ndc = mul(g_View.m_inverse_projection, ndc); - return ndc.xyz / ndc.w; -} - -[numthreads(8, 8, 1)] -void CS_Clear(uint3 PixCoord : SV_DispatchThreadID, uint3 GroupID : SV_GroupID, uint GroupIndex : SV_GroupIndex) { - uint2 pixel = PixCoord.xy; - g_OutDiffuse[pixel].rgb = 0.; -} - -[numthreads(8, 8, 1)] -void CS_Spotlight(uint3 PixCoord : SV_DispatchThreadID, uint3 GroupID : SV_GroupID, uint GroupIndex : SV_GroupIndex) { - uint2 gbuffer_dimensions; - uint2 pixel = PixCoord.xy; - - g_OutDiffuse.GetDimensions(gbuffer_dimensions.x, gbuffer_dimensions.y); - float3 position = ReconstructPos(PixelToCS(PixCoord.xy, gbuffer_dimensions), g_GbufferDepth[pixel]); - float3 view = mul((float3x3)g_View.m_inverse_model_view, position); - - float3 L_offset = g_SpotLight.m_origin - view; - float3 L = normalize(L_offset); - if(dot(L_offset, L_offset) > g_SpotLight.m_radius_sqr) return; - - float3 albedo = g_GbufferColor[pixel]; - float4 params = g_GbufferParams[pixel]; - float3 normal = UnpackNormalXYZ(g_GbufferNormal[pixel]); - - SurfaceData surface_data; - surface_data.m_Albedo = albedo; - surface_data.m_Alpha = 1.; - surface_data.m_Metalness = params.r; - surface_data.m_Roughness = params.g; -#ifdef GBUFFER_SSAO_HLSLI - surface_data.m_DiffuseOcclusion = min(params.b, GetSSAO(pixel)); -#else - surface_data.m_DiffuseOcclusion = params.b; -#endif - surface_data.m_Normal = mul((float3x3)g_View.m_inverse_model_view, normal); - surface_data.m_View = -normalize(view); - surface_data.m_Reflect = reflect(-surface_data.m_View, surface_data.m_Normal); - surface_data.m_NdotV = max(dot(surface_data.m_Normal, surface_data.m_View), 0.); - surface_data.m_SpecularF0 = float3(.04, .04, .04) * params.a * 2.; - surface_data.m_SpecularF0 = lerp(surface_data.m_SpecularF0, surface_data.m_Albedo, surface_data.m_Metalness); - surface_data.m_SpecularF = FresnelSchlickRoughness(surface_data.m_NdotV, surface_data.m_SpecularF0, surface_data.m_Roughness); - surface_data.m_HorizonFading = 1.6; - surface_data.m_SpecularOcclusion = ComputeSpecOcclusion(surface_data.m_NdotV, surface_data.m_DiffuseOcclusion, surface_data.m_Roughness); - - float3 lit = float3(0., 0., 0.); - - DirectionalLight directionalLight; - directionalLight.m_LightVector = L; - directionalLight.m_Color = max(REC709_to_XYZ(g_SpotLight.m_color), 0.); - ApplyDirectionalLight(lit, directionalLight, surface_data, 1.); - - - float cone = saturate((dot(L, -g_SpotLight.m_direction) - g_SpotLight.m_cos_inner_cone) / (g_SpotLight.m_cos_outer_cone - g_SpotLight.m_cos_inner_cone)); - float attenuation = 1. / dot(L_offset, L_offset) * (1. - cone); - - g_OutDiffuse[pixel].rgb += lit * attenuation; -} - - - diff --git a/betterRenderer/shaders/manul/gbuffer_ssao.hlsli b/betterRenderer/shaders/manul/gbuffer_ssao.hlsli index ad84a45b..c5049a76 100644 --- a/betterRenderer/shaders/manul/gbuffer_ssao.hlsli +++ b/betterRenderer/shaders/manul/gbuffer_ssao.hlsli @@ -1,6 +1,6 @@ #ifndef GBUFFER_SSAO_HLSLI #define GBUFFER_SSAO_HLSLI -Texture2D g_SSAO : register(t5); +Texture2D g_SSAO : register(t5); float4 R8G8B8A8_UNORM_to_FLOAT4( uint packedInput ) { @@ -19,8 +19,11 @@ void DecodeVisibilityBentNormal( const uint packedValue, out float visibility, o visibility = decoded.w; } -float GetSSAO(in uint2 pixel_position) { - return g_SSAO[pixel_position]; +float4 GetBentNormal(in uint2 pixel_position) { + float4 bent_normal = g_SSAO[pixel_position]; + bent_normal.xyz = 2. * bent_normal.xyz - 1.; + bent_normal.z *= -1.; + return bent_normal; } #endif diff --git a/betterRenderer/shaders/manul/lighting.hlsli b/betterRenderer/shaders/manul/lighting.hlsli index 1e8db7ce..9d0ddb6a 100644 --- a/betterRenderer/shaders/manul/lighting.hlsli +++ b/betterRenderer/shaders/manul/lighting.hlsli @@ -39,15 +39,21 @@ void ApplyMaterialLighting(out float4 lit, in MaterialData material) { // Camera-centric world position float3 view = mul((float3x3)g_InverseModelView, material.m_Position); + +#ifdef GBUFFER_SSAO_HLSLI + float4 bent_normal = GetBentNormal(pixel_position); + bent_normal.a = min(material.m_MaterialParams.b, bent_normal.a); + bent_normal.xyz = mul((float3x3)g_InverseModelView, bent_normal.xyz); +#endif // Convert material to surface data SurfaceData surface_data; - surface_data.m_Albedo = clamp(material.m_MaterialAlbedoAlpha.rgb, .02, .9); + surface_data.m_Albedo = saturate(material.m_MaterialAlbedoAlpha.rgb); surface_data.m_Alpha = material.m_MaterialAlbedoAlpha.a; surface_data.m_Metalness = material.m_MaterialParams.r; surface_data.m_Roughness = material.m_MaterialParams.g; #ifdef GBUFFER_SSAO_HLSLI - surface_data.m_DiffuseOcclusion = min(material.m_MaterialParams.b, GetSSAO(pixel_position)); + surface_data.m_DiffuseOcclusion = bent_normal.a; #else surface_data.m_DiffuseOcclusion = material.m_MaterialParams.b; #endif @@ -59,7 +65,12 @@ void ApplyMaterialLighting(out float4 lit, in MaterialData material) surface_data.m_SpecularF0 = lerp(surface_data.m_SpecularF0, surface_data.m_Albedo, surface_data.m_Metalness); surface_data.m_SpecularF = FresnelSchlickRoughness(surface_data.m_NdotV, surface_data.m_SpecularF0, surface_data.m_Roughness); surface_data.m_HorizonFading = 1.6; +#ifdef GBUFFER_SSAO_HLSLI + float BNdotR = max(0., dot(surface_data.m_Reflect, bent_normal.xyz)); + surface_data.m_SpecularOcclusion = ComputeSpecOcclusion(BNdotR, surface_data.m_DiffuseOcclusion, surface_data.m_Roughness); +#else surface_data.m_SpecularOcclusion = ComputeSpecOcclusion(surface_data.m_NdotV, surface_data.m_DiffuseOcclusion, surface_data.m_Roughness); +#endif lit.rgb = material.m_MaterialEmission * surface_data.m_Alpha; lit.a = 1. - (saturate((1. - surface_data.m_Alpha) * lerp(1. - dot(surface_data.m_SpecularF, 1./3.), 0., surface_data.m_Metalness))); diff --git a/betterRenderer/shaders/manul/material.hlsli b/betterRenderer/shaders/manul/material.hlsli index 5cac890a..4be5dae1 100644 --- a/betterRenderer/shaders/manul/material.hlsli +++ b/betterRenderer/shaders/manul/material.hlsli @@ -48,6 +48,7 @@ struct PixelInput { #include "sky.hlsli" Texture2D g_GbufferDepth : register(t12); +Texture2D 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; } diff --git a/betterRenderer/shaders/manul/material_common.hlsli b/betterRenderer/shaders/manul/material_common.hlsli index 3784d0c3..5011b187 100644 --- a/betterRenderer/shaders/manul/material_common.hlsli +++ b/betterRenderer/shaders/manul/material_common.hlsli @@ -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 diff --git a/betterRenderer/shaders/manul/random.hlsli b/betterRenderer/shaders/manul/random.hlsli new file mode 100644 index 00000000..e45ba37a --- /dev/null +++ b/betterRenderer/shaders/manul/random.hlsli @@ -0,0 +1,39 @@ + +uint Hash(uint s); +uint Hash(uint2 s); +uint Hash(uint3 s); +uint Hash(float s); +uint HashCombine(uint seed, uint value); + +// Evolving Sub-Grid Turbulence for Smoke Animation +// H. Schechter and R. Bridson +uint Hash(uint s) { + s ^= 2747636419u; + s *= 2654435769u; + s ^= s >> 16u; + s *= 2654435769u; + s ^= s >> 16u; + s *= 2654435769u; + return s; +} + +uint Hash(uint2 s) { + return HashCombine(Hash(s.x), s.y); +} + +uint Hash(uint3 s) { + return HashCombine(Hash(s.xy), s.z); +} + +uint HashCombine(uint seed, uint value) { + return seed ^ (Hash(value) + 0x9e3779b9u + (seed<<6u) + (seed>>2u)); +} + +uint Rand(inout uint seed) { + seed = Hash(seed); + return seed; +} + +float RandF(inout uint seed) { + return float(Rand(seed)) * 2.3283064365386963e-10; +} diff --git a/betterRenderer/shaders/manul/view_data.hlsli b/betterRenderer/shaders/manul/view_data.hlsli index 7a2fd2d6..8b49d519 100644 --- a/betterRenderer/shaders/manul/view_data.hlsli +++ b/betterRenderer/shaders/manul/view_data.hlsli @@ -8,6 +8,11 @@ cbuffer DrawConstants : register(b2) { float g_Altitude; float3 g_LightColor; float g_Time; + float4 g_RainParams; + float4 g_WiperPos; + float4 g_WiperTimerOut; + float4 g_WiperTimerReturn; + float g_VerticalFov; } float2 PixelToCS(in float2 pixel, in float2 size) { diff --git a/betterRenderer/shaders/project.manul b/betterRenderer/shaders/project.manul index e82c75b3..92cde1ff 100644 --- a/betterRenderer/shaders/project.manul +++ b/betterRenderer/shaders/project.manul @@ -1,8 +1,6 @@ templates: # Partially pre-filled containers to avoid excessive copypasta XeGTAO: - # TODO Depth conversion is broken since converting to reversed depth buffer - # after attempting to fix it, it is still broken but with hardcoded values source: XeGTAO/GTAO target: compute definitions: # TODO (mostly) move to shader file I guess @@ -14,6 +12,8 @@ templates: XE_GTAO_USE_HALF_FLOAT_PRECISION: 0 VA_COMPILED_AS_SHADER_CODE: 1 VA_SATURATE: saturate + XE_GTAO_GENERATE_NORMALS_INPLACE: 1 + XE_GTAO_COMPUTE_BENT_NORMALS: 1 envmap: source: envmap target: compute @@ -23,9 +23,6 @@ templates: bloom: source: manul/bloom target: compute - gbuffer_lights: - source: manul/gbuffer_lights - target: compute shaders: materials: # Material shaders @@ -47,18 +44,6 @@ shaders: binding: 2 hint: normalmap default: normalmap - # paramx: # Metalness.Roughness.[UNUSED].Normal[X] - # binding: 1 - # hint: linear - # default: legacy_params - # paramy: # Specular.Occlusion.[UNUSED].Normal[Y] - # binding: 2 - # hint: linear - # default: legacy_params - # selfillum: - # binding: 3 - # hint: color - # default: black masked_shadow_texture: albedo source: ps_default_lit legacy: @@ -137,6 +122,29 @@ shaders: default: normalmap masked_shadow_texture: diffuse source: ps_legacy_water + windshield_rain: + textures: + diffuse: + binding: 0 + hint: color # so that texture will be marked as sRGB + default: white + raindropsatlas: + binding: 1 + hint: color + default: white + no_anisotropy: true + wipermask: + binding: 2 + hint: color + default: white + no_filter: true + rain: + binding: 3 + hint: linear + default: system/raindrops_buffer + masked_shadow_texture: diffuse + source: ps_windshield_rain + refraction: true utility: # Everything that does not belong to scene graph rendering # ImGui shaders @@ -153,6 +161,20 @@ shaders: source: ps_shadow_masked target: pixel entrypoint: main + prepass_masked: + source: ps_shadow_masked + target: pixel + 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: @@ -169,6 +191,18 @@ 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 + entrypoint: main + definitions: + PREPASS: 1 shadow_vertex: source: shadow_vertex target: vertex @@ -181,6 +215,12 @@ shaders: source: instanced_vertex target: vertex entrypoint: main + instanced_prepass_vertex: + source: instanced_vertex + target: vertex + entrypoint: main + definitions: + PREPASS: 1 instanced_shadow_vertex: source: instanced_shadow_vertex target: vertex @@ -285,13 +325,6 @@ shaders: source: manul/line target: pixel entrypoint: pix_main -# Deferred light primitives - gbuffer_light_clear: - use_template: gbuffer_lights - entrypoint: CS_Clear - gbuffer_light_spot: - use_template: gbuffer_lights - entrypoint: CS_Spotlight # Forward+ light culling forwardplus_compute_frustums: source: manul/forward_plus/forward_plus diff --git a/betterRenderer/shaders/ps_shadow_masked.hlsl b/betterRenderer/shaders/ps_shadow_masked.hlsl index eb19eff4..d666507b 100644 --- a/betterRenderer/shaders/ps_shadow_masked.hlsl +++ b/betterRenderer/shaders/ps_shadow_masked.hlsl @@ -3,8 +3,13 @@ sampler diffuse_sampler : register(s0); Texture2D diffuse : register(t0); +#ifdef PREPASS +#include "manul/draw_constants.hlsli" +#else #include "manul/draw_constants_shadow.hlsli" +#endif +#define SHADOW #include "manul/alpha_mask.hlsli" struct VertexOutput { diff --git a/betterRenderer/shaders/ps_windshield_rain.hlsl b/betterRenderer/shaders/ps_windshield_rain.hlsl new file mode 100644 index 00000000..ec424b08 --- /dev/null +++ b/betterRenderer/shaders/ps_windshield_rain.hlsl @@ -0,0 +1,83 @@ +#include "manul/math.hlsli" +#include "manul/material.hlsli" +#include "manul/color_transform.hlsli" + +sampler diffuse_sampler : register(s0); +sampler raindrop_sampler : register(s1); +sampler wipermask_sampler : register(s2); +Texture2D diffuse : register(t0); +Texture2D raindropsatlas : register(t1); +Texture2D wipermask : register(t2); +Texture2D rain : register(t3); + +// 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); +} + +// 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 + + MaterialData material_glass = material; + float4 tex_color = diffuse.Sample(diffuse_sampler, material.m_TexCoord); + 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 * -.0075); + 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)); + + float3 normal_world = mul((float3x3)g_InverseModelView, material_glass.m_MaterialNormal); + + float4 glass_lit; + ApplyMaterialLighting(glass_lit, material_glass, material_glass.m_PixelCoord); + + material.m_MaterialEmission = glass_lit * smoothstep(0., .15, droplet_distance) * smoothstep(-1., 0., normal_world.y); + + material.m_MaterialAlbedoAlpha.xyz = 0.; + material.m_RefractionOffset = normal.xy * (.005 / (length(material.m_Position) * tan(.5 * g_VerticalFov))) * smoothstep(0., .15, droplet_distance); + + 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; + + + { // 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 +} diff --git a/betterRenderer/shaders/ps_windshield_rain_anim.hlsl b/betterRenderer/shaders/ps_windshield_rain_anim.hlsl new file mode 100644 index 00000000..81556849 --- /dev/null +++ b/betterRenderer/shaders/ps_windshield_rain_anim.hlsl @@ -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 raindropsatlas : register(t0); +Texture2D 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; +} diff --git a/gl/glsl_common.cpp b/gl/glsl_common.cpp index 785e08db..2ca344f4 100644 --- a/gl/glsl_common.cpp +++ b/gl/glsl_common.cpp @@ -72,6 +72,10 @@ void gl::glsl_common_setup() mat4 lightview[MAX_CASCADES]; vec3 cascade_end; float time; + vec4 rain_params; + vec4 wiper_pos; + vec4 wiper_timer_out; + vec4 wiper_timer_return; }; )STRING"; diff --git a/gl/ubo.h b/gl/ubo.h index 56ca593a..8bac33b7 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -45,9 +45,13 @@ namespace gl glm::mat4 lightview[MAX_CASCADES]; glm::vec3 cascade_end; float time; + glm::vec4 rain_params; + glm::vec4 wiper_pos; + glm::vec4 wiper_timer_out; + glm::vec4 wiper_timer_return; }; - static_assert(sizeof(scene_ubs) == 336, "bad size of ubs"); + static_assert(sizeof(scene_ubs) == 400, "bad size of ubs"); const size_t MAX_PARAMS = 3; diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index c6449881..11a1b465 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -694,6 +694,46 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) m_colorpass = m_renderpass; // cache pass data scene_ubs.time = Timer::GetTime(); + { + float percipitation_intensity = glm::saturate(Global.Overcast - 1.); + scene_ubs.rain_params.x = percipitation_intensity; // % amount of droplets + scene_ubs.rain_params.y = glm::mix(15., 1., percipitation_intensity); // Regeneration time + if (TDynamicObject const *owner = Global.pCamera.m_owner; owner && !!owner->MoverParameters->CabActive) + { + for (int i = 0; i < 4; ++i) + { + if (i < owner->dWiperPos.size()) + { + int index = owner->MoverParameters->CabActive > 0 ? i : static_cast(owner->dWiperPos.size() - 1) - i; + scene_ubs.wiper_pos[i] = owner->dWiperPos[index]; + if (owner->dWiperPos[index] > 0. && owner->wiperDirection[index]) + { + scene_ubs.wiper_pos[i] += 1.; + } + if (owner->dWiperPos[index] < .025) + { + scene_ubs.wiper_timer_out[i] = scene_ubs.time; + } + if (owner->dWiperPos[index] > .975) + { + scene_ubs.wiper_timer_return[i] = scene_ubs.time; + } + } + else + { + scene_ubs.wiper_pos[i] = 0.; + scene_ubs.wiper_timer_out[i] = -1000.; + scene_ubs.wiper_timer_return[i] = -1000.; + } + } + } + else + { + scene_ubs.wiper_pos = glm::vec4{0.}; + scene_ubs.wiper_timer_out = glm::vec4{-1000.}; + scene_ubs.wiper_timer_return = glm::vec4{-1000.}; + } + } scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION); scene_ubs.inv_view = glm::inverse( glm::mat4{ glm::mat3{ m_colorpass.pass_camera.modelview() } } ); scene_ubo->update(scene_ubs); diff --git a/shaders/mat_rain_windscreen.frag b/shaders/mat_rain_windscreen.frag new file mode 100644 index 00000000..f7f057ca --- /dev/null +++ b/shaders/mat_rain_windscreen.frag @@ -0,0 +1,212 @@ +// Windshield rain effects +// by @lcddisplay; wiper movement @MichauSto + +in vec3 f_normal; +in vec2 f_coord; +in vec4 f_pos; +#include + +layout(location = 0) out vec4 out_color; + +#if MOTIONBLUR_ENABLED +layout(location = 1) out vec4 out_motion; +#endif + +#texture(diffuse, 0, sRGB_A) +#texture(raindropsatlas, 1, sRGB_A) +#texture(wipermask, 2, sRGB_A) +#param (color, 0, 0, 4, diffuse) +#param (diffuse, 1, 0, 1, diffuse) +#param (specular, 1, 1, 1, specular) +#param (reflection, 1, 2, 1, zero) +#param (glossiness, 1, 3, 1, glossiness) +#param (raindrop_grid_size, 2, 0, 1, one) + +#include +#include +#include + +uniform sampler2D diffuse; +uniform sampler2D raindropsatlas; +uniform sampler2D wipermask; + +uniform float specular_intensity = 1.0; +uniform float wobble_strength = 0.002; +uniform float wobble_speed = 30.0; + +float hash(vec2 p) { + return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); +} + +vec4 getDropTex(float choice, vec2 uv) { + vec2 offset; + if (choice < 0.25) offset = vec2(0.0, 0.0); + else if (choice < 0.5) offset = vec2(0.5, 0.0); + else if (choice < 0.75) offset = vec2(0.0, 0.5); + else offset = vec2(0.5, 0.5); + return texture(raindropsatlas, offset + uv * 0.5); +} + +float GetMixFactor(in vec2 co, out float side); + +void main() { + vec4 tex_color = texture(diffuse, f_coord); + if (tex_color.a < 0.01) discard; + + vec2 rainCoord = f_coord; + + const float numDrops = 20000.0; + const float cycleDuration = 4.0; + const float squareMin = 0.0035; + const float squareMax = 0.008; + + float gridSize = ceil(param[2].x); + vec2 cell = floor(rainCoord * gridSize); + + vec3 dropLayer = vec3(0.0); + float dropMaskSum = 0.0; + + // Grid of 9 droplets in immediate neighbourhood + for (int oy = -1; oy <= 1; oy++) { + for (int ox = -1; ox <= 1; ox++) { + + vec2 neighborCell = cell + vec2(ox, oy); + vec2 neighborCenter = (neighborCell + .5) / gridSize; + + float side; + float mixFactor = GetMixFactor(neighborCenter, side); + + if(mixFactor < hash(neighborCell + vec2(mix(0., .2137, side), 0.))) { + continue; + } + + float i = neighborCell.x + neighborCell.y * gridSize; + + // Show a percentage of droplets given by rain intensity param + float activationSeed = hash(vec2(i, 0.333 + side)); + if (activationSeed > rain_params.x) + continue; // kropla nieaktywna + + // Randomly modulate droplet center & size + vec2 dropCenter = (neighborCell + vec2(hash(vec2(i, 0.12 + side)), hash(vec2(i, 0.34 + side)))) / gridSize; + float squareSize = mix(squareMin, squareMax, hash(vec2(i, 0.56 + side))); + + float lifeTime = time + hash(vec2(i, 0.78 + side)) * cycleDuration; + float phase = fract(lifeTime / cycleDuration); + float active = clamp(1.0 - phase, 0.0, 1.0); + + // Gravity influence (TODO add vehicle speed & wind here!) + float gravityStart = 0.5; + float gravityPhase = smoothstep(gravityStart, 1.0, phase); + float dropMass = mix(0.3, 1.2, hash(vec2(i, 0.21 + side))); + float gravitySpeed = 0.15 * dropMass; + vec2 gravityOffset = vec2(0.0, gravityPhase * gravitySpeed * phase); + + // Random wobble + bool hasWobble = (hash(vec2(i, 0.91 + side)) < 0.10); + vec2 wobbleOffset = vec2(0.0); + if (hasWobble && gravityPhase > 0.0) { + float intensity = sin(time * wobble_speed + i) * wobble_strength * gravityPhase; + wobbleOffset = vec2(intensity, 0.0); + } + + vec2 slideOffset = gravityOffset + wobbleOffset; + + // Flatten droplets influenced by gravity + float flattenAmount = smoothstep(0.1, 0.5, gravityPhase); + float flattenX = mix(1.0, 0.4, flattenAmount); + float stretchY = mix(1.0, 1.6, flattenAmount); + + // Droplet local position & mask + vec2 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 > 0.001) { + vec2 localUV = (diff + squareSize * 0.5) / squareSize; + float choice = hash(vec2(i, 0.99 + side)); + vec4 dropTex = getDropTex(choice, localUV); + float sharpAlpha = smoothstep(0.3, 0.9, dropTex.a); + + float ambLum = clamp(dot(ambient, vec3(0.2126, 0.7152, 0.0722)), 0.0, 1.0); + float sunFactor = pow(clamp(ambLum * 6.0, 0.0, 1.0), 0.5); + float dynBright = mix(0.0, 1.0, sunFactor); + + float colorLuma = length(dropTex.rgb); + float alphaRange = smoothstep(0.1, 0.3, colorLuma); + float blackAlpha = mix(0.25, 0.85, alphaRange); + + float sparkle = 0.0; + if (hasWobble) { + sparkle = pow(abs(sin(f_coord.x * 8.0 + time * 2.0 + hash(vec2(i, 0.9 + side)) * 6.2831)), 40.0) + * mix(0.2, 1.0, sunFactor); + } + + vec3 specularColor = vec3(1.0) * specular_intensity * sparkle; + vec3 dropLit = dropTex.rgb * dynBright + specularColor * sharpAlpha; + + dropLayer += dropLit * sharpAlpha * active * blackAlpha * mask; + dropMaskSum += sharpAlpha * active * blackAlpha * mask; + } + } + } + vec3 finalMix = dropLayer; + float alphaOut = clamp(dropMaskSum, 0.0, 1.0); + out_color = vec4(finalMix, alphaOut); + + { // Overlay windshield texture with alpha + vec3 fragcolor = ambient; + vec3 fragnormal = normalize(f_normal); + float reflectivity = param[1].z; + float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; + glossiness = abs(param[1].w); + + fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); + vec4 color = vec4(fragcolor, tex_color.a * alpha_mult); + + out_color.xyz = apply_fog(mix(out_color.xyz, color.xyz, color.a)); + out_color.a = mix(out_color.a, 1., color.a); + } + +#if MOTIONBLUR_ENABLED + out_motion = vec4(0.0); +#endif +} + +float GetMixFactor(in vec2 co, out float side) { + vec4 movePhase = wiper_pos; + bvec4 is_out = lessThanEqual(movePhase, vec4(1.)); + movePhase = mix(vec4(2.) - movePhase, movePhase, is_out); + + vec4 mask = texture(wipermask, co); + + vec4 areaMask = step(.001, mask); + + vec4 maskVal = mix(1. - mask, mask, is_out); + vec4 wipeWidth = smoothstep(1., .9, movePhase) * .25; + vec4 cleaned = smoothstep(movePhase - wipeWidth, movePhase, maskVal) * areaMask; + vec4 side_v = step(maskVal, movePhase); + cleaned *= side_v; + side_v = mix(side_v, vec4(1.) - side_v, is_out); + + // "regeneration", raindrops gradually returning after wiper pass: + vec4 regenPhase = clamp((vec4(time) - mix(wiper_timer_out, wiper_timer_return, side_v) - vec4(.2)) / vec4(rain_params.y), vec4(0.), vec4(1.)); + + side_v = mix(vec4(0.), vec4(1.) - side_v, areaMask); + + vec4 factor_v = mix(vec4(1.), regenPhase * (vec4(1.) - cleaned), areaMask); + + side = 0.; + float out_factor = 1.; + + // Find out the wiper blade that influences given grid cell the most + for(int i = 0; i < 4; ++i) + { + bool is_candidate = factor_v[i] < out_factor; + out_factor = mix(out_factor, factor_v[i], is_candidate); + side = mix(side, side_v[i], is_candidate); + } + + return out_factor; +} diff --git a/shaders/postfx_chromaticaberration.frag b/shaders/postfx_chromaticaberration.frag index c7f98d18..26a36045 100644 --- a/shaders/postfx_chromaticaberration.frag +++ b/shaders/postfx_chromaticaberration.frag @@ -3,19 +3,55 @@ in vec2 f_coords; layout(location = 0) out vec4 out_color; #texture (color_tex, 0, RGB) + uniform sampler2D iChannel0; -void main() -{ - float amount = 0.001; - - vec2 uv = f_coords; - vec3 col; - col.r = texture( iChannel0, vec2(uv.x+amount,uv.y) ).r; - col.g = texture( iChannel0, uv ).g; - col.b = texture( iChannel0, vec2(uv.x-amount,uv.y) ).b; +vec2 barrelDistortion(vec2 f_coords, float amt) { + vec2 cc = f_coords - 0.5; + float dist = dot(cc, cc); + return f_coords + cc * dist * amt; +} - col *= (1.0 - amount * 0.5); - - out_color = vec4(col,1.0); -} +float sat( float t ) +{ + return clamp( t, 0.0, 1.0 ); +} + +float linterp( float t ) { + return sat( 1.0 - abs( 1.0*t - 1.0 ) ); +} + +float remap( float t, float a, float b ) { + return sat( (t - a) / (b - a) ); +} + +vec4 spectrum_offset( float t ) { + vec4 ret; + float lo = step(t,0.5); + float hi = 1.0-lo; + float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) ); + ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.); + + return pow( ret, vec4(1.0/2.2) ); +} + +const float max_distort = 0.05; +const int num_iter = 10; +const float reci_num_iter_f = 1.0 / float(num_iter); + +void main() +{ + vec2 uv=f_coords.xy; + + vec4 sumcol = vec4(0.0); + vec4 sumw = vec4(0.0); + for ( int i=0; i> 16u; + s *= 2654435769u; + s ^= s >> 16u; + s *= 2654435769u; + return s; +} + +uint Hash(uvec2 s) { + return HashCombine(Hash(s.x), s.y); +} + +uint Hash(uvec3 s) { + return HashCombine(Hash(s.xy), s.z); +} + +uint HashCombine(uint seed, uint value) { + return seed ^ (Hash(value) + 0x9e3779b9u + (seed<<6u) + (seed>>2u)); +} + +uint Rand(inout uint seed) { + seed = Hash(seed); + return seed; +} + +float RandF(inout uint seed) { + return float(Rand(seed)) * 2.3283064365386963e-10; +} diff --git a/widgets/perfgraphs.cpp b/widgets/perfgraphs.cpp index 277f93f0..5d37a332 100644 --- a/widgets/perfgraphs.cpp +++ b/widgets/perfgraphs.cpp @@ -33,6 +33,8 @@ void perfgraph_panel::render_contents() { stopwatch = &Timer::subsystem.gfx_swap; else if (current_timer == gfx_gui) stopwatch = &Timer::subsystem.gfx_gui; + else if (current_timer == gfx_animate) + stopwatch = &Timer::subsystem.gfx_animate; else if (current_timer == sim_total) stopwatch = &Timer::subsystem.sim_total; else if (current_timer == sim_dynamics) diff --git a/widgets/perfgraphs.h b/widgets/perfgraphs.h index f00c554a..302a2e12 100644 --- a/widgets/perfgraphs.h +++ b/widgets/perfgraphs.h @@ -12,6 +12,7 @@ class perfgraph_panel : public ui_panel gfx_reflections, gfx_swap, gfx_gui, + gfx_animate, sim_total, sim_dynamics, sim_events, @@ -31,6 +32,7 @@ class perfgraph_panel : public ui_panel "gfx_reflections", "gfx_swap", "gfx_gui", + "gfx_animate", "sim_total", "sim_dynamics", "sim_events",