mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-29 05:47:28 +01:00
Fix the screen space ambient occlusion and add ground truth ambient occlusion to the GL 4.6 renderer
This commit is contained in:
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];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user