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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
89
rendering/common/temporalaa.cpp
Normal file
89
rendering/common/temporalaa.cpp
Normal 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};
|
||||
}
|
||||
94
rendering/common/temporalaa.h
Normal file
94
rendering/common/temporalaa.h
Normal 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
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user