mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 22:39:19 +02:00
Fix the screen space ambient occlusion and add ground truth ambient occlusion to the GL 4.6 renderer
This commit is contained in:
@@ -336,6 +336,7 @@ if (WITH_OPENGL_MODERN)
|
||||
"rendering/opengl46/opengl46skydome.cpp"
|
||||
"rendering/opengl46/opengl46sky.cpp"
|
||||
"rendering/opengl46/opengl46exposure.cpp"
|
||||
"rendering/opengl46/opengl46ao.cpp"
|
||||
"rendering/opengl46/opengl46precipitation.cpp"
|
||||
"rendering/opengl46/opengl46particles.cpp"
|
||||
"rendering/opengl46/opengl46terrain.cpp"
|
||||
|
||||
@@ -81,6 +81,21 @@ 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.
|
||||
|
||||
### Ambient occlusion
|
||||
|
||||
Darkening of creases, corners and the ground under objects, worked out from the depth of the image (screen space)
|
||||
and multiplied into the image before the motion blur and the tonemapping. It works with and without motion blur.
|
||||
It darkens all light alike, the sun and the headlights included; limiting it to the ambient light needs the depth
|
||||
before the shading (a depth prepass), which is planned later.
|
||||
|
||||
| Entry | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `gfx.postfx.ao gtao/ssao/no` | `gtao` | `gtao` (or `yes`): ground truth ambient occlusion (Jimenez et al. 2016), at half the resolution with a depth aware smoothing and upsampling. `ssao`: the older, experimental ssao. `no`: none, and no targets are made for it; the mode can then not be turned on in the debug window |
|
||||
| `gfx.postfx.ao.radius` | `1.5` | 0.1..10: reach of the occluders, metres |
|
||||
| `gfx.postfx.ao.strength` | `1.0` | 0..4: power the occlusion is raised to; 1 = as computed, 0 = none |
|
||||
| `gfx.postfx.ao.bounce` | `0.3` | 0..1: albedo of the surfaces assumed for the light that comes back after more bounces (brightens the occlusion a little) |
|
||||
| `gfx.postfx.ao.quality slices steps` | `3 4` | directions searched around each pixel (1..8) and samples on each side of a direction (1..16) |
|
||||
|
||||
### Changed option
|
||||
|
||||
`dynamiclights` (0..7) used to limit the number of headlight sets that light the surroundings. It is still
|
||||
@@ -96,7 +111,7 @@ read, but it only takes effect with `gfx.lights.clustered no`.
|
||||
`gfx.lights.clustered no`.
|
||||
- **Reflections (environment map)** are lit with the sun as seen from the reflection itself, and vehicle
|
||||
headlights no longer light the scene in them.
|
||||
- **Motion blur and SSAO** read the real depth of the scene. Before, the depth texture was read in a way the
|
||||
- **Motion blur and ambient occlusion** read the real depth of the scene. Before, the depth texture was read in a way the
|
||||
specification leaves undefined, and the result depended on the driver.
|
||||
- **Lines are 1 pixel wide**, as they always were on this kind of context. The widths the code asked for
|
||||
(catenary wires, map tracks) never took effect.
|
||||
@@ -116,7 +131,8 @@ editor goes through the first `dynamiclights` headlights, the reflections throug
|
||||
- **Sky and exposure** window (next to the Light clusters window): the switches and scales above, the share of the
|
||||
atmosphere, the colours of the sun, ambient light and fog in use, and the measured luminance and exposure.
|
||||
Shift+F12 shows the GPU time of `atmosphere:` (look-up tables, only when the sun moved or the weather changed),
|
||||
`envmap ggx:` (prefiltering, with each update of the environment map), `exposure:` and `bloom:`.
|
||||
`envmap ggx:` (prefiltering, with each update of the environment map), `exposure:` and `bloom:`. The same window holds
|
||||
the ambient occlusion mode and its settings; Shift+F12 shows its time as `ambient occlusion:` (gtao only).
|
||||
- `gfx.gldebug yes` turns on the OpenGL debug output (`KHR_debug`); messages go to the log.
|
||||
|
||||
## Building
|
||||
|
||||
@@ -315,7 +315,7 @@ int generate_nodebank_previews(int Argc, char *Argv[])
|
||||
Global.gfx_skippipeline = false;
|
||||
Global.gfx_postfx_motionblur_enabled = false;
|
||||
Global.gfx_postfx_chromaticaberration_enabled = false;
|
||||
Global.gfx_postfx_ssao_enabled = false;
|
||||
Global.gfx_postfx_ao = 0;
|
||||
Global.gfx_envmap_enabled = false;
|
||||
Global.gfx_shadowmap_enabled = settings.shadows;
|
||||
Global.shadowtune.map_size = std::max(Global.shadowtune.map_size, 2048u);
|
||||
|
||||
130
rendering/opengl46/opengl46ao.cpp
Normal file
130
rendering/opengl46/opengl46ao.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
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/opengl46/opengl46ao.h"
|
||||
|
||||
#include "model/Texture.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/utilities.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// work group of the compute passes, 8x8 (gtao_*.comp)
|
||||
int const GROUP_SIZE{8};
|
||||
|
||||
} // namespace
|
||||
|
||||
void opengl46_ao::init()
|
||||
{
|
||||
auto const make_compute = [](std::string const &Name) {
|
||||
gl::shader shader(Name);
|
||||
return std::make_unique<gl::program>(std::vector<std::reference_wrapper<const gl::shader>>{shader});
|
||||
};
|
||||
m_main_program = make_compute("gtao_main.comp");
|
||||
m_denoise_program = make_compute("gtao_denoise.comp");
|
||||
{
|
||||
gl::shader vertex("quad.vert");
|
||||
gl::shader fragment("postfx_ssao_apply.frag");
|
||||
m_apply_program = std::make_unique<gl::program>(std::vector<std::reference_wrapper<const gl::shader>>{vertex, fragment});
|
||||
}
|
||||
m_vao = std::make_unique<gl::vao>();
|
||||
}
|
||||
|
||||
void opengl46_ao::compute(opengl_texture &Depth, opengl_texture &Raw, opengl_texture &Smooth, glm::mat4 const &Projection)
|
||||
{
|
||||
m_timer.begin();
|
||||
m_timing = true;
|
||||
|
||||
auto const inverseprojection{glm::inverse(Projection)};
|
||||
auto const groups_x{static_cast<GLuint>((Raw.get_width() + GROUP_SIZE - 1) / GROUP_SIZE)};
|
||||
auto const groups_y{static_cast<GLuint>((Raw.get_height() + GROUP_SIZE - 1) / GROUP_SIZE)};
|
||||
|
||||
// horizon search
|
||||
Depth.bind(0);
|
||||
glProgramUniformMatrix4fv(*m_main_program, 0, 1, GL_FALSE, glm::value_ptr(inverseprojection));
|
||||
glProgramUniform2f(*m_main_program, 1, Global.gfx_postfx_ao_radius, Projection[0][0] * 0.5f * static_cast<float>(Raw.get_width()));
|
||||
glProgramUniform4f(*m_main_program, 2, Projection[0][3], Projection[1][3], Projection[2][3], Projection[3][3]);
|
||||
glProgramUniform2i(*m_main_program, 3, Global.gfx_postfx_ao_slices, Global.gfx_postfx_ao_steps);
|
||||
glBindImageTexture(0, Raw.id, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8);
|
||||
m_main_program->dispatch(groups_x, groups_y);
|
||||
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
|
||||
|
||||
// smoothing
|
||||
Raw.bind(1);
|
||||
glProgramUniformMatrix4fv(*m_denoise_program, 0, 1, GL_FALSE, glm::value_ptr(inverseprojection));
|
||||
glBindImageTexture(0, Smooth.id, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8);
|
||||
m_denoise_program->dispatch(groups_x, groups_y);
|
||||
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
|
||||
|
||||
glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8);
|
||||
opengl_texture::reset_unit_cache();
|
||||
}
|
||||
|
||||
void opengl46_ao::apply(int const Mode, opengl_texture &Depth, opengl_texture &Occlusion, gl::framebuffer &Target, glm::mat4 const &Projection)
|
||||
{
|
||||
Target.bind();
|
||||
m_apply_program->bind();
|
||||
m_vao->bind();
|
||||
Depth.bind(0);
|
||||
Occlusion.bind(1);
|
||||
glProgramUniformMatrix4fv(*m_apply_program, 0, 1, GL_FALSE, glm::value_ptr(glm::inverse(Projection)));
|
||||
glProgramUniform4f(*m_apply_program, 1, (Mode == 1 ? 1.f : 0.f), Global.gfx_postfx_ao_strength, Global.gfx_postfx_ao_bounce, 0.f);
|
||||
|
||||
// result = image * occlusion; the blending state is put back as it was
|
||||
auto const blending{glIsEnabled(GL_BLEND) == GL_TRUE};
|
||||
GLint blendfunc[4];
|
||||
glGetIntegerv(GL_BLEND_SRC_RGB, &blendfunc[0]);
|
||||
glGetIntegerv(GL_BLEND_DST_RGB, &blendfunc[1]);
|
||||
glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendfunc[2]);
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, &blendfunc[3]);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBlendFuncSeparate(blendfunc[0], blendfunc[1], blendfunc[2], blendfunc[3]);
|
||||
if (!blending)
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
opengl_texture::reset_unit_cache();
|
||||
|
||||
if (m_timing)
|
||||
{
|
||||
m_timer.end();
|
||||
m_timing = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string opengl46_ao::info_times()
|
||||
{
|
||||
std::string text;
|
||||
if (auto const time = m_timer.result())
|
||||
text += " ambient occlusion: " + to_string(static_cast<double>(*time) / 1000000.0, 3) + " ms\n";
|
||||
return text;
|
||||
}
|
||||
|
||||
void opengl46_ao::draw_debug_ui(bool const Available)
|
||||
{
|
||||
if (!Available)
|
||||
{
|
||||
ImGui::TextUnformatted("ambient occlusion: off at the start, set gfx.postfx.ao in eu07.ini");
|
||||
return;
|
||||
}
|
||||
char const *modes[]{"off", "older ssao", "ground truth (gtao)"};
|
||||
ImGui::Combo("ambient occlusion (gfx.postfx.ao)", &Global.gfx_postfx_ao, modes, IM_ARRAYSIZE(modes));
|
||||
if (Global.gfx_postfx_ao != 2)
|
||||
return;
|
||||
ImGui::SliderFloat("ao radius (m)", &Global.gfx_postfx_ao_radius, 0.1f, 10.f, "%.2f", ImGuiSliderFlags_Logarithmic);
|
||||
ImGui::SliderFloat("ao strength", &Global.gfx_postfx_ao_strength, 0.f, 4.f);
|
||||
ImGui::SliderFloat("ao bounce albedo", &Global.gfx_postfx_ao_bounce, 0.f, 1.f);
|
||||
ImGui::SliderInt("ao slices", &Global.gfx_postfx_ao_slices, 1, 8);
|
||||
ImGui::SliderInt("ao steps per side", &Global.gfx_postfx_ao_steps, 1, 16);
|
||||
}
|
||||
55
rendering/opengl46/opengl46ao.h
Normal file
55
rendering/opengl46/opengl46ao.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "gl/shader.h"
|
||||
#include "gl/vao.h"
|
||||
#include "gl/framebuffer.h"
|
||||
#include "gl/query.h"
|
||||
|
||||
class opengl_texture;
|
||||
|
||||
// screen space ambient occlusion of the post processing of the opengl 4.6 renderer:
|
||||
// - ground truth ambient occlusion (Jimenez et al. 2016) at half the resolution in two compute passes, the horizon search
|
||||
// (gtao_main.comp) and a depth aware smoothing (gtao_denoise.comp)
|
||||
// - its application to the image: a multiplier drawn over it (postfx_ssao_apply.frag), which also applies the older
|
||||
// ssao (postfx_ssao.frag, run by the renderer) when that one is chosen
|
||||
// switched by Global.gfx_postfx_ao (0 off, 1 older ssao, 2 gtao); the targets are made with the viewport when it is not 0
|
||||
class opengl46_ao
|
||||
{
|
||||
public:
|
||||
opengl46_ao() = default;
|
||||
opengl46_ao(const opengl46_ao &) = delete;
|
||||
opengl46_ao &operator=(const opengl46_ao &) = delete;
|
||||
|
||||
// creates the programs. throws gl::shader_exception
|
||||
void init();
|
||||
// ground truth ambient occlusion from the depth of the scene drawn with the given projection: Raw gets the result of
|
||||
// the horizon search, Smooth the smoothed one. both half the size of Depth
|
||||
void compute(opengl_texture &Depth, opengl_texture &Raw, opengl_texture &Smooth, glm::mat4 const &Projection);
|
||||
// multiplies the image of the framebuffer by the occlusion. Mode: Global.gfx_postfx_ao. the viewport has to be set
|
||||
// to the size of Depth. leaves the framebuffer bound
|
||||
void apply(int const Mode, opengl_texture &Depth, opengl_texture &Occlusion, gl::framebuffer &Target, glm::mat4 const &Projection);
|
||||
// lines for the gpu times of the debug panel
|
||||
std::string info_times();
|
||||
// controls of the debug window. Available: the targets were made, so the mode can be changed
|
||||
void draw_debug_ui(bool const Available);
|
||||
|
||||
private:
|
||||
std::unique_ptr<gl::program> m_main_program;
|
||||
std::unique_ptr<gl::program> m_denoise_program;
|
||||
std::unique_ptr<gl::program> m_apply_program;
|
||||
std::unique_ptr<gl::vao> m_vao;
|
||||
gl::gpu_timer m_timer;
|
||||
bool m_timing{false};
|
||||
};
|
||||
@@ -185,6 +185,7 @@ bool opengl46_renderer::Init(GLFWwindow *Window)
|
||||
{
|
||||
m_sky.init();
|
||||
m_exposure.init();
|
||||
m_ao.init();
|
||||
}
|
||||
catch (gl::shader_exception const &e)
|
||||
{
|
||||
@@ -220,7 +221,6 @@ bool opengl46_renderer::Init(GLFWwindow *Window)
|
||||
|
||||
m_pfx_ssao = std::make_unique<gl::postfx>("ssao");
|
||||
m_pfx_ssao_blur = std::make_unique<gl::postfx>("ssao_blur");
|
||||
m_pfx_ssao_apply = std::make_unique<gl::postfx>("ssao_apply");
|
||||
|
||||
// Generate hemisphere kernel (z > 0 = toward surface normal)
|
||||
std::uniform_real_distribution<float> rnd(0.0f, 1.0f);
|
||||
@@ -494,7 +494,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_ssao_enabled)
|
||||
if (Global.gfx_postfx_chromaticaberration_enabled || Global.gfx_postfx_motionblur_enabled || 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);
|
||||
@@ -539,7 +539,7 @@ bool opengl46_renderer::init_viewport(viewport_config &vp)
|
||||
vp.main2_fb = std::make_unique<gl::framebuffer>();
|
||||
vp.main2_fb->attach(*vp.main2_tex, GL_COLOR_ATTACHMENT0);
|
||||
|
||||
if (Global.gfx_postfx_ssao_enabled)
|
||||
if (Global.gfx_postfx_ao != 0)
|
||||
{
|
||||
int ssao_w = std::max(1, vp.width / 2);
|
||||
int ssao_h = std::max(1, vp.height / 2);
|
||||
@@ -555,6 +555,13 @@ bool opengl46_renderer::init_viewport(viewport_config &vp)
|
||||
vp.ssao_blur_fb = std::make_unique<gl::framebuffer>();
|
||||
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)
|
||||
{
|
||||
vp.ao_apply_fb = std::make_unique<gl::framebuffer>();
|
||||
vp.ao_apply_fb->attach(*vp.main_tex, GL_COLOR_ATTACHMENT0);
|
||||
if (!vp.ao_apply_fb->is_complete()) { ErrorLog("ambient occlusion framebuffer setup failed"); return false; }
|
||||
}
|
||||
}
|
||||
|
||||
if (!vp.main2_fb->is_complete())
|
||||
@@ -693,6 +700,7 @@ void opengl46_renderer::SwapBuffers()
|
||||
m_debugtimestext += " cull: " + to_string(static_cast<double>(*culltime) / 1000000.0, 3) + " ms\n";
|
||||
m_debugtimestext += m_sky.info_times();
|
||||
m_debugtimestext += m_exposure.info_times();
|
||||
m_debugtimestext += m_ao.info_times();
|
||||
m_debugtimestext += "uilayer: " + to_string( Timer::subsystem.gfx_gui.average(), 2 ) + " ms\n";
|
||||
if( DebugModeFlag )
|
||||
m_debugtimestext += m_textures.info();
|
||||
@@ -785,6 +793,8 @@ void opengl46_renderer::draw_debug_ui()
|
||||
m_sky.draw_debug_ui();
|
||||
ImGui::Separator();
|
||||
m_exposure.draw_debug_ui();
|
||||
ImGui::Separator();
|
||||
m_ao.draw_debug_ui(!m_viewports.empty() && m_viewports.front()->ssao_tex != nullptr);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
@@ -1078,6 +1088,8 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
|
||||
|
||||
if (!Global.gfx_skippipeline)
|
||||
{
|
||||
// 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 (Global.gfx_postfx_motionblur_enabled)
|
||||
{
|
||||
vp.main_fb->clear(GL_COLOR_BUFFER_BIT);
|
||||
@@ -1086,38 +1098,24 @@ if (!Global.gfx_skippipeline)
|
||||
vp.msaa_fb->blit_to(vp.main_fb.get(), vp.width, vp.height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT1);
|
||||
vp.msaa_fb->blit_to(vp.main_fb.get(), vp.width, vp.height, GL_DEPTH_BUFFER_BIT, GL_DEPTH_ATTACHMENT);
|
||||
|
||||
// the occlusion goes into the image before the blur, which smears both alike
|
||||
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());
|
||||
}
|
||||
else if (Global.gfx_postfx_ssao_enabled)
|
||||
{
|
||||
// resolve color+depth so SSAO can sample the depth texture
|
||||
vp.main_fb->clear(GL_COLOR_BUFFER_BIT);
|
||||
vp.main_fb->setup_drawing(1);
|
||||
vp.msaa_fb->blit_to(vp.main_fb.get(), vp.width, vp.height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0);
|
||||
vp.msaa_fb->blit_to(vp.main_fb.get(), vp.width, vp.height, GL_DEPTH_BUFFER_BIT, GL_DEPTH_ATTACHMENT);
|
||||
}
|
||||
else
|
||||
{
|
||||
vp.main2_fb->clear(GL_COLOR_BUFFER_BIT);
|
||||
vp.msaa_fb->blit_to(vp.main2_fb.get(), vp.width, vp.height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0);
|
||||
}
|
||||
|
||||
// SSAO: depth -> occlusion -> blur -> modulate color into main2_fb
|
||||
if (Global.gfx_postfx_ssao_enabled && !Global.gfx_postfx_motionblur_enabled)
|
||||
{
|
||||
int ssao_w = std::max(1, vp.width / 2);
|
||||
int ssao_h = std::max(1, vp.height / 2);
|
||||
|
||||
// (kernel is generated procedurally in the shader; no UBO upload needed)
|
||||
glViewport(0, 0, ssao_w, ssao_h);
|
||||
m_pfx_ssao ->apply({ vp.main_texd.get(), m_ssao_noise_tex.get() }, vp.ssao_fb.get());
|
||||
m_pfx_ssao_blur->apply({ vp.ssao_tex.get() }, vp.ssao_blur_fb.get());
|
||||
|
||||
glViewport(0, 0, vp.width, vp.height);
|
||||
vp.main2_fb->clear(GL_COLOR_BUFFER_BIT);
|
||||
m_pfx_ssao_apply->apply({ vp.main_tex.get(), vp.ssao_blur_tex.get() }, vp.main2_fb.get());
|
||||
if (aomode != 0)
|
||||
{
|
||||
// the depth for the occlusion to read, the image stays in main2
|
||||
vp.msaa_fb->blit_to(vp.main_fb.get(), vp.width, vp.height, GL_DEPTH_BUFFER_BIT, GL_DEPTH_ATTACHMENT);
|
||||
Render_ambient_occlusion(vp, *vp.main2_fb, aomode);
|
||||
}
|
||||
}
|
||||
|
||||
// the exposure follows the main view; the other viewports use it too
|
||||
@@ -6200,6 +6198,26 @@ void opengl46_renderer::Cull_Lights(viewport_config &vp)
|
||||
m_cullstimer.end();
|
||||
}
|
||||
|
||||
void opengl46_renderer::Render_ambient_occlusion(viewport_config &vp, gl::framebuffer &Target, int const Mode)
|
||||
{
|
||||
glDebug("ambient occlusion");
|
||||
// the projection the depth was drawn with
|
||||
auto const &projection{m_colorpass.pass_camera.projection()};
|
||||
if (Mode == 1)
|
||||
{
|
||||
// the older ssao, which reads the projection from the scene uniforms
|
||||
glViewport(0, 0, vp.ssao_tex->get_width(), vp.ssao_tex->get_height());
|
||||
m_pfx_ssao->apply({vp.main_texd.get(), m_ssao_noise_tex.get()}, vp.ssao_fb.get());
|
||||
m_pfx_ssao_blur->apply({vp.ssao_tex.get()}, vp.ssao_blur_fb.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ao.compute(*vp.main_texd, *vp.ssao_tex, *vp.ssao_blur_tex, projection);
|
||||
}
|
||||
glViewport(0, 0, vp.width, vp.height);
|
||||
m_ao.apply(Mode, *vp.main_texd, *vp.ssao_blur_tex, Target, projection);
|
||||
}
|
||||
|
||||
void opengl46_renderer::Render_cluster_heatmap(viewport_config &vp, gl::framebuffer *Target, glm::ivec2 const &Targetsize)
|
||||
{
|
||||
glDebug("Render_cluster_heatmap");
|
||||
|
||||
@@ -34,6 +34,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "rendering/opengl46/opengl46terrain.h"
|
||||
#include "rendering/opengl46/opengl46sky.h"
|
||||
#include "rendering/opengl46/opengl46exposure.h"
|
||||
#include "rendering/opengl46/opengl46ao.h"
|
||||
|
||||
// bare-bones render controller, in lack of anything better yet
|
||||
class opengl46_renderer : public gfx_renderer {
|
||||
@@ -253,11 +254,13 @@ class opengl46_renderer : public gfx_renderer {
|
||||
std::unique_ptr<gl::framebuffer> main2_fb;
|
||||
std::unique_ptr<opengl_texture> main2_tex;
|
||||
|
||||
// ssao
|
||||
// ambient occlusion, half size: raw and smoothed (gtao), or the ssao and its blur (older ssao)
|
||||
std::unique_ptr<opengl_texture> ssao_tex;
|
||||
std::unique_ptr<opengl_texture> ssao_blur_tex;
|
||||
std::unique_ptr<gl::framebuffer> ssao_fb;
|
||||
std::unique_ptr<gl::framebuffer> ssao_blur_fb;
|
||||
// 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;
|
||||
|
||||
// LDR backbuffer for offscreen rendering
|
||||
std::unique_ptr<gl::framebuffer> backbuffer_fb;
|
||||
@@ -358,6 +361,8 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// debug view: tints the finished image of the viewport with the cluster of every pixel. Target is the framebuffer
|
||||
// holding the image (null for the default one), Targetsize its size
|
||||
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);
|
||||
glm::vec3 pick_color(std::size_t const Index);
|
||||
std::size_t pick_index(glm::ivec3 const &Color);
|
||||
|
||||
@@ -391,6 +396,8 @@ class opengl46_renderer : public gfx_renderer {
|
||||
opengl46_sky m_sky;
|
||||
// automatic exposure and bloom of the post processing
|
||||
opengl46_exposure m_exposure;
|
||||
// screen space ambient occlusion of the post processing
|
||||
opengl46_ao m_ao;
|
||||
// true while the environment map bound for the shading is the real one (not the empty stand-in of the reflection pass)
|
||||
bool m_envmap_bound{false};
|
||||
/*
|
||||
@@ -554,7 +561,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// postfx ssao
|
||||
std::unique_ptr<gl::postfx> m_pfx_ssao;
|
||||
std::unique_ptr<gl::postfx> m_pfx_ssao_blur;
|
||||
std::unique_ptr<gl::postfx> m_pfx_ssao_apply;
|
||||
std::unique_ptr<opengl_texture> m_ssao_noise_tex;
|
||||
glm::vec3 m_ssao_kernel[32];
|
||||
|
||||
|
||||
50
shaders/gtao_denoise.comp
Normal file
50
shaders/gtao_denoise.comp
Normal file
@@ -0,0 +1,50 @@
|
||||
// smooths the ambient occlusion of gtao_main.comp: the average of the 4x4 pixels around each one, which takes out the
|
||||
// repeating 4x4 pattern of the slices, with the pixels at a clearly different distance left out so the occlusion does
|
||||
// not leak across the edges of objects. one invocation per pixel of the half size target
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8) in;
|
||||
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 1) uniform sampler2D ao_tex;
|
||||
layout(binding = 0, r8) writeonly uniform image2D ao_target;
|
||||
|
||||
layout(location = 0) uniform mat4 inv_projection;
|
||||
|
||||
// relative difference of the distance at which a neighbour stops counting
|
||||
const float DEPTH_TOLERANCE = 0.05;
|
||||
|
||||
float view_distance(ivec2 texel)
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
float depth = texelFetch(depth_tex, min(texel * 2, full_size - 1), 0).r;
|
||||
if (depth <= 0.0)
|
||||
return 1e30;
|
||||
vec2 uv = (vec2(texel * 2) + 0.5) / vec2(full_size);
|
||||
vec4 position = inv_projection * vec4(uv * 2.0 - 1.0, depth, 1.0);
|
||||
return length(position.xyz / position.w);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 size = imageSize(ao_target);
|
||||
if (texel.x >= size.x || texel.y >= size.y)
|
||||
return;
|
||||
|
||||
float distance = view_distance(texel);
|
||||
float sum = 0.0;
|
||||
float weights = 0.0;
|
||||
for (int y = -2; y < 2; ++y)
|
||||
{
|
||||
for (int x = -2; x < 2; ++x)
|
||||
{
|
||||
ivec2 sampletexel = clamp(texel + ivec2(x, y), ivec2(0), size - 1);
|
||||
float sampledistance = view_distance(sampletexel);
|
||||
float weight = clamp(1.0 - abs(sampledistance - distance) / (DEPTH_TOLERANCE * distance), 0.0, 1.0);
|
||||
sum += texelFetch(ao_tex, sampletexel, 0).r * weight;
|
||||
weights += weight;
|
||||
}
|
||||
}
|
||||
// the pixel itself is in the window with the full weight, so weights >= 1
|
||||
imageStore(ao_target, texel, vec4(sum / weights));
|
||||
}
|
||||
152
shaders/gtao_main.comp
Normal file
152
shaders/gtao_main.comp
Normal file
@@ -0,0 +1,152 @@
|
||||
// ground truth ambient occlusion (Jimenez et al. 2016, "Practical realtime strategies for accurate indirect occlusion";
|
||||
// integration as in Intel XeGTAO) at half the resolution of the image. for every pixel, a few slices through the
|
||||
// hemisphere around the view direction; in each the horizons on both sides are searched in the depth buffer and the
|
||||
// cosine weighted visible arc between them is integrated against the normal rebuilt from the depth.
|
||||
// one invocation per pixel of the half size target. the pattern of the slice directions and steps repeats every 4x4
|
||||
// pixels; gtao_denoise.comp averages it out
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8) in;
|
||||
|
||||
// depth of the scene at full resolution: reversed, 1 at the near plane, 0 at infinity (the sky)
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 0, r8) writeonly uniform image2D ao_target;
|
||||
|
||||
layout(location = 0) uniform mat4 inv_projection;
|
||||
// x: radius in metres, y: projection[0][0] times half the width of the target in pixels
|
||||
layout(location = 1) uniform vec2 params;
|
||||
// the row of the projection which gives the clip w of a view space position
|
||||
layout(location = 2) uniform vec4 clipw_row;
|
||||
// x: number of slices, y: steps on each side of a slice
|
||||
layout(location = 3) uniform ivec2 quality;
|
||||
|
||||
const float PI = 3.14159265;
|
||||
const float HALF_PI = 1.57079633;
|
||||
// share of the radius over which the weight of an occluder falls to nothing
|
||||
const float FALLOFF_RANGE = 0.615;
|
||||
|
||||
// depth of the pixel of the half size target: the top left one of the four full size pixels it covers, so the
|
||||
// application (postfx_ssao_apply.frag) finds the same depth for it
|
||||
float half_depth(ivec2 texel)
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
return texelFetch(depth_tex, min(texel * 2, full_size - 1), 0).r;
|
||||
}
|
||||
|
||||
vec3 view_position(ivec2 texel, float depth)
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
vec2 uv = (vec2(texel * 2) + 0.5) / vec2(full_size);
|
||||
vec4 position = inv_projection * vec4(uv * 2.0 - 1.0, depth, 1.0);
|
||||
return position.xyz / position.w;
|
||||
}
|
||||
|
||||
// 4x4 ordered pattern, values 0..15
|
||||
const float BAYER[16] = float[](0.0, 8.0, 2.0, 10.0, 12.0, 4.0, 14.0, 6.0, 3.0, 11.0, 1.0, 9.0, 15.0, 7.0, 13.0, 5.0);
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 size = imageSize(ao_target);
|
||||
if (texel.x >= size.x || texel.y >= size.y)
|
||||
return;
|
||||
|
||||
float depth = half_depth(texel);
|
||||
if (depth <= 0.0)
|
||||
{
|
||||
imageStore(ao_target, texel, vec4(1.0));
|
||||
return;
|
||||
}
|
||||
vec3 position = view_position(texel, depth);
|
||||
vec3 view = normalize(-position);
|
||||
|
||||
// normal from the neighbours: on each axis the one closer in depth, so edges of objects do not bend it
|
||||
vec3 left = view_position(texel - ivec2(1, 0), half_depth(texel - ivec2(1, 0)));
|
||||
vec3 right = view_position(texel + ivec2(1, 0), half_depth(texel + ivec2(1, 0)));
|
||||
vec3 down = view_position(texel - ivec2(0, 1), half_depth(texel - ivec2(0, 1)));
|
||||
vec3 up = view_position(texel + ivec2(0, 1), half_depth(texel + ivec2(0, 1)));
|
||||
vec3 dx = (abs(right.z - position.z) < abs(position.z - left.z)) ? right - position : position - left;
|
||||
vec3 dy = (abs(up.z - position.z) < abs(position.z - down.z)) ? up - position : position - down;
|
||||
vec3 normal = normalize(cross(dx, dy));
|
||||
if (dot(normal, view) < 0.0)
|
||||
normal = -normal;
|
||||
|
||||
float radius = params.x;
|
||||
float clipw = dot(clipw_row, vec4(position, 1.0));
|
||||
float screenradius = params.y * radius / max(clipw, 1e-6);
|
||||
// smaller than a pixel: nothing to find
|
||||
if (screenradius < 1.0)
|
||||
{
|
||||
imageStore(ao_target, texel, vec4(1.0));
|
||||
return;
|
||||
}
|
||||
screenradius = min(screenradius, 256.0);
|
||||
|
||||
float falloffrange = FALLOFF_RANGE * radius;
|
||||
float falloffmul = -1.0 / falloffrange;
|
||||
float falloffadd = (radius - falloffrange) / falloffrange + 1.0;
|
||||
|
||||
int pattern = (texel.x & 3) + 4 * (texel.y & 3);
|
||||
float slicenoise = (BAYER[pattern] + 0.5) / 16.0;
|
||||
float stepnoise = fract(float(pattern) * 0.618034 + 0.25);
|
||||
|
||||
int slices = max(quality.x, 1);
|
||||
int steps = max(quality.y, 1);
|
||||
float visibility = 0.0;
|
||||
for (int slice = 0; slice < slices; ++slice)
|
||||
{
|
||||
float angle = (float(slice) + slicenoise) * PI / float(slices);
|
||||
vec2 omega = vec2(cos(angle), sin(angle));
|
||||
vec3 direction = vec3(omega, 0.0);
|
||||
vec3 orthodirection = direction - dot(direction, view) * view;
|
||||
vec3 axis = normalize(cross(orthodirection, view));
|
||||
vec3 projectednormal = normal - axis * dot(normal, axis);
|
||||
float projectedlength = length(projectednormal);
|
||||
if (projectedlength < 1e-4)
|
||||
continue;
|
||||
float signnormal = sign(dot(orthodirection, projectednormal));
|
||||
float cosnormal = clamp(dot(projectednormal, view) / projectedlength, 0.0, 1.0);
|
||||
float n = signnormal * acos(cosnormal);
|
||||
|
||||
float lowcos0 = cos(n + HALF_PI);
|
||||
float lowcos1 = cos(n - HALF_PI);
|
||||
float horizoncos0 = lowcos0;
|
||||
float horizoncos1 = lowcos1;
|
||||
|
||||
for (int step = 0; step < steps; ++step)
|
||||
{
|
||||
// denser near the pixel, where the occluders matter most
|
||||
float s = (float(step) + stepnoise) / float(steps);
|
||||
s = s * s;
|
||||
vec2 offset = omega * max(s * screenradius, float(step) + 1.0);
|
||||
for (int side = 0; side < 2; ++side)
|
||||
{
|
||||
ivec2 sampletexel = texel + ivec2(round(side == 0 ? offset : -offset));
|
||||
if (any(lessThan(sampletexel, ivec2(0))) || any(greaterThanEqual(sampletexel, size)))
|
||||
continue;
|
||||
float sampledepth = half_depth(sampletexel);
|
||||
if (sampledepth <= 0.0)
|
||||
continue;
|
||||
vec3 delta = view_position(sampletexel, sampledepth) - position;
|
||||
float distance = length(delta);
|
||||
if (distance < 1e-5)
|
||||
continue;
|
||||
float horizoncos = dot(delta / distance, view);
|
||||
float weight = clamp(distance * falloffmul + falloffadd, 0.0, 1.0);
|
||||
if (side == 0)
|
||||
horizoncos0 = max(horizoncos0, mix(lowcos0, horizoncos, weight));
|
||||
else
|
||||
horizoncos1 = max(horizoncos1, mix(lowcos1, horizoncos, weight));
|
||||
}
|
||||
}
|
||||
|
||||
float h0 = -acos(clamp(horizoncos1, -1.0, 1.0));
|
||||
float h1 = acos(clamp(horizoncos0, -1.0, 1.0));
|
||||
h0 = n + clamp(h0 - n, -HALF_PI, HALF_PI);
|
||||
h1 = n + clamp(h1 - n, -HALF_PI, HALF_PI);
|
||||
float arc0 = (cosnormal + 2.0 * h0 * sin(n) - cos(2.0 * h0 - n)) * 0.25;
|
||||
float arc1 = (cosnormal + 2.0 * h1 * sin(n) - cos(2.0 * h1 - n)) * 0.25;
|
||||
visibility += projectedlength * (arc0 + arc1);
|
||||
}
|
||||
visibility = clamp(visibility / float(slices), 0.0, 1.0);
|
||||
imageStore(ao_target, texel, vec4(visibility));
|
||||
}
|
||||
@@ -9,8 +9,9 @@ uniform sampler2D noise_tex;
|
||||
|
||||
#include <common>
|
||||
|
||||
// the depth is reversed (glClipControl GL_ZERO_TO_ONE, 1 at the near plane, 0 at infinity), so it is the clip z itself
|
||||
vec3 view_pos_from_depth(vec2 uv, float depth) {
|
||||
vec4 clip = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
|
||||
vec4 clip = vec4(uv * 2.0 - 1.0, depth, 1.0);
|
||||
vec4 view = inverse(projection) * clip;
|
||||
return view.xyz / view.w;
|
||||
}
|
||||
@@ -31,7 +32,7 @@ vec3 kernel_sample(int i) {
|
||||
|
||||
void main() {
|
||||
float d = texture(depth_tex, f_coords).r;
|
||||
if (d >= 1.0) { out_color = vec4(1.0); return; } // skybox -> no occlusion
|
||||
if (d <= 0.0) { out_color = vec4(1.0); return; } // skybox -> no occlusion
|
||||
|
||||
vec3 pos = view_pos_from_depth(f_coords, d);
|
||||
|
||||
|
||||
@@ -1,22 +1,80 @@
|
||||
// applies the ambient occlusion of the half size target to the image at full size, as a multiplier: drawn with the
|
||||
// blending dst * src over the image (opengl46_ao::apply). the four nearest pixels of the occlusion are weighed by how
|
||||
// close they are and by how close their distance is to that of the pixel, so the occlusion does not spill over the
|
||||
// edges of objects. each pixel of the occlusion stands for the top left one of the four full size pixels it covers
|
||||
// params.x: 0 ground truth ambient occlusion, 1 the older ssao (its own fixed curve)
|
||||
// params.y: strength, a power of the occlusion (1 = as computed); params.z: albedo assumed by the multiple bounce term
|
||||
|
||||
in vec2 f_coords;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#texture (color_tex, 0, RGB)
|
||||
uniform sampler2D color_tex;
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 1) uniform sampler2D ao_tex;
|
||||
|
||||
#texture (ssao_tex, 1, R)
|
||||
uniform sampler2D ssao_tex;
|
||||
layout(location = 0) uniform mat4 inv_projection;
|
||||
layout(location = 1) uniform vec4 params;
|
||||
|
||||
const float SSAO_STRENGTH = 0.8;
|
||||
const float SSAO_MIN = 0.3; // floor on darkening so shadows don't go pitch black
|
||||
float view_distance(ivec2 pixel, float depth)
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
vec2 uv = (vec2(pixel) + 0.5) / vec2(full_size);
|
||||
vec4 position = inv_projection * vec4(uv * 2.0 - 1.0, depth, 1.0);
|
||||
return length(position.xyz / position.w);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 c = texture(color_tex, f_coords).rgb;
|
||||
float occ = texture(ssao_tex, f_coords).r;
|
||||
// light which comes back after more than one bounce off surfaces of the given albedo, fit of Jimenez et al. 2016
|
||||
float multi_bounce(float visibility, float albedo)
|
||||
{
|
||||
float a = 2.0404 * albedo - 0.3324;
|
||||
float b = -4.7951 * albedo + 0.6417;
|
||||
float c = 2.7552 * albedo + 0.6903;
|
||||
return max(visibility, ((visibility * a + b) * visibility + c) * visibility);
|
||||
}
|
||||
|
||||
// remap occlusion with strength and a minimum floor
|
||||
occ = pow(clamp(occ, 0.0, 1.0), SSAO_STRENGTH);
|
||||
occ = mix(SSAO_MIN, 1.0, occ);
|
||||
void main()
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
ivec2 half_size = textureSize(ao_tex, 0);
|
||||
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
||||
float depth = texelFetch(depth_tex, pixel, 0).r;
|
||||
if (depth <= 0.0)
|
||||
{
|
||||
// the sky
|
||||
out_color = vec4(1.0);
|
||||
return;
|
||||
}
|
||||
float distance = view_distance(pixel, depth);
|
||||
|
||||
out_color = vec4(c * occ, 1.0);
|
||||
}
|
||||
vec2 position = vec2(pixel) * 0.5;
|
||||
ivec2 base = ivec2(floor(position));
|
||||
vec2 fraction = position - vec2(base);
|
||||
float sum = 0.0;
|
||||
float weights = 0.0;
|
||||
for (int y = 0; y < 2; ++y)
|
||||
{
|
||||
for (int x = 0; x < 2; ++x)
|
||||
{
|
||||
ivec2 texel = clamp(base + ivec2(x, y), ivec2(0), half_size - 1);
|
||||
ivec2 texelpixel = min(texel * 2, full_size - 1);
|
||||
float texeldepth = texelFetch(depth_tex, texelpixel, 0).r;
|
||||
float texeldistance = (texeldepth > 0.0) ? view_distance(texelpixel, texeldepth) : 1e30;
|
||||
float bilinear = (x == 0 ? 1.0 - fraction.x : fraction.x) * (y == 0 ? 1.0 - fraction.y : fraction.y);
|
||||
float weight = (bilinear + 1e-3) / (1e-3 + abs(texeldistance - distance) / distance);
|
||||
sum += texelFetch(ao_tex, texel, 0).r * weight;
|
||||
weights += weight;
|
||||
}
|
||||
}
|
||||
float ao = clamp(sum / max(weights, 1e-6), 0.0, 1.0);
|
||||
|
||||
if (params.x > 0.5)
|
||||
{
|
||||
// the curve the older ssao was applied with, a floor so the shadows do not go black
|
||||
ao = mix(0.3, 1.0, pow(ao, 0.8));
|
||||
}
|
||||
else
|
||||
{
|
||||
ao = pow(ao, params.y);
|
||||
ao = multi_bounce(ao, params.z);
|
||||
}
|
||||
out_color = vec4(vec3(ao), 1.0);
|
||||
}
|
||||
|
||||
@@ -1578,6 +1578,21 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To
|
||||
else if( Token == "gfx.postfx.bloom" ) { readswitch( gfx_postfx_bloom ); }
|
||||
else if( Token == "gfx.postfx.bloom.intensity" ) { readvalue( gfx_postfx_bloom_intensity, 0.f, 1.f ); }
|
||||
else if( Token == "gfx.postfx.bloom.threshold" ) { readvalue( gfx_postfx_bloom_threshold, 0.f, 1000.f ); }
|
||||
else if( Token == "gfx.postfx.ao" ) {
|
||||
// yes/gtao: ground truth ambient occlusion, ssao: the older one, anything else: off
|
||||
Parser.getTokens( 1 );
|
||||
std::string mode;
|
||||
Parser >> mode;
|
||||
gfx_postfx_ao = ( mode == "yes" || mode == "true" || mode == "1" || mode == "gtao" ) ? 2 : ( mode == "ssao" ) ? 1 : 0; }
|
||||
else if( Token == "gfx.postfx.ao.radius" ) { readvalue( gfx_postfx_ao_radius, 0.1f, 10.f ); }
|
||||
else if( Token == "gfx.postfx.ao.strength" ) { readvalue( gfx_postfx_ao_strength, 0.f, 4.f ); }
|
||||
else if( Token == "gfx.postfx.ao.bounce" ) { readvalue( gfx_postfx_ao_bounce, 0.f, 1.f ); }
|
||||
else if( Token == "gfx.postfx.ao.quality" ) {
|
||||
// slices and steps per side
|
||||
Parser.getTokens( 2, false );
|
||||
Parser >> gfx_postfx_ao_slices >> gfx_postfx_ao_steps;
|
||||
gfx_postfx_ao_slices = std::clamp( gfx_postfx_ao_slices, 1, 8 );
|
||||
gfx_postfx_ao_steps = std::clamp( gfx_postfx_ao_steps, 1, 16 ); }
|
||||
else { return false; }
|
||||
|
||||
return true;
|
||||
@@ -1839,6 +1854,11 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
export_as_text( Output, "gfx.postfx.bloom", gfx_postfx_bloom );
|
||||
export_as_text( Output, "gfx.postfx.bloom.intensity", gfx_postfx_bloom_intensity );
|
||||
export_as_text( Output, "gfx.postfx.bloom.threshold", gfx_postfx_bloom_threshold );
|
||||
export_as_text( Output, "gfx.postfx.ao", std::string{ gfx_postfx_ao == 2 ? "gtao" : gfx_postfx_ao == 1 ? "ssao" : "no" } );
|
||||
export_as_text( Output, "gfx.postfx.ao.radius", gfx_postfx_ao_radius );
|
||||
export_as_text( Output, "gfx.postfx.ao.strength", gfx_postfx_ao_strength );
|
||||
export_as_text( Output, "gfx.postfx.ao.bounce", gfx_postfx_ao_bounce );
|
||||
Output << "gfx.postfx.ao.quality " << gfx_postfx_ao_slices << " " << gfx_postfx_ao_steps << "\n";
|
||||
export_as_text( Output, "python.enabled", python_enabled );
|
||||
export_as_text( Output, "python.threadedupload", python_threadedupload );
|
||||
export_as_text( Output, "python.uploadmain", python_uploadmain );
|
||||
|
||||
@@ -324,6 +324,14 @@ struct global_settings {
|
||||
bool gfx_postfx_bloom { true }; // glow around bright parts of the image
|
||||
float gfx_postfx_bloom_intensity { 0.04f }; // share of the blurred image in the result
|
||||
float gfx_postfx_bloom_threshold { 0.f }; // luminance below which nothing glows, 0 = everything contributes
|
||||
// screen space ambient occlusion of the opengl 4.6 renderer, applied to the image (debug window "Sky and exposure").
|
||||
// 0 off, 1 the older ssao, 2 ground truth ambient occlusion; when 0 at the start the targets are not made at all
|
||||
int gfx_postfx_ao { 2 };
|
||||
float gfx_postfx_ao_radius { 1.5f }; // reach of the occluders, metres
|
||||
float gfx_postfx_ao_strength { 1.f }; // power the occlusion is raised to, 1 = as computed, 0 = none
|
||||
float gfx_postfx_ao_bounce { 0.3f }; // albedo of the surfaces assumed for the light coming back after more bounces
|
||||
int gfx_postfx_ao_slices { 3 }; // directions searched around each pixel
|
||||
int gfx_postfx_ao_steps { 4 }; // samples on each side of a direction
|
||||
|
||||
float ui_fontsize = 13.0f;
|
||||
float ui_scale = 1.0f;
|
||||
@@ -333,7 +341,6 @@ struct global_settings {
|
||||
|
||||
std::string exec_on_exit;
|
||||
std::string prepend_scn;
|
||||
bool gfx_postfx_ssao_enabled {false};
|
||||
|
||||
struct extraviewport_config {
|
||||
std::string monitor;
|
||||
@@ -398,7 +405,7 @@ struct global_settings {
|
||||
bool ConfigParseHardware(cParser& Parser, const std::string& token);
|
||||
bool ConfigParseDebug(cParser& Parser, const std::string& token);
|
||||
bool ConfigParse_gfx( cParser &parser, std::string_view const Token );
|
||||
// sky light and exposure switches of the opengl 4.6 renderer (gfx.ambient.*, gfx.envmap.ggx, gfx.atmosphere*, gfx.postfx.autoexposure*, gfx.postfx.bloom*)
|
||||
// sky light and exposure switches of the opengl 4.6 renderer (gfx.ambient.*, gfx.envmap.ggx, gfx.atmosphere*, gfx.postfx.autoexposure*, gfx.postfx.bloom*, gfx.postfx.ao*)
|
||||
bool ConfigParse_gfx_sky( cParser &parser, std::string_view const Token );
|
||||
// sends basic content of the class in legacy (text) format to provided stream
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user