17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-09 08:00:04 +01:00

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

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

View File

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

View File

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

View File

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

View File

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