mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-26 02:12:27 +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:
@@ -321,6 +321,7 @@ set(SOURCES
|
||||
"rendering/common/skylight.cpp"
|
||||
"rendering/common/weatherstate.cpp"
|
||||
"rendering/common/exposure.cpp"
|
||||
"rendering/common/temporalaa.cpp"
|
||||
"gl/buffer.cpp"
|
||||
"gl/fence.cpp"
|
||||
"gl/pbo.cpp"
|
||||
|
||||
@@ -7,6 +7,7 @@ struct _global
|
||||
bool gfx_skippipeline;
|
||||
bool gfx_extraeffects;
|
||||
bool gfx_shadergamma;
|
||||
bool gfx_motion_vectors() const { return gfx_postfx_motionblur_enabled; }
|
||||
};
|
||||
|
||||
extern _global Global;
|
||||
|
||||
@@ -80,6 +80,20 @@ existed.
|
||||
The automatic exposure and the bloom need the post processing pipeline (`gfx.skippipeline no`, the default).
|
||||
Model previews of the editor use none of these.
|
||||
|
||||
### Anti-aliasing
|
||||
|
||||
| Entry | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `gfx.antialiasing off/msaax2/msaax4/msaax8/taa` | `msaax4` | `msaax2`..`msaax8`: multisampling of the colour pass. `taa`: temporal anti-aliasing of the main view: one sample per pixel, the projection moved by a part of a pixel every frame (8 offsets) and the frame blended into the history of the last frames along the velocities of the pixels. Also in the Vulkan renderer. Read at the start; the older `multisampling 0..4` still works, the entry later in the file wins |
|
||||
|
||||
TAA needs the post processing pipeline (`gfx.skippipeline no`); extra viewports and the eyes of the VR get no
|
||||
anti-aliasing with it. The velocities are drawn with it whether the motion blur is on or not; with both on the
|
||||
motion blur smears the image after the TAA, and it also blurs the turning of the camera. Thin lines across the
|
||||
pixels (rails and wires far away) are locked in the history for 8 frames on the world standing still farther
|
||||
than 20..40 m, so the frames whose offset misses them don't make them flicker; on near or fast moving pixels the
|
||||
usual check of the history against the colours around the pixel applies. Further anti-aliasing modes (FSR, DLSS) are meant to be added to
|
||||
the same entry.
|
||||
|
||||
### Ambient occlusion
|
||||
|
||||
Darkening of creases, corners and the ground under objects, worked out from the depth of the image (screen space)
|
||||
|
||||
@@ -100,7 +100,7 @@ std::string gl::glsl_settings_defines()
|
||||
return
|
||||
"#define SHADOWMAP_ENABLED_VALUE " + value(Global.gfx_shadowmap_enabled) + "\n"
|
||||
"#define ENVMAP_ENABLED_VALUE " + value(Global.gfx_envmap_enabled) + "\n"
|
||||
"#define MOTIONBLUR_ENABLED_VALUE " + value(Global.gfx_postfx_motionblur_enabled) + "\n"
|
||||
"#define MOTIONBLUR_ENABLED_VALUE " + value(Global.gfx_motion_vectors()) + "\n"
|
||||
"#define POSTFX_ENABLED_VALUE " + value(!Global.gfx_skippipeline) + "\n"
|
||||
"#define EXTRAEFFECTS_ENABLED_VALUE " + value(Global.gfx_extraeffects) + "\n"
|
||||
"#define OUTPUT_GAMMA_VALUE " + value(Global.gfx_shadergamma) + "\n";
|
||||
@@ -112,7 +112,7 @@ std::array<GLuint, 6> gl::glsl_settings_values()
|
||||
return {
|
||||
static_cast<GLuint>(Global.gfx_shadowmap_enabled),
|
||||
static_cast<GLuint>(Global.gfx_envmap_enabled),
|
||||
static_cast<GLuint>(Global.gfx_postfx_motionblur_enabled),
|
||||
static_cast<GLuint>(Global.gfx_motion_vectors()),
|
||||
static_cast<GLuint>(!Global.gfx_skippipeline),
|
||||
static_cast<GLuint>(Global.gfx_extraeffects),
|
||||
static_cast<GLuint>(Global.gfx_shadergamma) };
|
||||
|
||||
@@ -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");
|
||||
|
||||
25
shaders/ogl46/postfx_taa.frag
Normal file
25
shaders/ogl46/postfx_taa.frag
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// the temporal anti-aliasing (taa_resolve.glsl). param[0].x: 1 with a history of the last frame, param[0].y: the near plane;
|
||||
// future: the clip space of this frame to the one of the last frame, for the world standing still (gfx::temporal_aa::reprojection())
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#pragma texture(color_tex, 0, RGB)
|
||||
layout(binding = 0) uniform sampler2D color_tex;
|
||||
|
||||
#pragma texture(velocity_tex, 1, RG)
|
||||
layout(binding = 1) uniform sampler2D velocity_tex;
|
||||
|
||||
#pragma texture(depth_tex, 2, R)
|
||||
layout(binding = 2) uniform sampler2D depth_tex;
|
||||
|
||||
#pragma texture(history_tex, 3, RGBA)
|
||||
layout(binding = 3) uniform sampler2D history_tex;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#define TAA_HISTORY_VALID (param[0].x > 0.5)
|
||||
#define TAA_REPROJECTION future
|
||||
#define TAA_NEAR param[0].y
|
||||
#include "taa_resolve.glsl"
|
||||
184
shaders/ogl46/taa_resolve.glsl
Normal file
184
shaders/ogl46/taa_resolve.glsl
Normal file
@@ -0,0 +1,184 @@
|
||||
// the temporal anti-aliasing (gfx.antialiasing taa, gfx::temporal_aa): the frame, drawn with the projection moved by a part of a
|
||||
// pixel, blended into the history of the last frames. the same in ogl46/ and vk14/; the stage file gives the images and
|
||||
// TAA_HISTORY_VALID the history holds the last frame (false: the frame alone is taken)
|
||||
// TAA_REPROJECTION the clip space of this frame (with the depth) to the one of the last frame, for the world standing still
|
||||
// TAA_NEAR the near plane of the projection: a pixel is that much over its depth away (reverse z)
|
||||
// the history: the colour (linear, hdr) and in alpha the lock of the thin things.
|
||||
//
|
||||
// the thin things (rails and wires far away, thinner than a pixel) are hit by some of the offsets only; a pixel of a frame which
|
||||
// misses them has the colours of the ground or the sky all around, so the usual check of the history against the colours around
|
||||
// the pixel would wipe them out of the history and they would flicker. a pixel where a frame shows a line across (brighter or
|
||||
// darker than the neighbours on both sides, and not along) is locked: for a few frames its history is kept as it is, as long as the
|
||||
// pixel belongs to the world standing still (where the place in the last frame is sure), moves slowly over the screen and is far
|
||||
// enough not to be a part of the cab (the needles of the gauges, the screens) or of things passing close by
|
||||
|
||||
// the share of the frame in the result
|
||||
const float TAA_CURRENT_SHARE = 0.1;
|
||||
// the history kept within this many standard deviations of the colours around the pixel: wide for a pixel standing still on the
|
||||
// screen, narrow for one moving across it this many pixels a frame
|
||||
const float TAA_VARIANCE_WIDE = 1.5;
|
||||
const float TAA_VARIANCE_NARROW = 1.0;
|
||||
const float TAA_SPEED_NARROW = 8.0;
|
||||
// the velocity drawn agrees with the world standing still within this many pixels (the velocity is a half float)
|
||||
const float TAA_STILL_PIXELS = 0.25;
|
||||
// the lock of a thin thing lasts as many frames as the offsets go round (gfx::temporal_aa), and holds while the pixel moves slower
|
||||
// than this many pixels a frame
|
||||
const float TAA_LOCK_DECAY = 1.0 / 8.0;
|
||||
const float TAA_LOCK_SPEED_MIN = 2.0;
|
||||
const float TAA_LOCK_SPEED_MAX = 6.0;
|
||||
const float TAA_LOCK_DISTANCE_MIN = 20.0;
|
||||
const float TAA_LOCK_DISTANCE_FULL = 40.0;
|
||||
// a line across: brighter or darker than both neighbours by this share of the brightest of the three, and the neighbours along
|
||||
// it closer to the pixel than half of that
|
||||
const float TAA_THIN_CONTRAST = 0.1;
|
||||
const float TAA_THIN_MINIMUM = 0.01;
|
||||
|
||||
// the largest value of a half float: the colour drawn kept within it, without the nan and the infinity which would stay in the
|
||||
// history for good
|
||||
const float TAA_HALF_MAX = 65504.0;
|
||||
|
||||
vec3 taa_clean(vec3 c)
|
||||
{
|
||||
return (any(isnan(c)) || any(isinf(c))) ? vec3(0.0) : clamp(c, vec3(0.0), vec3(TAA_HALF_MAX));
|
||||
}
|
||||
|
||||
// the colour squeezed into 0..1 by its largest channel (a weight against the bright pixels), and back
|
||||
vec3 taa_compress(vec3 c)
|
||||
{
|
||||
return c / (1.0 + max(c.r, max(c.g, c.b)));
|
||||
}
|
||||
|
||||
vec3 taa_decompress(vec3 c)
|
||||
{
|
||||
return c / max(1.0 - max(c.r, max(c.g, c.b)), 1.0 / TAA_HALF_MAX);
|
||||
}
|
||||
|
||||
vec3 taa_ycocg(vec3 c)
|
||||
{
|
||||
return vec3(0.25 * c.r + 0.5 * c.g + 0.25 * c.b, 0.5 * c.r - 0.5 * c.b, -0.25 * c.r + 0.5 * c.g - 0.25 * c.b);
|
||||
}
|
||||
|
||||
vec3 taa_rgb(vec3 c)
|
||||
{
|
||||
return vec3(c.x + c.y - c.z, c.x + c.z, c.x - c.y - c.z);
|
||||
}
|
||||
|
||||
// the history at the place, sharp: a catmull-rom spline through the texels, in five bilinear lookups (the corners left out)
|
||||
vec3 taa_history_colour(vec2 uv, vec2 size)
|
||||
{
|
||||
vec2 position = uv * size;
|
||||
vec2 texel1 = floor(position - 0.5) + 0.5;
|
||||
vec2 f = position - texel1;
|
||||
vec2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f));
|
||||
vec2 w1 = 1.0 + f * f * (-2.5 + 1.5 * f);
|
||||
vec2 w2 = f * (0.5 + f * (2.0 - 1.5 * f));
|
||||
vec2 w3 = f * f * (-0.5 + 0.5 * f);
|
||||
vec2 w12 = w1 + w2;
|
||||
vec2 uv0 = (texel1 - 1.0) / size;
|
||||
vec2 uv3 = (texel1 + 2.0) / size;
|
||||
vec2 uv12 = (texel1 + w2 / w12) / size;
|
||||
vec3 result = textureLod(history_tex, vec2(uv12.x, uv0.y), 0.0).rgb * (w12.x * w0.y);
|
||||
result += textureLod(history_tex, vec2(uv0.x, uv12.y), 0.0).rgb * (w0.x * w12.y);
|
||||
result += textureLod(history_tex, uv12, 0.0).rgb * (w12.x * w12.y);
|
||||
result += textureLod(history_tex, vec2(uv3.x, uv12.y), 0.0).rgb * (w3.x * w12.y);
|
||||
result += textureLod(history_tex, vec2(uv12.x, uv3.y), 0.0).rgb * (w12.x * w3.y);
|
||||
float weight = w12.x * w0.y + w0.x * w12.y + w12.x * w12.y + w3.x * w12.y + w12.x * w3.y;
|
||||
return taa_clean(result / weight);
|
||||
}
|
||||
|
||||
// the history brought towards the middle of the box of the colours around the pixel until it's inside it
|
||||
vec3 taa_clip(vec3 history, vec3 centre, vec3 extent)
|
||||
{
|
||||
vec3 offset = history - centre;
|
||||
vec3 units = abs(offset / max(extent, vec3(1.0 / 65504.0)));
|
||||
float largest = max(units.x, max(units.y, units.z));
|
||||
return (largest > 1.0) ? centre + offset / largest : history;
|
||||
}
|
||||
|
||||
// the pixel against its neighbours on both sides (A, B) and along (C, D): a line across it
|
||||
bool taa_line(float centre, float a, float b, float c, float d)
|
||||
{
|
||||
float da = a - centre;
|
||||
float db = b - centre;
|
||||
float across = min(abs(da), abs(db));
|
||||
float contrast = max(TAA_THIN_MINIMUM, TAA_THIN_CONTRAST * max(centre, max(a, b)));
|
||||
return (da * db > 0.0) && (across > contrast) && (max(abs(c - centre), abs(d - centre)) < 0.5 * across);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 size = vec2(textureSize(color_tex, 0));
|
||||
ivec2 pixel = ivec2(f_coords * size);
|
||||
ivec2 last = ivec2(size) - 1;
|
||||
|
||||
// the colours around the pixel (squeezed, ycocg): their moments and brightness, and the nearest depth among them
|
||||
vec3 m1 = vec3(0.0);
|
||||
vec3 m2 = vec3(0.0);
|
||||
float luma[9];
|
||||
float nearest = -1.0;
|
||||
ivec2 nearestpixel = pixel;
|
||||
for (int y = -1; y <= 1; ++y)
|
||||
{
|
||||
for (int x = -1; x <= 1; ++x)
|
||||
{
|
||||
ivec2 p = clamp(pixel + ivec2(x, y), ivec2(0), last);
|
||||
vec3 c = taa_ycocg(taa_compress(taa_clean(texelFetch(color_tex, p, 0).rgb)));
|
||||
m1 += c;
|
||||
m2 += c * c;
|
||||
luma[(y + 1) * 3 + (x + 1)] = c.x;
|
||||
// reverse z: the nearer, the greater
|
||||
float d = texelFetch(depth_tex, p, 0).r;
|
||||
if (d > nearest)
|
||||
{
|
||||
nearest = d;
|
||||
nearestpixel = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
vec3 frame = taa_clean(texelFetch(color_tex, pixel, 0).rgb);
|
||||
if (false == TAA_HISTORY_VALID)
|
||||
{
|
||||
out_color = vec4(frame, 0.0);
|
||||
return;
|
||||
}
|
||||
vec3 current = taa_ycocg(taa_compress(frame));
|
||||
|
||||
// the place of the pixel in the last frame: by the velocity drawn (of the nearest pixel around, so the edges go with what's in
|
||||
// front), or for the world standing still by the depth. the sky has no velocity drawn and stands still
|
||||
float depth = texelFetch(depth_tex, pixel, 0).r;
|
||||
vec2 moved = f_coords + texelFetch(velocity_tex, nearestpixel, 0).xy;
|
||||
vec4 world = TAA_REPROJECTION * vec4(f_coords * 2.0 - 1.0, depth, 1.0);
|
||||
vec2 worlduv = (world.w > 0.0) ? world.xy / world.w * 0.5 + 0.5 : vec2(-1.0);
|
||||
float difference = length((moved - worlduv) * size);
|
||||
bool still = (world.w > 0.0) && ((depth <= 0.0) || (difference < max(TAA_STILL_PIXELS, 0.01 * length((moved - f_coords) * size))));
|
||||
vec2 uv = still ? worlduv : moved;
|
||||
if (any(lessThan(uv, vec2(0.0))) || any(greaterThan(uv, vec2(1.0))))
|
||||
{
|
||||
// out of the last frame: the frame alone
|
||||
out_color = vec4(frame, 0.0);
|
||||
return;
|
||||
}
|
||||
float speed = length((uv - f_coords) * size);
|
||||
|
||||
vec3 history = taa_ycocg(taa_compress(taa_history_colour(uv, size)));
|
||||
float lock = textureLod(history_tex, uv, 0.0).a;
|
||||
lock = isnan(lock) ? 0.0 : clamp(lock, 0.0, 1.0);
|
||||
|
||||
// a line across the pixel in this frame locks it; the lock wears off, and holds only where the place in the last frame is sure,
|
||||
// the pixel moves slowly and is far away
|
||||
bool line = taa_line(luma[4], luma[3], luma[5], luma[1], luma[7]) || taa_line(luma[4], luma[1], luma[7], luma[3], luma[5]) ||
|
||||
taa_line(luma[4], luma[0], luma[8], luma[2], luma[6]) || taa_line(luma[4], luma[2], luma[6], luma[0], luma[8]);
|
||||
lock = max(lock - TAA_LOCK_DECAY, line ? 1.0 : 0.0);
|
||||
float distance = (depth > 0.0) ? TAA_NEAR / depth : TAA_LOCK_DISTANCE_FULL;
|
||||
lock *= (still ? 1.0 : 0.0) * (1.0 - smoothstep(TAA_LOCK_SPEED_MIN, TAA_LOCK_SPEED_MAX, speed)) *
|
||||
smoothstep(TAA_LOCK_DISTANCE_MIN, TAA_LOCK_DISTANCE_FULL, distance);
|
||||
|
||||
// the history within the colours around the pixel, but where it's locked
|
||||
vec3 mean = m1 / 9.0;
|
||||
vec3 deviation = sqrt(max(m2 / 9.0 - mean * mean, vec3(0.0)));
|
||||
float gamma = mix(TAA_VARIANCE_WIDE, TAA_VARIANCE_NARROW, clamp(speed / TAA_SPEED_NARROW, 0.0, 1.0));
|
||||
history = mix(taa_clip(history, mean, gamma * deviation), history, lock);
|
||||
|
||||
vec3 result = mix(history, current, TAA_CURRENT_SHARE);
|
||||
out_color = vec4(taa_clean(taa_decompress(clamp(taa_rgb(result), vec3(0.0), vec3(1.0 - 1.0 / TAA_HALF_MAX)))), lock);
|
||||
}
|
||||
26
shaders/vk14/postfx_taa.frag
Normal file
26
shaders/vk14/postfx_taa.frag
Normal file
@@ -0,0 +1,26 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
// the temporal anti-aliasing (taa_resolve.glsl). param is pc.data of the push constants: param[0].x 1 with a history of the last
|
||||
// frame, param[0].y the near plane; param[2..5] the columns of the matrix of the clip space of this frame to the one of the last
|
||||
// frame, for the world standing still (gfx::temporal_aa::reprojection())
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#pragma texture(color_tex, 0, RGB)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D color_tex;
|
||||
|
||||
#pragma texture(velocity_tex, 1, RG)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 1) uniform sampler2D velocity_tex;
|
||||
|
||||
#pragma texture(depth_tex, 2, R)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 2) uniform sampler2D depth_tex;
|
||||
|
||||
#pragma texture(history_tex, 3, RGBA)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 3) uniform sampler2D history_tex;
|
||||
|
||||
#define TAA_HISTORY_VALID (param[0].x > 0.5)
|
||||
#define TAA_REPROJECTION mat4(param[2], param[3], param[4], param[5])
|
||||
#define TAA_NEAR param[0].y
|
||||
#include "taa_resolve.glsl"
|
||||
184
shaders/vk14/taa_resolve.glsl
Normal file
184
shaders/vk14/taa_resolve.glsl
Normal file
@@ -0,0 +1,184 @@
|
||||
// the temporal anti-aliasing (gfx.antialiasing taa, gfx::temporal_aa): the frame, drawn with the projection moved by a part of a
|
||||
// pixel, blended into the history of the last frames. the same in ogl46/ and vk14/; the stage file gives the images and
|
||||
// TAA_HISTORY_VALID the history holds the last frame (false: the frame alone is taken)
|
||||
// TAA_REPROJECTION the clip space of this frame (with the depth) to the one of the last frame, for the world standing still
|
||||
// TAA_NEAR the near plane of the projection: a pixel is that much over its depth away (reverse z)
|
||||
// the history: the colour (linear, hdr) and in alpha the lock of the thin things.
|
||||
//
|
||||
// the thin things (rails and wires far away, thinner than a pixel) are hit by some of the offsets only; a pixel of a frame which
|
||||
// misses them has the colours of the ground or the sky all around, so the usual check of the history against the colours around
|
||||
// the pixel would wipe them out of the history and they would flicker. a pixel where a frame shows a line across (brighter or
|
||||
// darker than the neighbours on both sides, and not along) is locked: for a few frames its history is kept as it is, as long as the
|
||||
// pixel belongs to the world standing still (where the place in the last frame is sure), moves slowly over the screen and is far
|
||||
// enough not to be a part of the cab (the needles of the gauges, the screens) or of things passing close by
|
||||
|
||||
// the share of the frame in the result
|
||||
const float TAA_CURRENT_SHARE = 0.1;
|
||||
// the history kept within this many standard deviations of the colours around the pixel: wide for a pixel standing still on the
|
||||
// screen, narrow for one moving across it this many pixels a frame
|
||||
const float TAA_VARIANCE_WIDE = 1.5;
|
||||
const float TAA_VARIANCE_NARROW = 1.0;
|
||||
const float TAA_SPEED_NARROW = 8.0;
|
||||
// the velocity drawn agrees with the world standing still within this many pixels (the velocity is a half float)
|
||||
const float TAA_STILL_PIXELS = 0.25;
|
||||
// the lock of a thin thing lasts as many frames as the offsets go round (gfx::temporal_aa), and holds while the pixel moves slower
|
||||
// than this many pixels a frame
|
||||
const float TAA_LOCK_DECAY = 1.0 / 8.0;
|
||||
const float TAA_LOCK_SPEED_MIN = 2.0;
|
||||
const float TAA_LOCK_SPEED_MAX = 6.0;
|
||||
const float TAA_LOCK_DISTANCE_MIN = 20.0;
|
||||
const float TAA_LOCK_DISTANCE_FULL = 40.0;
|
||||
// a line across: brighter or darker than both neighbours by this share of the brightest of the three, and the neighbours along
|
||||
// it closer to the pixel than half of that
|
||||
const float TAA_THIN_CONTRAST = 0.1;
|
||||
const float TAA_THIN_MINIMUM = 0.01;
|
||||
|
||||
// the largest value of a half float: the colour drawn kept within it, without the nan and the infinity which would stay in the
|
||||
// history for good
|
||||
const float TAA_HALF_MAX = 65504.0;
|
||||
|
||||
vec3 taa_clean(vec3 c)
|
||||
{
|
||||
return (any(isnan(c)) || any(isinf(c))) ? vec3(0.0) : clamp(c, vec3(0.0), vec3(TAA_HALF_MAX));
|
||||
}
|
||||
|
||||
// the colour squeezed into 0..1 by its largest channel (a weight against the bright pixels), and back
|
||||
vec3 taa_compress(vec3 c)
|
||||
{
|
||||
return c / (1.0 + max(c.r, max(c.g, c.b)));
|
||||
}
|
||||
|
||||
vec3 taa_decompress(vec3 c)
|
||||
{
|
||||
return c / max(1.0 - max(c.r, max(c.g, c.b)), 1.0 / TAA_HALF_MAX);
|
||||
}
|
||||
|
||||
vec3 taa_ycocg(vec3 c)
|
||||
{
|
||||
return vec3(0.25 * c.r + 0.5 * c.g + 0.25 * c.b, 0.5 * c.r - 0.5 * c.b, -0.25 * c.r + 0.5 * c.g - 0.25 * c.b);
|
||||
}
|
||||
|
||||
vec3 taa_rgb(vec3 c)
|
||||
{
|
||||
return vec3(c.x + c.y - c.z, c.x + c.z, c.x - c.y - c.z);
|
||||
}
|
||||
|
||||
// the history at the place, sharp: a catmull-rom spline through the texels, in five bilinear lookups (the corners left out)
|
||||
vec3 taa_history_colour(vec2 uv, vec2 size)
|
||||
{
|
||||
vec2 position = uv * size;
|
||||
vec2 texel1 = floor(position - 0.5) + 0.5;
|
||||
vec2 f = position - texel1;
|
||||
vec2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f));
|
||||
vec2 w1 = 1.0 + f * f * (-2.5 + 1.5 * f);
|
||||
vec2 w2 = f * (0.5 + f * (2.0 - 1.5 * f));
|
||||
vec2 w3 = f * f * (-0.5 + 0.5 * f);
|
||||
vec2 w12 = w1 + w2;
|
||||
vec2 uv0 = (texel1 - 1.0) / size;
|
||||
vec2 uv3 = (texel1 + 2.0) / size;
|
||||
vec2 uv12 = (texel1 + w2 / w12) / size;
|
||||
vec3 result = textureLod(history_tex, vec2(uv12.x, uv0.y), 0.0).rgb * (w12.x * w0.y);
|
||||
result += textureLod(history_tex, vec2(uv0.x, uv12.y), 0.0).rgb * (w0.x * w12.y);
|
||||
result += textureLod(history_tex, uv12, 0.0).rgb * (w12.x * w12.y);
|
||||
result += textureLod(history_tex, vec2(uv3.x, uv12.y), 0.0).rgb * (w3.x * w12.y);
|
||||
result += textureLod(history_tex, vec2(uv12.x, uv3.y), 0.0).rgb * (w12.x * w3.y);
|
||||
float weight = w12.x * w0.y + w0.x * w12.y + w12.x * w12.y + w3.x * w12.y + w12.x * w3.y;
|
||||
return taa_clean(result / weight);
|
||||
}
|
||||
|
||||
// the history brought towards the middle of the box of the colours around the pixel until it's inside it
|
||||
vec3 taa_clip(vec3 history, vec3 centre, vec3 extent)
|
||||
{
|
||||
vec3 offset = history - centre;
|
||||
vec3 units = abs(offset / max(extent, vec3(1.0 / 65504.0)));
|
||||
float largest = max(units.x, max(units.y, units.z));
|
||||
return (largest > 1.0) ? centre + offset / largest : history;
|
||||
}
|
||||
|
||||
// the pixel against its neighbours on both sides (A, B) and along (C, D): a line across it
|
||||
bool taa_line(float centre, float a, float b, float c, float d)
|
||||
{
|
||||
float da = a - centre;
|
||||
float db = b - centre;
|
||||
float across = min(abs(da), abs(db));
|
||||
float contrast = max(TAA_THIN_MINIMUM, TAA_THIN_CONTRAST * max(centre, max(a, b)));
|
||||
return (da * db > 0.0) && (across > contrast) && (max(abs(c - centre), abs(d - centre)) < 0.5 * across);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 size = vec2(textureSize(color_tex, 0));
|
||||
ivec2 pixel = ivec2(f_coords * size);
|
||||
ivec2 last = ivec2(size) - 1;
|
||||
|
||||
// the colours around the pixel (squeezed, ycocg): their moments and brightness, and the nearest depth among them
|
||||
vec3 m1 = vec3(0.0);
|
||||
vec3 m2 = vec3(0.0);
|
||||
float luma[9];
|
||||
float nearest = -1.0;
|
||||
ivec2 nearestpixel = pixel;
|
||||
for (int y = -1; y <= 1; ++y)
|
||||
{
|
||||
for (int x = -1; x <= 1; ++x)
|
||||
{
|
||||
ivec2 p = clamp(pixel + ivec2(x, y), ivec2(0), last);
|
||||
vec3 c = taa_ycocg(taa_compress(taa_clean(texelFetch(color_tex, p, 0).rgb)));
|
||||
m1 += c;
|
||||
m2 += c * c;
|
||||
luma[(y + 1) * 3 + (x + 1)] = c.x;
|
||||
// reverse z: the nearer, the greater
|
||||
float d = texelFetch(depth_tex, p, 0).r;
|
||||
if (d > nearest)
|
||||
{
|
||||
nearest = d;
|
||||
nearestpixel = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
vec3 frame = taa_clean(texelFetch(color_tex, pixel, 0).rgb);
|
||||
if (false == TAA_HISTORY_VALID)
|
||||
{
|
||||
out_color = vec4(frame, 0.0);
|
||||
return;
|
||||
}
|
||||
vec3 current = taa_ycocg(taa_compress(frame));
|
||||
|
||||
// the place of the pixel in the last frame: by the velocity drawn (of the nearest pixel around, so the edges go with what's in
|
||||
// front), or for the world standing still by the depth. the sky has no velocity drawn and stands still
|
||||
float depth = texelFetch(depth_tex, pixel, 0).r;
|
||||
vec2 moved = f_coords + texelFetch(velocity_tex, nearestpixel, 0).xy;
|
||||
vec4 world = TAA_REPROJECTION * vec4(f_coords * 2.0 - 1.0, depth, 1.0);
|
||||
vec2 worlduv = (world.w > 0.0) ? world.xy / world.w * 0.5 + 0.5 : vec2(-1.0);
|
||||
float difference = length((moved - worlduv) * size);
|
||||
bool still = (world.w > 0.0) && ((depth <= 0.0) || (difference < max(TAA_STILL_PIXELS, 0.01 * length((moved - f_coords) * size))));
|
||||
vec2 uv = still ? worlduv : moved;
|
||||
if (any(lessThan(uv, vec2(0.0))) || any(greaterThan(uv, vec2(1.0))))
|
||||
{
|
||||
// out of the last frame: the frame alone
|
||||
out_color = vec4(frame, 0.0);
|
||||
return;
|
||||
}
|
||||
float speed = length((uv - f_coords) * size);
|
||||
|
||||
vec3 history = taa_ycocg(taa_compress(taa_history_colour(uv, size)));
|
||||
float lock = textureLod(history_tex, uv, 0.0).a;
|
||||
lock = isnan(lock) ? 0.0 : clamp(lock, 0.0, 1.0);
|
||||
|
||||
// a line across the pixel in this frame locks it; the lock wears off, and holds only where the place in the last frame is sure,
|
||||
// the pixel moves slowly and is far away
|
||||
bool line = taa_line(luma[4], luma[3], luma[5], luma[1], luma[7]) || taa_line(luma[4], luma[1], luma[7], luma[3], luma[5]) ||
|
||||
taa_line(luma[4], luma[0], luma[8], luma[2], luma[6]) || taa_line(luma[4], luma[2], luma[6], luma[0], luma[8]);
|
||||
lock = max(lock - TAA_LOCK_DECAY, line ? 1.0 : 0.0);
|
||||
float distance = (depth > 0.0) ? TAA_NEAR / depth : TAA_LOCK_DISTANCE_FULL;
|
||||
lock *= (still ? 1.0 : 0.0) * (1.0 - smoothstep(TAA_LOCK_SPEED_MIN, TAA_LOCK_SPEED_MAX, speed)) *
|
||||
smoothstep(TAA_LOCK_DISTANCE_MIN, TAA_LOCK_DISTANCE_FULL, distance);
|
||||
|
||||
// the history within the colours around the pixel, but where it's locked
|
||||
vec3 mean = m1 / 9.0;
|
||||
vec3 deviation = sqrt(max(m2 / 9.0 - mean * mean, vec3(0.0)));
|
||||
float gamma = mix(TAA_VARIANCE_WIDE, TAA_VARIANCE_NARROW, clamp(speed / TAA_SPEED_NARROW, 0.0, 1.0));
|
||||
history = mix(taa_clip(history, mean, gamma * deviation), history, lock);
|
||||
|
||||
vec3 result = mix(history, current, TAA_CURRENT_SHARE);
|
||||
out_color = vec4(taa_clean(taa_decompress(clamp(taa_rgb(result), vec3(0.0), vec3(1.0 - 1.0 / TAA_HALF_MAX)))), lock);
|
||||
}
|
||||
@@ -368,6 +368,7 @@ bool global_settings::ConfigParseGraphics(cParser& Parser, const std::string& to
|
||||
{
|
||||
ParseOne(Parser, iMultisampling, 1, false);
|
||||
iMultisampling = std::clamp(iMultisampling, 0, 4);
|
||||
gfx_antialiasing = (iMultisampling > 0 ? antialiasing_mode::msaa : antialiasing_mode::off);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1898,6 +1899,23 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To
|
||||
std::string level;
|
||||
Parser >> level;
|
||||
gfx_fog_quality = ( level == "low" || level == "1" ) ? 1 : ( level == "high" || level == "3" ) ? 3 : ( level == "ultra" || level == "4" ) ? 4 : 2; }
|
||||
else if( Token == "gfx.antialiasing" ) {
|
||||
// off, msaax2, msaax4, msaax8 or taa; it sets the multisampling too, the key which used to say it
|
||||
Parser.getTokens( 1 );
|
||||
std::string mode;
|
||||
Parser >> mode;
|
||||
std::transform( mode.begin(), mode.end(), mode.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
|
||||
if( mode == "taa" ) {
|
||||
gfx_antialiasing = antialiasing_mode::taa;
|
||||
iMultisampling = 0; }
|
||||
else if( mode == "msaax2" || mode == "msaax4" || mode == "msaax8" || mode == "msaax16" ) {
|
||||
gfx_antialiasing = antialiasing_mode::msaa;
|
||||
iMultisampling = ( mode == "msaax2" ) ? 1 : ( mode == "msaax4" ) ? 2 : ( mode == "msaax8" ) ? 3 : 4; }
|
||||
else if( mode == "off" || mode == "no" || mode == "none" || mode == "0" ) {
|
||||
gfx_antialiasing = antialiasing_mode::off;
|
||||
iMultisampling = 0; }
|
||||
else {
|
||||
ErrorLog( "Bad config: unknown gfx.antialiasing \"" + mode + "\", expected off, msaax2, msaax4, msaax8 or taa" ); } }
|
||||
else if( Token == "gfx.fog.range" ) { readvalue( gfx_fog_range, 100.f, 10000.f ); }
|
||||
else if( Token == "gfx.fog.lights" ) { readvalue( gfx_fog_lights, 0.f, 10.f ); }
|
||||
else if( Token == "gfx.fog.shadows" ) { readswitch( gfx_fog_shadows ); }
|
||||
@@ -2033,6 +2051,7 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
export_as_text( Output, "gfx.reflections.fidelity", reflectiontune.fidelity );
|
||||
export_as_text( Output, "timespeed", fTimeSpeed );
|
||||
export_as_text( Output, "multisampling", iMultisampling );
|
||||
export_as_text( Output, "gfx.antialiasing", std::string{ gfx_antialiasing == antialiasing_mode::taa ? "taa" : ( gfx_antialiasing == antialiasing_mode::off || iMultisampling == 0 ) ? "off" : "msaax" + std::to_string( 1 << iMultisampling ) } );
|
||||
export_as_text( Output, "latitude", fLatitudeDeg );
|
||||
export_as_text( Output, "convertmodels", iConvertModels );
|
||||
export_as_text( Output, "file.binary.terrain", file_binary_terrain );
|
||||
|
||||
@@ -179,6 +179,12 @@ struct global_settings {
|
||||
GLint iMaxTextureSize{ 4096 }; // maksymalny rozmiar tekstury
|
||||
GLint iMaxCabTextureSize{ 4096 }; // largest allowed texture in vehicle cab
|
||||
int iMultisampling{ 2 }; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px
|
||||
// gfx.antialiasing: off, msaax2/x4/x8 (the samples go to iMultisampling) or taa (one sample, with the history of the last frames;
|
||||
// iMultisampling 0). the old key multisampling sets msaa or off. read at the start
|
||||
enum class antialiasing_mode { off, msaa, taa };
|
||||
antialiasing_mode gfx_antialiasing{ antialiasing_mode::msaa };
|
||||
// the temporal anti-aliasing is on: asked for, and the post-processing which does it is there
|
||||
bool gfx_taa() const { return ( gfx_antialiasing == antialiasing_mode::taa ) && ( false == gfx_skippipeline ); }
|
||||
float SplineFidelity{ 1.f }; // determines segment size during conversion of splines to geometry
|
||||
float SleeperDistance{ 250.f }; // max distance (in meters) at which per-track sleeper models are still drawn; 0 disables sleeper rendering entirely
|
||||
std::string Sleepers3D; // model of the sleepers and switch timbers laid where the simulator makes the ballast; empty: none
|
||||
@@ -358,6 +364,8 @@ struct global_settings {
|
||||
// one for each of the last frames, instead of with glBufferSubData. read at the start
|
||||
bool gfx_bufferstreaming = true;
|
||||
bool gfx_postfx_motionblur_enabled = true;
|
||||
// the colour pass writes the velocities of the pixels: for the motion blur, or for the temporal anti-aliasing
|
||||
bool gfx_motion_vectors() const { return gfx_postfx_motionblur_enabled || gfx_taa(); }
|
||||
float gfx_postfx_motionblur_shutter = 0.01f;
|
||||
GLenum gfx_postfx_motionblur_format = GL_RG16F;
|
||||
GLenum gfx_format_color = GL_RGB16F;
|
||||
|
||||
Reference in New Issue
Block a user