mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-30 23:40:29 +01:00
Add screen-space contact shadows for the sun, lamps and headlights
This commit is contained in:
@@ -740,10 +740,14 @@ void opengl46_renderer::SwapBuffers()
|
||||
m_debugtimestext += m_exposure.info_times();
|
||||
m_debugtimestext += m_ao.info_times();
|
||||
m_debugtimestext += m_instancecull.info_times();
|
||||
if( Global.gfx_depthprepass_scene ) {
|
||||
if( Global.gfx_depthprepass_scene || m_contactshadowslast ) {
|
||||
if( auto const scenedepthtime = m_scenedepthtimer.result() )
|
||||
m_debugtimestext += " scene depth: " + to_string( static_cast<double>( *scenedepthtime ) / 1000000.0, 3 ) + " ms\n";
|
||||
}
|
||||
if( m_contactshadowslast ) {
|
||||
if( auto const contacttime = m_contactshadowstimer.result() )
|
||||
m_debugtimestext += " contact shadows depth: " + to_string( static_cast<double>( *contacttime ) / 1000000.0, 3 ) + " ms\n";
|
||||
}
|
||||
if( Global.gfx_gpucull == 1 ) {
|
||||
m_debugtimestext += m_hiz.info_times();
|
||||
if( auto const prepasstime = m_prepasstimer.result() )
|
||||
@@ -770,9 +774,15 @@ void opengl46_renderer::SwapBuffers()
|
||||
+ " instanced:" + to_string( m_colorpass.draw_stats.instances, 7 ) + " +" + to_string( shadowstats.instances, 7 )
|
||||
+ " =" + to_string( m_colorpass.draw_stats.instances + shadowstats.instances, 7 )
|
||||
+ " (" + std::to_string( m_colorpass.draw_stats.instanced_drawcalls + shadowstats.instanced_drawcalls ) + " batches)\n"
|
||||
+ ( Global.gfx_depthprepass_scene ?
|
||||
+ ( ( Global.gfx_depthprepass_scene || m_contactshadowslast ) ?
|
||||
" scene depth: " + std::to_string( m_scenedepthstats.drawcalls ) + " draws, cpu " + to_string( m_scenedepthcputime, 2 ) + " ms\n" :
|
||||
std::string{} )
|
||||
+ ( Global.gfx_contactshadows > 0 ?
|
||||
" contact shadows: " + std::string{ Global.gfx_contactshadows == 3 ? "high" : Global.gfx_contactshadows == 2 ? "medium" : "low" }
|
||||
+ ( m_contactshadowslast ?
|
||||
", rays " + to_string( Global.gfx_contactshadows_length, 2 ) + " m within " + to_string( Global.gfx_contactshadows_range, 0 ) + " m\n" :
|
||||
", off in this view\n" ) :
|
||||
std::string{} )
|
||||
+ " instance buffer: " + std::to_string( m_instancestream->used() / static_cast<GLsizeiptr>( sizeof( glm::mat4 ) ) ) + " matrices, "
|
||||
+ std::to_string( m_instancestream->size() / 1024 ) + " KiB\n"
|
||||
+ " model blocks: " + std::to_string( model_ubo->updates() )
|
||||
@@ -853,6 +863,11 @@ void opengl46_renderer::draw_debug_ui()
|
||||
m_locallights.draw_debug_ui();
|
||||
ImGui::Separator();
|
||||
m_localshadows.draw_debug_ui();
|
||||
ImGui::Separator();
|
||||
char const *contactlevels[]{"no", "low (the sun)", "medium (the sun and 2 lamps)", "high (the sun and 4 lamps)"};
|
||||
ImGui::Combo("contact shadows (gfx.contactshadows)", &Global.gfx_contactshadows, contactlevels, IM_ARRAYSIZE(contactlevels));
|
||||
ImGui::SliderFloat("length of their rays, m (gfx.contactshadows.length)", &Global.gfx_contactshadows_length, 0.05f, 5.f, "%.2f", ImGuiSliderFlags_Logarithmic);
|
||||
ImGui::SliderFloat("distance they reach, m (gfx.contactshadows.range)", &Global.gfx_contactshadows_range, 1.f, 1000.f, "%.0f", ImGuiSliderFlags_Logarithmic);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
@@ -882,6 +897,8 @@ void opengl46_renderer::draw_debug_ui()
|
||||
ImGui::Checkbox("draw the depth of the instances before their colour (gfx.depthprepass)", &Global.gfx_depthprepass);
|
||||
}
|
||||
ImGui::Checkbox("draw the depth of the whole scene before its colour (gfx.depthprepass.scene)", &Global.gfx_depthprepass_scene);
|
||||
if (Global.gfx_contactshadows > 0)
|
||||
ImGui::TextUnformatted("(drawn anyway for the contact shadows, gfx.contactshadows)");
|
||||
auto const info{m_instancecull.info()};
|
||||
if (!info.empty())
|
||||
ImGui::TextUnformatted(info.c_str() + 1);
|
||||
@@ -1124,14 +1141,19 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
|
||||
if( Global.gfx_shadowmap_enabled )
|
||||
setup_shadow_bind_map();
|
||||
|
||||
// the depth of the whole opaque scene before its colour
|
||||
// the depth of the whole opaque scene before its colour, which the contact shadows need
|
||||
m_scenedepthdrawn = false;
|
||||
m_indirectcolourpending = false;
|
||||
if (Global.gfx_depthprepass_scene && vp.main && (false == Global.bWireFrame))
|
||||
auto const contactshadows{Contact_Shadows_Wanted(vp)};
|
||||
if ((Global.gfx_depthprepass_scene || contactshadows) && vp.main && (false == Global.bWireFrame))
|
||||
{
|
||||
glDebug("render scene depth");
|
||||
Render_Scene_Depth(future);
|
||||
if (contactshadows)
|
||||
Setup_Contact_Shadows(vp);
|
||||
}
|
||||
if (vp.main)
|
||||
m_contactshadowslast = m_contactshadows;
|
||||
|
||||
// opaque parts...
|
||||
setup_drawing(false);
|
||||
@@ -1203,6 +1225,15 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
|
||||
}
|
||||
}
|
||||
|
||||
if (m_contactshadows)
|
||||
{
|
||||
// the depth they read is of this view only
|
||||
m_contactshadows = false;
|
||||
scene_ubs.contact_shadow_params = glm::vec4{0.f};
|
||||
scene_ubs.contact_shadow_lamps = glm::vec4{0.f};
|
||||
scene_ubo->update(scene_ubs);
|
||||
}
|
||||
|
||||
Timer::subsystem.gfx_color.stop();
|
||||
|
||||
// store draw stats
|
||||
@@ -3957,6 +3988,46 @@ bool opengl46_renderer::Scene_Depth_Draws( gfx::geometry_handle const &Geometry
|
||||
return ( ( type == GL_TRIANGLES ) || ( type == GL_TRIANGLE_STRIP ) || ( type == GL_TRIANGLE_FAN ) );
|
||||
}
|
||||
|
||||
// the contact shadows read the depth of the scene drawn before the colour, which the main view has, and work out the distance
|
||||
// of a surface from its depth the way a perspective projection with the far plane at infinity gives it (depth = near / w):
|
||||
// not in the orthographic view of the editor
|
||||
bool opengl46_renderer::Contact_Shadows_Wanted( viewport_config const &Viewport ) const
|
||||
{
|
||||
if( ( Global.gfx_contactshadows <= 0 ) || ( false == Viewport.main ) || Global.bWireFrame || Global.gfx_skippipeline || ( Viewport.msaa_fb == nullptr ) ) {
|
||||
return false;
|
||||
}
|
||||
auto const &projection { scene_ubs.projection };
|
||||
return ( projection[ 2 ][ 3 ] != 0.f ) && ( projection[ 2 ][ 2 ] == 0.f ) && ( projection[ 3 ][ 2 ] > 0.f );
|
||||
}
|
||||
|
||||
void opengl46_renderer::Setup_Contact_Shadows( viewport_config &Viewport )
|
||||
{
|
||||
if( false == Resolved_Depth_Target( Viewport ) ) { return; }
|
||||
// making the target binds it
|
||||
Viewport.msaa_fb->bind();
|
||||
|
||||
m_contactshadowstimer.begin();
|
||||
// the depth is resolved apart from the buffer the colour pass draws to, which it tests and writes. the scissor test would limit the blit
|
||||
auto const scissor { ::glIsEnabled( GL_SCISSOR_TEST ) == GL_TRUE };
|
||||
if( scissor ) { ::glDisable( GL_SCISSOR_TEST ); }
|
||||
::glBlitNamedFramebuffer(
|
||||
*Viewport.msaa_fb, *Viewport.clusters_depth_fb,
|
||||
0, 0, Viewport.width, Viewport.height, 0, 0, Viewport.width, Viewport.height,
|
||||
GL_DEPTH_BUFFER_BIT, GL_NEAREST );
|
||||
if( scissor ) { ::glEnable( GL_SCISSOR_TEST ); }
|
||||
::glBindTextureUnit( gl::SCENE_DEPTH_TEX, Viewport.clusters_depth_tex->id );
|
||||
m_contactshadowstimer.end();
|
||||
|
||||
// steps of the ray towards the sun, of the rays towards lamps and headlights, and how many of those a pixel traces
|
||||
struct contact_level { float sunsteps; float lampsteps; float lamps; };
|
||||
static contact_level const levels[] { { 8.f, 0.f, 0.f }, { 12.f, 8.f, 2.f }, { 16.f, 12.f, 4.f } };
|
||||
auto const &level { levels[ std::clamp( Global.gfx_contactshadows, 1, 3 ) - 1 ] };
|
||||
scene_ubs.contact_shadow_params = glm::vec4{ Global.gfx_contactshadows_length, Global.gfx_contactshadows_range, level.sunsteps, level.lampsteps };
|
||||
scene_ubs.contact_shadow_lamps = glm::vec4{ level.lamps, 0.f, 0.f, 0.f };
|
||||
scene_ubo->update( scene_ubs );
|
||||
m_contactshadows = true;
|
||||
}
|
||||
|
||||
// Renders the per-track sleeper instances (TTrack::m_sleeper_local_transforms) using the
|
||||
// existing GPU-instanced submodel pipeline. The track owns a vector of pre-baked
|
||||
// local-space matrices; we compose each with `view * translate(track_origin - camera)`
|
||||
@@ -7063,6 +7134,31 @@ void opengl46_renderer::Render_ambient_occlusion(viewport_config &vp, gl::frameb
|
||||
m_ao.apply(Mode, *vp.main_texd, *vp.ssao_blur_tex, Target, projection);
|
||||
}
|
||||
|
||||
bool opengl46_renderer::Resolved_Depth_Target(viewport_config &vp)
|
||||
{
|
||||
if (vp.clusters_depth_fb)
|
||||
return true;
|
||||
|
||||
auto texture{std::make_unique<opengl_texture>()};
|
||||
texture->alloc_rendertarget(Global.gfx_format_depth, GL_DEPTH_COMPONENT, vp.width, vp.height, 1, 1, GL_CLAMP_TO_EDGE);
|
||||
// the values themselves are read, texel by texel
|
||||
glTextureParameteri(texture->id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTextureParameteri(texture->id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
auto framebuffer{std::make_unique<gl::framebuffer>()};
|
||||
framebuffer->attach(*texture, GL_DEPTH_ATTACHMENT);
|
||||
// depth only
|
||||
framebuffer->setup_drawing(0);
|
||||
if (!framebuffer->is_complete())
|
||||
{
|
||||
ErrorLog("resolved depth framebuffer setup failed");
|
||||
return false;
|
||||
}
|
||||
vp.clusters_depth_tex = std::move(texture);
|
||||
vp.clusters_depth_fb = std::move(framebuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void opengl46_renderer::Render_cluster_heatmap(viewport_config &vp, gl::framebuffer *Target, glm::ivec2 const &Targetsize)
|
||||
{
|
||||
glDebug("Render_cluster_heatmap");
|
||||
@@ -7070,26 +7166,10 @@ void opengl46_renderer::Render_cluster_heatmap(viewport_config &vp, gl::framebuf
|
||||
if (!vp.msaa_fb)
|
||||
return;
|
||||
|
||||
if (!vp.clusters_depth_fb)
|
||||
if (!Resolved_Depth_Target(vp))
|
||||
{
|
||||
auto texture{std::make_unique<opengl_texture>()};
|
||||
texture->alloc_rendertarget(Global.gfx_format_depth, GL_DEPTH_COMPONENT, vp.width, vp.height, 1, 1, GL_CLAMP_TO_EDGE);
|
||||
// the values themselves are read, texel by texel
|
||||
glTextureParameteri(texture->id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTextureParameteri(texture->id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
auto framebuffer{std::make_unique<gl::framebuffer>()};
|
||||
framebuffer->attach(*texture, GL_DEPTH_ATTACHMENT);
|
||||
// depth only
|
||||
framebuffer->setup_drawing(0);
|
||||
if (!framebuffer->is_complete())
|
||||
{
|
||||
ErrorLog("cluster heatmap framebuffer setup failed");
|
||||
Global.gfx_lights_clusterheatmap = false;
|
||||
return;
|
||||
}
|
||||
vp.clusters_depth_tex = std::move(texture);
|
||||
vp.clusters_depth_fb = std::move(framebuffer);
|
||||
Global.gfx_lights_clusterheatmap = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// the depth of the scene is still in the msaa buffer, the post processing worked on its colour
|
||||
|
||||
@@ -277,7 +277,8 @@ class opengl46_renderer : public gfx_renderer {
|
||||
|
||||
// light cluster grid of the viewport
|
||||
std::unique_ptr<gl::cluster_grid> clusters;
|
||||
// depth of the finished scene resolved from the msaa buffer, for the cluster heatmap; made when it is first needed
|
||||
// depth of the scene resolved from the msaa buffer (Resolved_Depth_Target()): of the finished scene for the cluster heatmap,
|
||||
// and of the depth pass of the scene for the contact shadows, read while the colour is drawn. made when it is first needed
|
||||
std::unique_ptr<gl::framebuffer> clusters_depth_fb;
|
||||
std::unique_ptr<opengl_texture> clusters_depth_tex;
|
||||
|
||||
@@ -362,6 +363,12 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// walks the scene a second time, Bind_Material() binds the programs of the depth prepass and draw() leaves out what they
|
||||
// can't draw. Future: the motion matrix the colour pass draws the region with
|
||||
void Render_Scene_Depth( glm::mat4 const &Future );
|
||||
// whether the colour pass of the viewport gets contact shadows, which need the depth pass of the scene
|
||||
bool Contact_Shadows_Wanted( viewport_config const &Viewport ) const;
|
||||
// resolves the depth drawn by Render_Scene_Depth() for the contact shadows and sets them up for the colour pass
|
||||
void Setup_Contact_Shadows( viewport_config &Viewport );
|
||||
// makes the target of the resolved depth of the viewport if it isn't there yet; false if it can't be made
|
||||
bool Resolved_Depth_Target( viewport_config &Viewport );
|
||||
// whether the depth pass of the whole scene draws the material bound last (Bind_Material()) and the geometry
|
||||
bool Scene_Depth_Draws( gfx::geometry_handle const &Geometry ) const;
|
||||
bool Render(TDynamicObject *Dynamic);
|
||||
@@ -553,6 +560,10 @@ class opengl46_renderer : public gfx_renderer {
|
||||
debug_stats m_scenedepthstats; // of its last run
|
||||
double m_scenedepthcputime { 0.0 }; // ms
|
||||
gl::gpu_timer m_scenedepthtimer;
|
||||
// contact shadows: set up for the colour pass under way, and whether the last main view had them
|
||||
bool m_contactshadows { false };
|
||||
bool m_contactshadowslast { false };
|
||||
gl::gpu_timer m_contactshadowstimer; // resolve of the depth
|
||||
renderpass_config m_colorpass; // parametrs of most recent color pass
|
||||
std::array<renderpass_config, 3> m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages
|
||||
std::vector<TSubModel const *> m_pickcontrolsitems;
|
||||
|
||||
Reference in New Issue
Block a user