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

Add gfx.antialiasing (off, msaax2/x4/x8, taa) with temporal anti-aliasing in the GL 4.6 and Vulkan renderers

This commit is contained in:
2026-10-25 05:17:41 +01:00
parent c48c58776a
commit 252b9373ea
20 changed files with 838 additions and 35 deletions

View File

@@ -1640,7 +1640,7 @@ bool scene_walk::Render(TDynamicObject *Dynamic)
glm::mat4 future_stack = model_ubs.future;
glm::mat4 mv = OpenGLMatrices.data(GL_MODELVIEW);
model_ubs.future *= glm::translate(mv, glm::vec3(Dynamic->get_future_movement())) * glm::inverse(mv);
model_ubs.future *= glm::translate(mv, glm::vec3(m_motionhistory ? -Dynamic->get_future_movement() : Dynamic->get_future_movement())) * glm::inverse(mv);
// the velocity it leaves in the colour pass keeps the motion blur on (Render_pass())
if ((m_renderpass.draw_mode == rendermode::color) && (Dynamic->get_future_movement() != glm::dvec3{0.0}))
m_vehiclesmoving = true;
@@ -1901,7 +1901,7 @@ void scene_walk::Render(TSubModel *Submodel)
Submodel->RaAnimation(Submodel->b_aAnim);
glm::mat4 mv = OpenGLMatrices.data(GL_MODELVIEW);
model_ubs.future *= (mv * Submodel->future_transform) * glm::inverse(mv);
model_ubs.future *= (mv * (m_motionhistory ? glm::inverse(Submodel->future_transform) : Submodel->future_transform)) * glm::inverse(mv);
// the placement may be different in the next frame: the walk isn't kept (Record_Indirect_Draws())
m_indirectchanging = true;
}
@@ -2716,7 +2716,7 @@ bool scene_walk::Render_Alpha(TDynamicObject *Dynamic)
glm::mat4 future_stack = model_ubs.future;
glm::mat4 mv = OpenGLMatrices.data(GL_MODELVIEW);
model_ubs.future *= glm::translate(mv, glm::vec3(Dynamic->get_future_movement())) * glm::inverse(mv);
model_ubs.future *= glm::translate(mv, glm::vec3(m_motionhistory ? -Dynamic->get_future_movement() : Dynamic->get_future_movement())) * glm::inverse(mv);
// the velocity it leaves in the colour pass keeps the motion blur on (Render_pass())
if ((m_renderpass.draw_mode == rendermode::color) && (Dynamic->get_future_movement() != glm::dvec3{0.0}))
m_vehiclesmoving = true;
@@ -2846,7 +2846,7 @@ void scene_walk::Render_Alpha(TSubModel *Submodel)
Submodel->RaAnimation(Submodel->b_aAnim);
glm::mat4 mv = OpenGLMatrices.data(GL_MODELVIEW);
model_ubs.future *= (mv * Submodel->future_transform) * glm::inverse(mv);
model_ubs.future *= (mv * (m_motionhistory ? glm::inverse(Submodel->future_transform) : Submodel->future_transform)) * glm::inverse(mv);
}
}

View File

@@ -502,6 +502,12 @@ class scene_walk
std::vector<gl::instance_draw_command> m_indirectcommandsordered;
// a vehicle drawn in the colour pass of this frame moved (motion blur)
bool m_vehiclesmoving{false};
// the velocities point to the last frame (the temporal anti-aliasing, gfx::temporal_aa) instead of the next one (the motion
// blur alone): the moving vehicles and the turning wheels add their motion to the future of the draws backwards
bool m_motionhistory{false};
// the future of the draws of the cab and the interiors of its vehicle: the identity (they move with the camera), or the matrix
// of their view of the last frame (gfx::temporal_aa::cab_future())
glm::mat4 m_cabfuture{1.f};
// set while a preview is drawn: submodels faded out entirely are skipped, instead of being drawn invisible over the stencil
bool m_previewing{false};
// the last lighting of the pass placed the cab lamp among the lights: the cab is lit by it, not by an addition to the ambient light

View File

@@ -0,0 +1,89 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "rendering/common/temporalaa.h"
#include "vehicle/DynObj.h"
namespace
{
// the offsets go round this many frames: enough to cover a pixel evenly, few enough for the history to hold all of them
constexpr std::uint32_t JITTER_PHASES{8};
// the camera moved farther than this from one frame to the next (another camera, another vehicle, a jump): the history is dropped
constexpr double CAMERA_JUMP{50.0};
// the halton sequence: the index written in the base, mirrored behind the point
float halton(std::uint32_t Index, std::uint32_t const Base)
{
auto fraction{1.f};
auto result{0.f};
while (Index > 0)
{
fraction /= static_cast<float>(Base);
result += fraction * static_cast<float>(Index % Base);
Index /= Base;
}
return result;
}
} // namespace
glm::mat4 gfx::temporal_aa::begin_frame(glm::mat4 const &Projection, glm::dmat3 const &View, glm::dvec3 const &Camera, TDynamicObject const *Cab, glm::ivec2 const &Size)
{
m_phase = (m_phase + 1) % JITTER_PHASES;
// the first element of the sequence is 0 in every base; from the second one on they are spread over the pixel
m_jitter = glm::vec2{halton(m_phase + 1, 2), halton(m_phase + 1, 3)} - 0.5f;
glm::dvec2 const size{static_cast<double>(std::max(Size.x, 1)), static_cast<double>(std::max(Size.y, 1))};
// the clip space is two units across the image
auto const offset{glm::translate(glm::dmat4{1.0}, glm::dvec3{2.0 * glm::dvec2{m_jitter} / size, 0.0})};
frame current;
current.projection = glm::dmat4{Projection};
current.view = View;
current.camera = Camera;
current.vehicle = Cab;
if (Cab != nullptr)
{
// as Render_cab() places it
current.cab = glm::translate(glm::dmat4{1.0}, Cab->GetPosition() - Camera) * Cab->mMatrix;
}
current.size = Size;
current.valid = true;
auto const projection{offset * current.projection};
m_historyvalid = m_previous.valid && (m_previous.size == Size) && (m_previous.vehicle == Cab) && (glm::length(Camera - m_previous.camera) < CAMERA_JUMP);
if (m_historyvalid)
{
auto const inverseprojection{glm::inverse(projection)};
// the view of this frame to the world around the camera, the world around the camera of the last frame, its view
auto const toworld{glm::dmat4{glm::inverse(current.view)}};
auto const world{glm::dmat4{m_previous.view} * glm::translate(glm::dmat4{1.0}, Camera - m_previous.camera) * toworld};
// the cab goes with its vehicle: the view of this frame to the cab, to the world around the camera of the last frame
auto const cab{glm::dmat4{m_previous.view} * m_previous.cab * glm::inverse(current.cab) * toworld};
// the shaders project the future with the projection of this frame (the velocity is the place in the future less the place
// now, both with the offset of this frame): the projection of the last frame with the offset of this one is put in front of
// it in the future. with the projection the same in both frames this is the identity, and the velocity holds just the motion
auto const previous{offset * m_previous.projection};
auto const projectionfix{inverseprojection * previous};
m_worldfuture = glm::mat4{projectionfix * world};
m_cabfuture = glm::mat4{projectionfix * cab};
m_reprojection = glm::mat4{previous * world * inverseprojection};
}
else
{
m_worldfuture = glm::mat4{1.f};
m_cabfuture = glm::mat4{1.f};
m_reprojection = glm::mat4{1.f};
}
m_target = 1 - m_target;
m_previous = current;
return glm::mat4{projection};
}

View File

@@ -0,0 +1,94 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
// the temporal anti-aliasing of the main view (gfx.antialiasing taa), the part shared by the renderers: the sub-pixel offset of the
// projection of every frame, and the matrices which take the pixels of a frame to the place they had in the last one.
//
// with it the velocities of the colour pass point to the last frame: the future of the draws (model_ubs.future) is the matrix of the
// view of the last frame instead of the next one, for the scenery and for the cab apart (the cab moves with the camera), and the
// moving vehicles and wheels take their motion backwards (scene_walk::m_motionhistory). the resolve pass (postfx_taa.frag,
// taa_resolve.glsl) reads the history with the velocities, and takes the pixels of the world standing still (and the sky, which has
// no velocity drawn) to the last frame with reprojection(); on those the thin things (rails, wires) are kept in the history for a few
// frames although the pixel of this frame misses them
#pragma once
#include <cstdint>
#include <glm/glm.hpp>
class TDynamicObject;
namespace gfx
{
class temporal_aa
{
public:
// the frame of the colour pass of the main view: Projection without the offset, View the rotation of the camera (the axes of the
// world to those of the view), Camera its place, Cab the vehicle whose cab is drawn (or null), Size of the images of the pass.
// returns the projection with the offset of the frame
glm::mat4 begin_frame(glm::mat4 const &Projection, glm::dmat3 const &View, glm::dvec3 const &Camera, TDynamicObject const *Cab, glm::ivec2 const &Size);
// the next frame starts the history anew
void reset()
{
m_previous.valid = false;
}
// the frame has a history: the last frame was drawn the same way
bool history_valid() const
{
return m_historyvalid;
}
// model_ubs.future of the scenery, and of the cab with the interiors of its vehicle
glm::mat4 const &world_future() const
{
return m_worldfuture;
}
glm::mat4 const &cab_future() const
{
return m_cabfuture;
}
// the clip space of the pixels of this frame (with their depth) to the clip space of the last frame, for the world standing still
glm::mat4 const &reprojection() const
{
return m_reprojection;
}
// the image of the history written this frame (0 or 1); the other one holds the last frame
int target() const
{
return m_target;
}
// the offset of the frame, in pixels
glm::vec2 jitter() const
{
return m_jitter;
}
private:
struct frame
{
glm::dmat4 projection{1.0}; // without the offset
glm::dmat3 view{1.0};
glm::dvec3 camera{0.0};
glm::dmat4 cab{1.0}; // the cab to the world around the camera
TDynamicObject const *vehicle{nullptr};
glm::ivec2 size{0};
bool valid{false};
};
frame m_previous;
std::uint32_t m_phase{0};
glm::vec2 m_jitter{0.f};
glm::mat4 m_worldfuture{1.f};
glm::mat4 m_cabfuture{1.f};
glm::mat4 m_reprojection{1.f};
int m_target{0};
bool m_historyvalid{false};
};
} // namespace gfx

View File

@@ -234,6 +234,8 @@ bool opengl46_renderer::Init(GLFWwindow *Window)
glEnable(GL_MULTISAMPLE);
m_pfx_motionblur = std::make_unique<gl::postfx>("motionblur");
if (Global.gfx_taa())
m_pfx_taa = std::make_unique<gl::postfx>("taa");
m_pfx_tonemapping = std::make_unique<gl::postfx>("tonemapping");
m_pfx_chromaticaberration = std::make_unique<gl::postfx>( "chromaticaberration" );
@@ -519,7 +521,7 @@ bool opengl46_renderer::init_viewport(viewport_config &vp)
vp.msaa_fb->attach(*vp.msaa_rbc, GL_COLOR_ATTACHMENT0);
vp.msaa_fb->attach(*vp.msaa_rbd, GL_DEPTH_ATTACHMENT);
if (Global.gfx_postfx_chromaticaberration_enabled || Global.gfx_postfx_motionblur_enabled || Global.gfx_postfx_ao != 0)
if (Global.gfx_postfx_chromaticaberration_enabled || Global.gfx_motion_vectors() || Global.gfx_postfx_ao != 0)
{
vp.main_tex = std::make_unique<opengl_texture>();
vp.main_tex->alloc_rendertarget(Global.gfx_format_color, GL_RGB, vp.width, vp.height, 1, 1, GL_CLAMP_TO_EDGE);
@@ -531,7 +533,7 @@ bool opengl46_renderer::init_viewport(viewport_config &vp)
vp.main_fb->attach(*vp.main_tex, GL_COLOR_ATTACHMENT0);
vp.main_fb->attach(*vp.main_texd, GL_DEPTH_ATTACHMENT);
if (Global.gfx_postfx_motionblur_enabled)
if (Global.gfx_motion_vectors())
{
vp.main_texv = std::make_unique<opengl_texture>();
vp.main_texv->alloc_rendertarget(Global.gfx_postfx_motionblur_format, GL_RG, vp.width, vp.height);
@@ -544,13 +546,14 @@ bool opengl46_renderer::init_viewport(viewport_config &vp)
}
}
if (Global.gfx_postfx_motionblur_enabled)
if (Global.gfx_motion_vectors())
{
vp.msaa_rbv = std::make_unique<gl::renderbuffer>();
vp.msaa_rbv->alloc(Global.gfx_postfx_motionblur_format, vp.width, vp.height, samples);
vp.msaa_fb->attach(*vp.msaa_rbv, GL_COLOR_ATTACHMENT1);
WriteLog("motion blur enabled");
if (Global.gfx_postfx_motionblur_enabled)
WriteLog("motion blur enabled");
}
if( !vp.msaa_fb->is_complete() ) {
@@ -581,7 +584,7 @@ bool opengl46_renderer::init_viewport(viewport_config &vp)
vp.ssao_blur_fb->attach(*vp.ssao_blur_tex, GL_COLOR_ATTACHMENT0);
if (!vp.ssao_blur_fb->is_complete()) { ErrorLog("ssao blur framebuffer setup failed"); return false; }
if (Global.gfx_postfx_motionblur_enabled)
if (Global.gfx_motion_vectors())
{
vp.ao_apply_fb = std::make_unique<gl::framebuffer>();
vp.ao_apply_fb->attach(*vp.main_tex, GL_COLOR_ATTACHMENT0);
@@ -596,6 +599,24 @@ bool opengl46_renderer::init_viewport(viewport_config &vp)
vp.bloom = opengl46_exposure::make_bloom_chain(vp.width, vp.height);
if (!vp.bloom)
ErrorLog("bloom framebuffer setup failed, no bloom");
// the two images of the history of the temporal anti-aliasing, of the main view (not the eyes of the vr)
if (Global.gfx_taa() && vp.main && (vp.proj_type != viewport_config::vr_left) && (vp.proj_type != viewport_config::vr_right))
{
for (std::size_t i = 0; i < vp.taa_tex.size(); ++i)
{
vp.taa_tex[i] = std::make_unique<opengl_texture>();
vp.taa_tex[i]->alloc_rendertarget(GL_RGBA16F, GL_RGBA, vp.width, vp.height, 1, 1, GL_CLAMP_TO_EDGE);
vp.taa_fb[i] = std::make_unique<gl::framebuffer>();
vp.taa_fb[i]->attach(*vp.taa_tex[i], GL_COLOR_ATTACHMENT0);
if (!vp.taa_fb[i]->is_complete())
{
ErrorLog("temporal anti-aliasing framebuffer setup failed");
return false;
}
}
WriteLog("temporal anti-aliasing enabled");
}
}
if (vp.custom_backbuffer)
@@ -1541,6 +1562,18 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
break;
}
// the temporal anti-aliasing: the projection of the frame moved by a part of a pixel, the velocities pointed to the last frame
auto const taa{Taa_View(vp)};
m_motionhistory = taa;
m_cabfuture = glm::mat4{1.f};
if (taa)
{
auto const *cab{((false == FreeFlyModeFlag) && Global.render_cab && (simulation::Train != nullptr)) ? simulation::Train->Dynamic() : nullptr};
m_renderpass.pass_camera.projection() = m_taa.begin_frame(
m_renderpass.pass_camera.projection(), glm::dmat3{m_renderpass.pass_camera.modelview()}, m_renderpass.pass_camera.position(), cab, glm::ivec2{vp.width, vp.height});
m_cabfuture = m_taa.cab_future();
}
m_colorpass = m_renderpass; // cache pass data
// parts of the gpu time of the frame; the marks go through the main view only, in the context they were begun in
auto const gpupart = [this, &vp](char const *Name) {
@@ -1665,7 +1698,7 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
{
vp.msaa_fb->bind();
if (motionblur)
if (motionblur || taa)
vp.msaa_fb->setup_drawing(2);
else
vp.msaa_fb->setup_drawing(1);
@@ -1709,7 +1742,11 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
model_ubs.future = glm::mat4();
glm::mat4 future;
if (Global.pCamera.m_owner != nullptr)
if (taa)
{
future = m_taa.world_future();
}
else if (Global.pCamera.m_owner != nullptr)
{
auto const *vehicle = Global.pCamera.m_owner;
glm::mat4 mv = OpenGLMatrices.data(GL_MODELVIEW);
@@ -1792,7 +1829,7 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
if (!FreeFlyModeFlag && Global.Overcast <= 1.0f && Global.render_cab)
{
glDebug("render opaque cab");
model_ubs.future = glm::mat4();
model_ubs.future = m_cabfuture;
auto const *vehicle{ simulation::Train->Dynamic() };
// the lamp lighting the shadows of the sun, unless it is a light of its own
if( ( vehicle->InteriorLightLevel > 0.f ) && ( false == m_cablamplit ) ) {
@@ -1843,7 +1880,7 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
// cab render
if (false == FreeFlyModeFlag && Global.render_cab)
{
model_ubs.future = glm::mat4();
model_ubs.future = m_cabfuture;
auto *vehicle { simulation::Train->Dynamic() };
if( ( vehicle->InteriorLightLevel > 0.f ) && ( false == m_cablamplit ) ) {
setup_shadow_color( glm::min( colors::white, m_shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) );
@@ -1865,6 +1902,10 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
if( Global.Overcast > 1.0f ) {
// with the cab in place we can (finally) safely draw translucent part of the occupied vehicle
if( taa ) {
// the vehicle adds its own motion to the view of the world (the cab future has it already)
model_ubs.future = m_taa.world_future();
}
Render_Alpha( vehicle );
}
}
@@ -1893,7 +1934,7 @@ if (!Global.gfx_skippipeline)
gpupart("resolve, ambient occlusion, motion blur");
// the targets of the ambient occlusion are there only when it was on at the start
auto const aomode{(vp.ssao_tex != nullptr) ? Global.gfx_postfx_ao : 0};
if (motionblur)
if (motionblur || taa)
{
vp.main_fb->clear(GL_COLOR_BUFFER_BIT);
vp.main_fb->setup_drawing(2);
@@ -1905,9 +1946,26 @@ if (!Global.gfx_skippipeline)
if (aomode != 0)
Render_ambient_occlusion(vp, *vp.ao_apply_fb, aomode);
model_ubs.param[0].x = m_framerate / (1.0 / Global.gfx_postfx_motionblur_shutter);
model_ubo->update(model_ubs);
m_pfx_motionblur->apply({vp.main_tex.get(), vp.main_texv.get(), vp.main_texd.get()}, vp.main2_fb.get());
// the image the motion blur takes: the scene, or the scene blended into its history
auto *image{vp.main_tex.get()};
if (taa)
{
gpupart("temporal anti-aliasing");
Render_taa(vp);
image = vp.taa_tex[m_taa.target()].get();
}
if (motionblur)
{
if (taa)
gpupart("motion blur");
model_ubs.param[0].x = m_framerate / (1.0 / Global.gfx_postfx_motionblur_shutter);
model_ubo->update(model_ubs);
m_pfx_motionblur->apply({image, vp.main_texv.get(), vp.main_texd.get()}, vp.main2_fb.get());
}
else
{
vp.taa_fb[m_taa.target()]->blit_to(vp.main2_fb.get(), vp.width, vp.height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0);
}
}
else
{
@@ -1925,7 +1983,7 @@ if (!Global.gfx_skippipeline)
// (gfx.shadow.sdsm). the main framebuffer holds it resolved for the motion blur or the ambient occlusion
if (vp.main && Global.gfx_shadow_sdsm && (Shadow_Fit_Unavailable(vp) == nullptr))
{
auto const resolved{(vp.main_texd != nullptr) && (motionblur || (aomode != 0))};
auto const resolved{(vp.main_texd != nullptr) && (motionblur || taa || (aomode != 0))};
opengl46_shadow_fit::view const view{
m_colorpass.pass_camera.position(),
m_colorpass.pass_camera.projection(),
@@ -3961,7 +4019,7 @@ void opengl46_renderer::Render_Scene_Depth( glm::mat4 const &Future, bool const
}
if( ( false == FreeFlyModeFlag ) && ( Global.Overcast <= 1.0f ) && ( Global.render_cab ) ) {
model_ubs.future = glm::mat4();
model_ubs.future = m_cabfuture;
auto const *vehicle { simulation::Train->Dynamic() };
if( ( vehicle->InteriorLightLevel > 0.f ) && ( false == m_cablamplit ) ) {
setup_shadow_color( glm::min( colors::white, m_shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) );
@@ -3980,7 +4038,7 @@ void opengl46_renderer::Render_Scene_Depth( glm::mat4 const &Future, bool const
else {
// the conditions of the cab drawn early by the colour pass
if( ( false == FreeFlyModeFlag ) && ( Global.Overcast <= 1.0f ) && ( Global.render_cab ) ) {
model_ubs.future = glm::mat4();
model_ubs.future = m_cabfuture;
auto const *vehicle { simulation::Train->Dynamic() };
Render_cab( vehicle, vehicle->InteriorLightLevel, false );
}
@@ -6379,6 +6437,28 @@ void opengl46_renderer::Cull_Lights(viewport_config &vp)
m_cullstimer.end();
}
// the temporal anti-aliasing of the main view: the scene of the frame (main_tex, with its velocities and depth) blended into the
// history of the last frames (postfx_taa.frag), to the image of the history of this frame
void opengl46_renderer::Render_taa(viewport_config &vp)
{
auto const target{m_taa.target()};
// the near plane gives the distance of the pixels from their depth
model_ubs.param[0] = glm::vec4{m_taa.history_valid() ? 1.f : 0.f, m_colorpass.znear, 0.f, 0.f};
model_ubs.future = m_taa.reprojection();
model_ubo->update(model_ubs);
glViewport(0, 0, vp.width, vp.height);
// the alpha of the history is the lock of the thin things, written as it is (the blending is on after the translucent parts)
glDisable(GL_BLEND);
m_pfx_taa->apply({vp.main_tex.get(), vp.main_texv.get(), vp.main_texd.get(), vp.taa_tex[1 - target].get()}, vp.taa_fb[target].get());
if (m_blendingenabled)
glEnable(GL_BLEND);
}
bool opengl46_renderer::Taa_View(viewport_config const &vp) const
{
return Global.gfx_taa() && vp.main && (vp.taa_tex[0] != nullptr) && (m_pfx_taa != nullptr);
}
void opengl46_renderer::Render_ambient_occlusion(viewport_config &vp, gl::framebuffer &Target, int const Mode)
{
glDebug("ambient occlusion");

View File

@@ -46,6 +46,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/opengl46/opengl46hiz.h"
#include "rendering/opengl46/opengl46shadowfit.h"
#include "rendering/common/scenewalk.h"
#include "rendering/common/temporalaa.h"
// bare-bones render controller, in lack of anything better yet
class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
@@ -267,6 +268,10 @@ class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
// main_tex alone, for the ambient occlusion to be applied to it before the motion blur (main_fb holds the depth it reads)
std::unique_ptr<gl::framebuffer> ao_apply_fb;
// the temporal anti-aliasing (main viewport only): the two images of the history, written in turn
std::array<std::unique_ptr<opengl_texture>, 2> taa_tex;
std::array<std::unique_ptr<gl::framebuffer>, 2> taa_fb;
// LDR backbuffer for offscreen rendering
std::unique_ptr<gl::framebuffer> backbuffer_fb;
std::unique_ptr<opengl_texture> backbuffer_tex;
@@ -451,6 +456,9 @@ class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
void Render_cluster_heatmap(viewport_config &vp, gl::framebuffer *Target, glm::ivec2 const &Targetsize);
// ambient occlusion from the resolved depth (vp.main_texd), multiplied into the image of Target. Mode: Global.gfx_postfx_ao
void Render_ambient_occlusion(viewport_config &vp, gl::framebuffer &Target, int const Mode);
// the temporal anti-aliasing of the main view (gfx.antialiasing taa), and whether the viewport has it
void Render_taa(viewport_config &vp);
bool Taa_View(viewport_config const &vp) const;
bool init_viewport(viewport_config &vp);
@@ -786,6 +794,8 @@ class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
std::vector<std::unique_ptr<viewport_config>> m_viewports;
std::unique_ptr<gl::postfx> m_pfx_motionblur;
std::unique_ptr<gl::postfx> m_pfx_taa;
gfx::temporal_aa m_taa;
std::unique_ptr<gl::postfx> m_pfx_tonemapping;
std::unique_ptr<gl::postfx> m_pfx_chromaticaberration;

View File

@@ -107,7 +107,8 @@ struct viewport_targets
{
VkExtent2D extent{0, 0};
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
bool motion{false}; // the velocity is drawn, for the motion blur
bool motion{false}; // the velocity is drawn, for the motion blur or the temporal anti-aliasing
bool taa{false};
bool aberration{false};
// with more than one sample: the images drawn to, resolved to the ones below
std::shared_ptr<image> msaa_colour;
@@ -119,6 +120,9 @@ struct viewport_targets
std::shared_ptr<image> depth;
// the colour after the motion blur (hdr)
std::shared_ptr<image> blurred;
// the temporal anti-aliasing (gfx.antialiasing taa, the main viewport): the two images of the history, written in turn (hdr, the
// lock of the thin things in alpha)
std::array<std::shared_ptr<image>, 2> taa_history;
// the tonemapped image, before the chromatic aberration, and the one which goes to the window
std::shared_ptr<image> tonemapped;
std::shared_ptr<image> output;

View File

@@ -308,7 +308,7 @@ namespace
{
bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std::uint64_t const Value, std::uint32_t &Formatscount, VkSampleCountFlags const Samplecounts, VkFormat const Bloomformat, vk14::viewport_targets &Targets,
int const Width, int const Height, bool const Srgb)
int const Width, int const Height, bool const Srgb, bool const Taa)
{
// as many samples as asked for, as far as the device has them for the colour and the depth
auto samples{static_cast<VkSampleCountFlagBits>(std::clamp(1 << Global.iMultisampling, 1, 8))};
@@ -316,11 +316,11 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
{
samples = static_cast<VkSampleCountFlagBits>(samples >> 1);
}
auto const motion{Global.gfx_postfx_motionblur_enabled};
auto const motion{Global.gfx_motion_vectors()};
auto const aberration{Global.gfx_postfx_chromaticaberration_enabled};
VkExtent2D const extent{static_cast<std::uint32_t>(std::max(Width, 1)), static_cast<std::uint32_t>(std::max(Height, 1))};
if ((Targets.extent.width == extent.width) && (Targets.extent.height == extent.height) && (Targets.samples == samples) && (Targets.motion == motion) && (Targets.aberration == aberration) && (Targets.srgb == Srgb) &&
Targets.output)
if ((Targets.extent.width == extent.width) && (Targets.extent.height == extent.height) && (Targets.samples == samples) && (Targets.motion == motion) && (Targets.taa == Taa) && (Targets.aberration == aberration) &&
(Targets.srgb == Srgb) && Targets.output)
{
return true;
}
@@ -333,6 +333,7 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
Targets.extent = extent;
Targets.samples = samples;
Targets.motion = motion;
Targets.taa = Taa;
Targets.aberration = aberration;
Targets.srgb = Srgb;
@@ -367,7 +368,15 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
make(Targets.depth, depth, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_SAMPLE_COUNT_1_BIT, "scene depth");
if (motion)
{
ok = ok && make(Targets.motion_image, velocity, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "scene velocity") && make(Targets.blurred, hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "motion blurred");
ok = ok && make(Targets.motion_image, velocity, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "scene velocity");
}
if (motion && Global.gfx_postfx_motionblur_enabled)
{
ok = ok && make(Targets.blurred, hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "motion blurred");
}
if (Taa)
{
ok = ok && make(Targets.taa_history[0], hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "taa history") && make(Targets.taa_history[1], hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "taa history");
}
auto const output{Srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM};
if (aberration)
@@ -873,11 +882,17 @@ void vulkan14_renderer::render_scene()
{
auto const &viewport{m_viewports[i]};
auto const srgb{false == Global.gfx_shadergamma};
auto const *history{scene.targets[i].taa_history[0].get()};
if (false == make_targets(ctx.memory, ctx.deletions, ctx.uploads.current_value(), scene.formats_count, ctx.device.caps().limits.framebufferColorSampleCounts & ctx.device.caps().limits.framebufferDepthSampleCounts, scene.bloom_format,
scene.targets[i], viewport.width, viewport.height, srgb))
scene.targets[i], viewport.width, viewport.height, srgb, viewport.main && Global.gfx_taa()))
{
continue;
}
if (viewport.main && (scene.targets[i].taa_history[0].get() != history))
{
// new images of the history, nothing in them yet
m_taa.reset();
}
render_viewport(i);
}
m_current_viewport = (m_viewports.empty() ? nullptr : &m_viewports.front());
@@ -1206,6 +1221,17 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
m_current_viewport = &vp;
setup_pass(vp, m_renderpass, rendermode::color);
// the temporal anti-aliasing: the projection of the frame moved by a part of a pixel, the velocities pointed to the last frame
auto const taa{vp.main && targets.taa && Global.gfx_taa()};
m_motionhistory = taa;
m_cabfuture = glm::mat4{1.f};
if (taa)
{
auto const *cab{((false == FreeFlyModeFlag) && Global.render_cab && (simulation::Train != nullptr)) ? simulation::Train->Dynamic() : nullptr};
m_renderpass.pass_camera.projection() = m_taa.begin_frame(
m_renderpass.pass_camera.projection(), glm::dmat3{m_renderpass.pass_camera.modelview()}, m_renderpass.pass_camera.position(), cab, glm::ivec2{vp.width, vp.height});
m_cabfuture = m_taa.cab_future();
}
m_colorpass = m_renderpass;
scene_ubs.time = Timer::GetTime();
@@ -1294,13 +1320,18 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
}
// the motion blur only when something in the view moves (see opengl46_renderer::Render_pass())
auto const motionblur{targets.motion && (m_vehiclesmoved || ((Global.pCamera.m_owner != nullptr) && (Global.pCamera.m_owner->get_future_movement() != glm::dvec3{0.0})))};
auto const motionblur{Global.gfx_postfx_motionblur_enabled && targets.motion &&
(m_vehiclesmoved || ((Global.pCamera.m_owner != nullptr) && (Global.pCamera.m_owner->get_future_movement() != glm::dvec3{0.0})))};
setup_matrices();
m_renderpass.draw_stats = {};
model_ubs.future = glm::mat4();
glm::mat4 future;
if (Global.pCamera.m_owner != nullptr)
if (taa)
{
future = m_taa.world_future();
}
else if (Global.pCamera.m_owner != nullptr)
{
auto const *vehicle = Global.pCamera.m_owner;
glm::mat4 mv = OpenGLMatrices.data(GL_MODELVIEW);
@@ -1377,7 +1408,7 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
// without rain/snow we can render the cab early to limit the overdraw
if (!FreeFlyModeFlag && Global.Overcast <= 1.0f && Global.render_cab && simulation::Train)
{
model_ubs.future = glm::mat4();
model_ubs.future = m_cabfuture;
auto const *vehicle{simulation::Train->Dynamic()};
// the lamp lighting the shadows of the sun, unless it is a light of its own
if ((vehicle->InteriorLightLevel > 0.f) && (false == m_cablamplit))
@@ -1413,7 +1444,7 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
// cab render
if ((false == FreeFlyModeFlag) && Global.render_cab && simulation::Train)
{
model_ubs.future = glm::mat4();
model_ubs.future = m_cabfuture;
auto *vehicle{simulation::Train->Dynamic()};
if ((vehicle->InteriorLightLevel > 0.f) && (false == m_cablamplit))
{
@@ -1433,6 +1464,11 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
if (Global.Overcast > 1.0f)
{
// with the cab in place we can (finally) safely draw translucent part of the occupied vehicle
if (taa)
{
// the vehicle adds its own motion to the view of the world (the cab future has it already)
model_ubs.future = m_taa.world_future();
}
Render_Alpha(vehicle);
}
}
@@ -1529,7 +1565,7 @@ void vulkan14_renderer::render_scene_depth(std::size_t const Index, glm::mat4 co
// the conditions of the cab drawn early by the colour pass
if ((false == FreeFlyModeFlag) && (Global.Overcast <= 1.0f) && Global.render_cab && simulation::Train)
{
model_ubs.future = glm::mat4();
model_ubs.future = m_cabfuture;
auto const *vehicle{simulation::Train->Dynamic()};
Render_cab(vehicle, vehicle->InteriorLightLevel, false);
}
@@ -2669,11 +2705,26 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
render_ao(Index, aomode);
}
auto *hdr{targets.colour.get()};
// the temporal anti-aliasing: the scene blended into its history (the motion blur takes the result)
if (m_motionhistory && targets.taa && targets.taa_history[0] && targets.taa_history[1] && targets.motion_image)
{
auto const target{m_taa.target()};
vk14::draw_constants constants;
// the near plane gives the distance of the pixels from their depth
constants.data[0] = glm::vec4{m_taa.history_valid() ? 1.f : 0.f, m_colorpass.znear, 0.f, 0.f};
auto const &reprojection{m_taa.reprojection()};
for (int column = 0; column < 4; ++column)
{
constants.data[2 + column] = reprojection[column];
}
post_pass("postfx_taa.frag", *targets.taa_history[target], {targets.colour.get(), targets.motion_image.get(), targets.depth.get(), targets.taa_history[1 - target].get()}, constants, "temporal anti-aliasing");
hdr = targets.taa_history[target].get();
}
if (Motionblur && targets.blurred)
{
vk14::draw_constants constants;
constants.data[0].x = m_framerate / (1.0 / Global.gfx_postfx_motionblur_shutter);
post_pass("postfx_motionblur.frag", *targets.blurred, {targets.colour.get(), targets.motion_image.get(), targets.depth.get()}, constants, "resolve, ambient occlusion, motion blur");
post_pass("postfx_motionblur.frag", *targets.blurred, {hdr, targets.motion_image.get(), targets.depth.get()}, constants, "resolve, ambient occlusion, motion blur");
hdr = targets.blurred.get();
}
ctx.passes.begin_pass("exposure, bloom");

View File

@@ -28,6 +28,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/common/weatherstate.h"
#include "rendering/common/shadowfit.h"
#include "rendering/common/gpuparticles.h"
#include "rendering/common/temporalaa.h"
#include "rendering/particles.h"
#include "model/material.h"
#include "vehicle/Camera.h"
@@ -399,6 +400,8 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
double m_environmentupdatetime{0.0};
glm::dvec3 m_environmentupdatelocation{0.0};
bool m_vehiclesmoved{true};
// the temporal anti-aliasing of the main viewport (gfx.antialiasing taa)
gfx::temporal_aa m_taa;
texture_handle m_suntexture{-1};
texture_handle m_moontexture{-1};
texture_handle m_smoketexture{-1};

View File

@@ -600,6 +600,10 @@ bool vulkan14_renderer::init_scene(std::string &Reason)
{
scene.post_fragments.emplace(name, ctx.shaders.get(name));
}
if (Global.gfx_taa())
{
scene.post_fragments.emplace("postfx_taa.frag", ctx.shaders.get("postfx_taa.frag"));
}
auto const build{ctx.shaders.get("cluster_build.comp")};
auto const cull{ctx.shaders.get("cluster_cull.comp")};
scene.cluster_build = ctx.pipelines.compute(build, scene.cluster_layout, {}, "cluster build");