13 Commits
v0.7 ... v0.7.1

28 changed files with 1502 additions and 103 deletions

View File

@ -41,6 +41,27 @@ jobs:
## See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail ## See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
#run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure #run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure
linux_gles:
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
# TODO: cache
- name: Install Dependencies
run: sudo apt update && sudo apt -y install libsdl2-dev
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DMM_OPENGL_3_GLES=ON
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4
macos: macos:
timeout-minutes: 10 timeout-minutes: 10

View File

@ -18,10 +18,30 @@ FBOBuilder FBOBuilder::start(void) {
} }
std::shared_ptr<FrameBufferObject> FBOBuilder::finish(void) { std::shared_ptr<FrameBufferObject> FBOBuilder::finish(void) {
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { const auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE) {
return _fbo; return _fbo;
} }
const char* error_str = "UNK";
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
error_str = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
break;
#ifdef MM_OPENGL_3_GLES
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
error_str = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
break;
#endif
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
error_str = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
error_str = "GL_FRAMEBUFFER_UNSUPPORTED";
break;
}
SPDLOG_ERROR("framebuffer status: {}", error_str);
return nullptr; return nullptr;
} }
@ -46,8 +66,15 @@ FBOBuilder& FBOBuilder::attachTexture(std::shared_ptr<Texture> tex, GLuint attac
break; break;
} }
//glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->getHandle(), 0); if (tex->samples == 0u) {
glFramebufferTexture2D(target, attachment_type, GL_TEXTURE_2D, tex->getHandle(), 0); glFramebufferTexture2D(target, attachment_type, GL_TEXTURE_2D, tex->getHandle(), 0);
} else {
#ifndef MM_OPENGL_3_GLES
glFramebufferTexture2D(target, attachment_type, GL_TEXTURE_2D_MULTISAMPLE, tex->getHandle(), 0);
#else
assert(false && "GLES has no multisampling support");
#endif
}
_fbo->_texAttachments.push_back(tex); // keep a ref at the fbo _fbo->_texAttachments.push_back(tex); // keep a ref at the fbo
return *this; return *this;

View File

@ -16,28 +16,49 @@ uint32_t Texture::getHandle(void) const {
Texture::Texture( Texture::Texture(
uint32_t handle, uint32_t handle,
int32_t width_, int32_t height_, int32_t width_, int32_t height_,
int32_t internalFormat, int32_t format, int32_t type int32_t internalFormat, int32_t format, int32_t type,
uint32_t samples_
) : _handle(handle), width(width_), height(height_), ) : _handle(handle), width(width_), height(height_),
_internalFormat(internalFormat), _format(format), _type(type) {} samples(samples_),
_internalFormat(internalFormat), _format(format), _type(type) {
}
Texture::~Texture(void) { Texture::~Texture(void) {
glDeleteTextures(1, &_handle); glDeleteTextures(1, &_handle);
} }
void Texture::unbind(void) const { void Texture::unbind(void) const {
// TODO: do i need ms variant?
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
void Texture::bind(uint32_t slot) const { void Texture::bind(uint32_t slot) const {
glActiveTexture(GL_TEXTURE0 + slot); glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, _handle); if (samples == 0) {
glBindTexture(GL_TEXTURE_2D, _handle);
} else {
#ifndef MM_OPENGL_3_GLES
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _handle);
#else
assert(false && "GLES has no multisampling support");
#endif
}
} }
void Texture::resize(int32_t new_width, int32_t new_height) { void Texture::resize(int32_t new_width, int32_t new_height) {
// if (glTexImage2D == true) // if (glTexImage2D == true)
glBindTexture(GL_TEXTURE_2D, _handle);
glTexImage2D(GL_TEXTURE_2D, 0, _internalFormat, new_width, new_height, 0, _format, _type, NULL); if (samples == 0) {
glBindTexture(GL_TEXTURE_2D, _handle);
glTexImage2D(GL_TEXTURE_2D, 0, _internalFormat, new_width, new_height, 0, _format, _type, NULL);
} else {
#ifndef MM_OPENGL_3_GLES
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _handle);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, _internalFormat, new_width, new_height, 0);
#else
assert(false && "GLES has no multisampling support");
#endif
}
// HACK: super dirty // HACK: super dirty
*(const_cast<int32_t*>(&width)) = new_width; *(const_cast<int32_t*>(&width)) = new_width;
@ -57,5 +78,27 @@ Texture::handle_t Texture::createEmpty(int32_t internalFormat, int32_t width, in
return handle_t(new Texture(id, width, height, internalFormat, format, type)); return handle_t(new Texture(id, width, height, internalFormat, format, type));
} }
Texture::handle_t Texture::createEmptyMultiSampled(int32_t internalFormat, int32_t width, int32_t height, uint32_t samples) {
#ifndef MM_OPENGL_3_GLES
uint32_t id;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, id);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internalFormat, width, height, GL_TRUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
// HACK: format + type?
return handle_t(new Texture(id, width, height, internalFormat, 0, 0, samples));
#else
(void)internalFormat;
(void)width;
(void)height;
(void)samples;
assert(false && "GLES has no multisampling support");
#endif
}
} // MM::OpenGL } // MM::OpenGL

View File

@ -20,7 +20,8 @@ namespace MM::OpenGL {
Texture( Texture(
uint32_t handle, uint32_t handle,
int32_t width_, int32_t height_, int32_t width_, int32_t height_,
int32_t internalFormat, int32_t format, int32_t type int32_t internalFormat, int32_t format, int32_t type,
uint32_t samples = 0u
); );
public: public:
@ -29,6 +30,8 @@ namespace MM::OpenGL {
int32_t const width; int32_t const width;
int32_t const height; int32_t const height;
//int32_t const bpp; // bits per pixel //int32_t const bpp; // bits per pixel
uint32_t const samples{0u}; // sample count, 0 == off
private: private:
int32_t const _internalFormat; int32_t const _internalFormat;
int32_t const _format; int32_t const _format;
@ -45,6 +48,7 @@ namespace MM::OpenGL {
void resize(int32_t new_width, int32_t new_height); void resize(int32_t new_width, int32_t new_height);
static handle_t createEmpty(int32_t internalFormat, int32_t width, int32_t height, int32_t format, int32_t type); static handle_t createEmpty(int32_t internalFormat, int32_t width, int32_t height, int32_t format, int32_t type);
static handle_t createEmptyMultiSampled(int32_t internalFormat, int32_t width, int32_t height, uint32_t samples);
}; };
} // MM::OpenGL } // MM::OpenGL

View File

@ -31,5 +31,12 @@ namespace MM::OpenGL {
} }
}; };
struct TextureLoaderEmptyMultiSampled final {
template<typename... Args>
std::shared_ptr<Texture> load(Args&& ... args) const {
return Texture::createEmptyMultiSampled(std::forward<Args>(args)...);
}
};
} // MM::OpenGL } // MM::OpenGL

View File

@ -9,6 +9,9 @@ add_library(opengl_renderer_s
src/mm/opengl/camera_3d.hpp src/mm/opengl/camera_3d.hpp
src/mm/opengl/camera_3d.cpp src/mm/opengl/camera_3d.cpp
src/mm/opengl/res/shaders_builtin.hpp
src/mm/opengl/res/shaders_builtin.cpp
) )
target_include_directories(opengl_renderer_s PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") target_include_directories(opengl_renderer_s PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
@ -176,6 +179,49 @@ target_link_libraries(blur_render_task
engine engine
) )
############# bloom_extraction render task ###########
add_library(bloom_extraction_render_task
src/mm/opengl/render_tasks/bloom_extraction.hpp
src/mm/opengl/render_tasks/bloom_extraction.cpp
)
target_include_directories(bloom_extraction_render_task PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(bloom_extraction_render_task
opengl_renderer_s
engine
)
############# bloom_combine render task ###########
add_library(bloom_combine_render_task
src/mm/opengl/render_tasks/bloom_combine.hpp
src/mm/opengl/render_tasks/bloom_combine.cpp
)
target_include_directories(bloom_combine_render_task PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(bloom_combine_render_task
opengl_renderer_s
engine
)
############# composition render task ###########
# intendet for bloom compositing and tonemapping
add_library(composition_render_task
src/mm/opengl/render_tasks/composition.hpp
src/mm/opengl/render_tasks/composition.cpp
)
target_include_directories(composition_render_task PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(composition_render_task
opengl_renderer_s
engine
)
############# tilemap renderer ########### ############# tilemap renderer ###########
add_library(tilemap_render_task add_library(tilemap_render_task
@ -207,6 +253,21 @@ target_link_libraries(fast_sky_render_task
engine engine
) )
############# bloom ###########
add_library(bloom
src/mm/opengl/bloom.hpp
src/mm/opengl/bloom.cpp
)
target_include_directories(bloom PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(bloom
bloom_extraction_render_task
blur_render_task
bloom_combine_render_task
)
######################## ########################
if (BUILD_TESTING) if (BUILD_TESTING)

View File

@ -0,0 +1,158 @@
#include "./bloom.hpp"
#include <mm/opengl/render_tasks/bloom_extraction.hpp>
#include <mm/opengl/render_tasks/blur.hpp>
#include <mm/opengl/render_tasks/bloom_combine.hpp>
#include <mm/opengl/fbo_builder.hpp>
#include <mm/opengl/texture_loader.hpp>
namespace MM::OpenGL {
using namespace entt::literals;
void setup_bloom(
MM::Engine& engine,
const std::string color_src_tex,
const size_t bloom_phases,
const float bloom_in_scale,
const float bloom_phase_scale
) {
assert(bloom_phases > 1);
auto& rs = engine.getService<MM::Services::OpenGLRenderer>();
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto [w, h] = engine.getService<MM::Services::SDLService>().getWindowSize();
#ifdef MM_OPENGL_3_GLES
#if 0 // NOPE!!
// TODO: caps at 1, invest in half float?
const auto bloom_internal_format = GL_RGB565; // prolly fine
const auto bloom_format_type = GL_UNSIGNED_BYTE;
#else
const auto bloom_internal_format = GL_RGB16F;
const auto bloom_format_type = GL_FLOAT;
#endif
#else
const auto bloom_internal_format = GL_RGB16F;
const auto bloom_format_type = GL_FLOAT;
#endif
{ // bloom in (bloom extraction)
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"bloom_in",
bloom_internal_format,
w * bloom_in_scale, h * bloom_in_scale,
GL_RGB, bloom_format_type
);
{ // filter
rm_t.get("bloom_in"_hs)->bind(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
rs.targets["bloom_extraction"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("bloom_in"_hs), GL_COLOR_ATTACHMENT0)
.setResizeFactors(bloom_in_scale, bloom_in_scale)
.setResize(true)
.finish();
assert(rs.targets["bloom_extraction"]);
}
// blur textures and fbos
for (size_t i = 1; i <= bloom_phases; i++) {
// TODO: further dedup
std::string tex_out_name {"blur_out" + std::to_string(i)};
auto tex_out_id = entt::hashed_string::value(tex_out_name.c_str());
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
tex_out_id,
bloom_internal_format,
w * bloom_in_scale * glm::pow(bloom_phase_scale, i), h * bloom_in_scale * glm::pow(bloom_phase_scale, i),
GL_RGB, bloom_format_type
);
{ // filter
rm_t.get(tex_out_id)->bind(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
std::string tex_tmp_name {"blur_tmp" + std::to_string(i)};
auto tex_tmp_id = entt::hashed_string::value(tex_tmp_name.c_str());
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
tex_tmp_id,
bloom_internal_format,
w * bloom_in_scale * glm::pow(bloom_phase_scale, i), h * bloom_in_scale * glm::pow(bloom_phase_scale, i),
GL_RGB, bloom_format_type
);
{ // filter
rm_t.get(tex_tmp_id)->bind(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
rs.targets[tex_out_name] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get(tex_out_id), GL_COLOR_ATTACHMENT0)
.setResizeFactors(bloom_in_scale * glm::pow(bloom_phase_scale, i), bloom_in_scale * glm::pow(bloom_phase_scale, i))
.setResize(true)
.finish();
assert(rs.targets[tex_out_name]);
rs.targets[tex_tmp_name] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get(tex_tmp_id), GL_COLOR_ATTACHMENT0)
.setResizeFactors(bloom_in_scale * glm::pow(bloom_phase_scale, i), bloom_in_scale * glm::pow(bloom_phase_scale, i))
.setResize(true)
.finish();
assert(rs.targets[tex_tmp_name]);
}
{ // render tasks
auto& extraction = rs.addRenderTask<MM::OpenGL::RenderTasks::BloomExtraction>(engine);
extraction.src_tex = color_src_tex;
extraction.target_fbo = "bloom_extraction";
const glm::vec2 blur_factor {1.f, 1.f};
{ // blur rt
std::string prev_out_tex = "bloom_in";
for (size_t i = 1; i <= bloom_phases; i++) {
auto& blur = rs.addRenderTask<MM::OpenGL::RenderTasks::Blur>(engine);
// h
blur.in_tex = prev_out_tex;
blur.temp_fbo = "blur_tmp" + std::to_string(i);
// v
blur.temp_tex = "blur_tmp" + std::to_string(i);
blur.out_fbo = "blur_out" + std::to_string(i);
blur.out_tex = "blur_out" + std::to_string(i);
blur.tex_offset_factor = blur_factor * glm::vec2{2.f, 1.f}; // the input texture is double the size
// old blur:
//blur.tex_offset = {1.f/(w * bloom_in_scale * bloom_in_scale), 1.f/(h * bloom_in_scale * bloom_in_scale)};
prev_out_tex = blur.out_tex;
}
}
// combine passes
for (size_t i = bloom_phases; i > 1; i--) {
auto& combine = rs.addRenderTask<MM::OpenGL::RenderTasks::BloomCombine>(engine);
if (i == bloom_phases) {
combine.tex0 = "blur_out" + std::to_string(i);
} else {
combine.tex0 = "blur_tmp" + std::to_string(i);
}
combine.tex1 = "blur_out" + std::to_string(i-1);
combine.target_fbo = "blur_tmp" + std::to_string(i-1); // -> blur_tmpi-1
}
}
}
} // MM::OpenGL

View File

@ -0,0 +1,27 @@
#pragma once
#include <string>
#include "./render_task.hpp"
namespace MM::OpenGL {
// helper function to setup a bloom blur and recombine chain
// creates (texture) rendertarget, fbos and rendertasks
// outputs blur to "blur_tmp1" texture
//
// you still need to add the Composition rendertask (or eqv) your self, eg:
// auto& comp = rs.addRenderTask<MM::OpenGL::RenderTasks::Composition>(engine);
// comp.color_tex = "hdr_color";
// comp.bloom_tex = "blur_tmp1";
// comp.target_fbo = "display";
void setup_bloom(
MM::Engine& engine,
const std::string color_src_tex = "hdr_color", // texture to extract color from
const size_t bloom_phases = 5, // number of downsampled blurs (4 prob fine for 720, 5 for 1080)
const float bloom_in_scale = 0.5f, // scale of bloom extraction layer (1 - 0.5 best, lower for more perf)
const float bloom_phase_scale = 0.5f // ammount of scaling per downsampling
);
} // MM::OpenGL

View File

@ -51,20 +51,7 @@ void Camera3D::updateView(void) {
pitch = glm::clamp(pitch, -(glm::pi<float>()/2 - 0.00001f), glm::pi<float>()/2 - 0.00001f); pitch = glm::clamp(pitch, -(glm::pi<float>()/2 - 0.00001f), glm::pi<float>()/2 - 0.00001f);
yaw = glm::mod(yaw, 2*glm::pi<float>()); yaw = glm::mod(yaw, 2*glm::pi<float>());
glm::vec3 front {0,0,0}; glm::vec3 front = getViewDir();
{ // TODO: optimize
// if y up/down
front.x += up.y * glm::cos(pitch) * glm::cos(-yaw); // TODO: y is yaw inverted??
front.y += up.y * glm::sin(pitch);
front.z += up.y * glm::cos(pitch) * glm::sin(-yaw);
// if z up/down
front.x += up.z * glm::cos(pitch) * glm::cos(yaw);
front.y += up.z * glm::cos(pitch) * glm::sin(yaw);
front.z += up.z * glm::sin(pitch);
}
front = glm::normalize(front);
_view = glm::lookAt(translation, translation + front, up); _view = glm::lookAt(translation, translation + front, up);
} }
@ -82,6 +69,28 @@ glm::mat4 Camera3D::getProjection() const {
return _projection; return _projection;
} }
glm::vec3 Camera3D::getViewDir(void) const {
glm::vec3 front {0,0,0};
{ // TODO: optimize
if (up.y != 0.f) {
// if y up/down
front.x += up.y * glm::cos(pitch) * glm::cos(-yaw); // TODO: y is yaw inverted??
front.y += up.y * glm::sin(pitch);
front.z += up.y * glm::cos(pitch) * glm::sin(-yaw);
}
if (up.z != 0.f) {
// if z up/down
front.x += up.z * glm::cos(pitch) * glm::cos(yaw);
front.y += up.z * glm::cos(pitch) * glm::sin(yaw);
front.z += up.z * glm::sin(pitch);
}
}
front = glm::normalize(front);
return front;
}
std::array<glm::vec4, 6> Camera3D::getFrustumPlanes(void) const { std::array<glm::vec4, 6> Camera3D::getFrustumPlanes(void) const {
ZoneScopedN("Camera3D::getFrustumPlanes") ZoneScopedN("Camera3D::getFrustumPlanes")

View File

@ -47,6 +47,8 @@ namespace MM::OpenGL {
glm::mat4 getView(void) const; glm::mat4 getView(void) const;
glm::mat4 getProjection(void) const; glm::mat4 getProjection(void) const;
glm::vec3 getViewDir(void) const;
// call updateView beforehand, does not cache // call updateView beforehand, does not cache
std::array<glm::vec4, 6> getFrustumPlanes(void) const; std::array<glm::vec4, 6> getFrustumPlanes(void) const;

View File

@ -30,5 +30,5 @@ namespace MM::OpenGL {
//virtual void reload(void) {} // TODO: remove //virtual void reload(void) {} // TODO: remove
//virtual std::vector<const char*> getShaderPaths(void) {return {};} // TODO: remove //virtual std::vector<const char*> getShaderPaths(void) {return {};} // TODO: remove
}; };
} } // MM:OpenGL

View File

@ -0,0 +1,161 @@
#include "./bloom_combine.hpp"
#include <mm/fs_const_archiver.hpp>
#include <mm/services/opengl_renderer.hpp>
#include <mm/opengl/texture.hpp>
#include <tracy/Tracy.hpp>
namespace MM::OpenGL::RenderTasks {
BloomCombine::BloomCombine(Engine& engine) {
float vertices[] = {
-1.f, 1.f,
-1.f, -1.f,
1.f, -1.f,
1.f, -1.f,
1.f, 1.f,
-1.f, 1.f,
};
_vertexBuffer = std::make_unique<Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW);
_vao = std::make_unique<VertexArrayObject>();
_vao->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vao->unbind();
setupShaderFiles();
_shader = Shader::createF(engine, vertexPath, fragmentPath);
assert(_shader != nullptr);
}
BloomCombine::~BloomCombine(void) {
}
void BloomCombine::render(Services::OpenGLRenderer& rs, Engine& engine) {
ZoneScopedN("RenderTasks::BloomCombine::render");
rs.targets[target_fbo]->bind(FrameBufferObject::W);
{ // TODO: move to fbo
GLsizei width {0};
GLsizei height {0};
{ // get size of fb <.<
auto& fbo_tex_arr = rs.targets[target_fbo]->_texAttachments;
if (fbo_tex_arr.empty()) {
//GLint o_type {0};
//glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &o_type);
//if (o_type == GL_NONE) {
//// default framebuffer or error
//SPDLOG_INFO("gl none");
//}
// nah, just assume screen res
std::tie(width, height) = engine.getService<Services::SDLService>().getWindowSize();
} else {
auto& target_fbo_tex = rs.targets[target_fbo]->_texAttachments.front();
width = target_fbo_tex->width;
height = target_fbo_tex->height;
}
}
glViewport(0, 0, width, height);
}
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
_shader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto& rm = MM::ResourceManager<Texture>::ref();
auto tex_0 = rm.get(entt::hashed_string::value(tex0.c_str()));
tex_0->bind(0);
auto tex_1 = rm.get(entt::hashed_string::value(tex1.c_str()));
tex_1->bind(1);
// assign image units
_shader->setUniform1i("_tex0", 0);
_shader->setUniform1i("_tex1", 1);
glDrawArrays(GL_TRIANGLES, 0, 6);
_vao->unbind();
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_shader->unbind();
}
void BloomCombine::reloadShaders(Engine& engine) {
auto tmp_shader = Shader::createF(engine, vertexPath, fragmentPath);
if (tmp_shader) {
_shader = tmp_shader;
}
}
void BloomCombine::setupShaderFiles(void) {
FS_CONST_MOUNT_FILE(vertexPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
in vec2 _vertexPosition;
out vec2 _uv;
void main() {
gl_Position = vec4(_vertexPosition, 0, 1);
_uv = vec2(_vertexPosition.x * 0.5 + 0.5, _vertexPosition.y * 0.5 + 0.5);
})")
FS_CONST_MOUNT_FILE(fragmentPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
// tent sampling
#include "/shaders/builtin/sampling.glsl"
uniform sampler2D _tex0;
uniform sampler2D _tex1;
in vec2 _uv;
out vec3 _out_color;
vec3 tentSampling() {
// i hope the pipeline caches this
ivec2 tex0_size = textureSize(_tex0, 0); // TODO: lod
vec2 tex0_texel_step = vec2(1.0) / vec2(tex0_size);
return sampling2D_tent3x3_vec3(
_tex0,
_uv,
tex0_texel_step
);
}
vec3 simpleSampling() {
return texture(_tex0, _uv).rgb;
}
void main() {
#ifdef SIMPLE_SAMPLING
_out_color = texture(_tex1, _uv).rgb + simpleSampling();
#else
_out_color = texture(_tex1, _uv).rgb + tentSampling();
#endif
})")
}
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,40 @@
#pragma once
#include <mm/opengl/render_task.hpp>
#include <mm/opengl/shader.hpp>
#include <mm/opengl/buffer.hpp>
#include <mm/opengl/vertex_array_object.hpp>
namespace MM::OpenGL::RenderTasks {
class BloomCombine : public RenderTask {
private:
std::shared_ptr<Shader> _shader;
std::unique_ptr<Buffer> _vertexBuffer;
std::unique_ptr<VertexArrayObject> _vao;
public:
BloomCombine(Engine& engine);
~BloomCombine(void);
const char* name(void) override { return "BloomCombine"; }
void render(Services::OpenGLRenderer& rs, Engine& engine) override;
public:
const char* vertexPath {"shader/combine_render_task/vert.glsl"};
const char* fragmentPath {"shader/combine_render_task/frag.glsl"};
std::string target_fbo {"display"};
std::string tex0 {"tex0"}; // lower res
std::string tex1 {"tex1"};
void reloadShaders(Engine& engine);
private:
void setupShaderFiles(void);
};
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,114 @@
#include "./bloom_extraction.hpp"
#include <mm/services/opengl_renderer.hpp>
#include <mm/fs_const_archiver.hpp>
#include <mm/opengl/texture.hpp>
#include <tracy/Tracy.hpp>
namespace MM::OpenGL::RenderTasks {
BloomExtraction::BloomExtraction(Engine& engine) {
float vertices[] = {
-1.f, 1.f,
-1.f, -1.f,
1.f, -1.f,
1.f, -1.f,
1.f, 1.f,
-1.f, 1.f,
};
_vertexBuffer = std::make_unique<Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW);
_vao = std::make_unique<VertexArrayObject>();
_vao->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vao->unbind();
setupShaderFiles();
_shader = Shader::createF(engine, vertexPath, fragmentPath);
assert(_shader != nullptr);
}
BloomExtraction::~BloomExtraction(void) {
}
void BloomExtraction::render(Services::OpenGLRenderer& rs, Engine&) {
ZoneScopedN("RenderTasks::BloomExtraction::render");
auto& target_fbo_ = rs.targets[target_fbo];
target_fbo_->bind(FrameBufferObject::W);
auto& target_fbo_tex = target_fbo_->_texAttachments.front();
glViewport(0, 0, target_fbo_tex->width, target_fbo_tex->height);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
_shader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto& rm = MM::ResourceManager<Texture>::ref();
auto tex = rm.get(entt::hashed_string::value(src_tex.c_str()));
tex->bind(0);
_shader->setUniform1i("color_tex", 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
_vao->unbind();
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_shader->unbind();
}
void BloomExtraction::reloadShaders(Engine& engine) {
auto tmp_shader = Shader::createF(engine, vertexPath, fragmentPath);
if (tmp_shader) {
_shader = tmp_shader;
}
}
void BloomExtraction::setupShaderFiles(void) {
FS_CONST_MOUNT_FILE(vertexPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
in vec2 _vertexPosition;
out vec2 _uv;
void main() {
gl_Position = vec4(_vertexPosition, 0, 1);
_uv = vec2(_vertexPosition.x * 0.5 + 0.5, _vertexPosition.y * 0.5 + 0.5);
})")
FS_CONST_MOUNT_FILE(fragmentPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D color_tex;
in vec2 _uv;
out vec3 _out_color;
void main() {
vec3 color = texture(color_tex, _uv).rgb;
// TODO: expose threshold
_out_color = max(vec3(0.0), color - vec3(1.0));
})")
}
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,39 @@
#pragma once
#include <mm/opengl/render_task.hpp>
#include <mm/opengl/shader.hpp>
#include <mm/opengl/buffer.hpp>
#include <mm/opengl/vertex_array_object.hpp>
namespace MM::OpenGL::RenderTasks {
class BloomExtraction : public RenderTask {
private:
std::shared_ptr<Shader> _shader;
std::unique_ptr<Buffer> _vertexBuffer;
std::unique_ptr<VertexArrayObject> _vao;
public:
BloomExtraction(Engine& engine);
~BloomExtraction(void);
const char* name(void) override { return "BloomExtraction"; }
void render(Services::OpenGLRenderer& rs, Engine& engine) override;
public:
const char* vertexPath {"shader/bloom_extraction_render_task/vert.glsl"};
const char* fragmentPath {"shader/bloom_extraction_render_task/frag.glsl"};
std::string target_fbo {"display"};
std::string src_tex {"hdr_color"};
void reloadShaders(Engine& engine);
private:
void setupShaderFiles(void);
};
} // MM::OpenGL::RenderTasks

View File

@ -36,10 +36,8 @@ Blur::Blur(Engine& engine) {
_vao->unbind(); _vao->unbind();
setupShaderFiles(); setupShaderFiles();
_hShader = Shader::createF(engine, vertexPath, fragmentHPath); _shader = Shader::createF(engine, vertexPath, fragmentPath);
assert(_hShader != nullptr); assert(_shader != nullptr);
_vShader = Shader::createF(engine, vertexPath, fragmentVPath);
assert(_vShader != nullptr);
} }
Blur::~Blur(void) { Blur::~Blur(void) {
@ -48,47 +46,44 @@ Blur::~Blur(void) {
void Blur::render(Services::OpenGLRenderer& rs, Engine&) { void Blur::render(Services::OpenGLRenderer& rs, Engine&) {
ZoneScopedN("MM::OpenGL::RenderTasks::Blur::render"); ZoneScopedN("MM::OpenGL::RenderTasks::Blur::render");
rs.targets[io_fbo]->bind(FrameBufferObject::W); //rs.targets[io_fbo]->bind(FrameBufferObject::W);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND); // TODO: test glDisable(GL_BLEND);
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref(); auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto tex_io = rm_t.get(entt::hashed_string::value(io_tex.c_str())); // TODO: perf problems auto tex_in = rm_t.get(entt::hashed_string::value(in_tex.c_str())); // TODO: perf problems
auto tex_out = rm_t.get(entt::hashed_string::value(out_tex.c_str())); // TODO: perf problems
auto tex_temp = rm_t.get(entt::hashed_string::value(temp_tex.c_str())); // TODO: perf problems auto tex_temp = rm_t.get(entt::hashed_string::value(temp_tex.c_str())); // TODO: perf problems
_shader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
_shader->setUniform2f("tex_offset_factor", tex_offset_factor);
{ // horizontal { // horizontal
rs.targets[temp_fbo]->bind(FrameBufferObject::W); rs.targets[temp_fbo]->bind(FrameBufferObject::W);
_hShader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
glViewport(0, 0, tex_temp->width, tex_temp->height); glViewport(0, 0, tex_temp->width, tex_temp->height);
tex_io->bind(0); // read tex_in->bind(0); // read
_hShader->setUniform2f("tex_offset", tex_offset); _shader->setUniform1i("horizontal", 1);
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
} }
{ // vertical { // vertical
rs.targets[io_fbo]->bind(FrameBufferObject::W); rs.targets[out_fbo]->bind(FrameBufferObject::W);
_vShader->bind(); glViewport(0, 0, tex_out->width, tex_out->height);
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
glViewport(0, 0, tex_io->width, tex_io->height);
tex_temp->bind(0); // read tex_temp->bind(0); // read
_vShader->setUniform2f("tex_offset", tex_offset); _shader->setUniform1i("horizontal", 0);
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
} }
_vao->unbind(); _vao->unbind();
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vShader->unbind();
} }
void Blur::setupShaderFiles(void) { void Blur::setupShaderFiles(void) {
@ -107,9 +102,7 @@ void main() {
_tex_uv = _vertexPosition * 0.5 + 0.5; _tex_uv = _vertexPosition * 0.5 + 0.5;
})") })")
// TODO: deduplicate FS_CONST_MOUNT_FILE(fragmentPath,
FS_CONST_MOUNT_FILE(fragmentHPath,
GLSL_VERSION_STRING GLSL_VERSION_STRING
R"( R"(
#ifdef GL_ES #ifdef GL_ES
@ -119,54 +112,19 @@ R"(
uniform sampler2D tex0; uniform sampler2D tex0;
in vec2 _tex_uv; in vec2 _tex_uv;
//uniform bool horizontal; uniform bool horizontal;
const bool horizontal = true; //const bool horizontal = true;
uniform vec2 tex_offset; uniform vec2 tex_offset_factor;
const float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); const float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
out vec4 _out_color; out vec4 _out_color;
void main() { void main() {
//vec2 tex_offset = vec2(1.0) / vec2(textureSize(tex0, 0)); // gets size of single texel
vec3 result = texture(tex0, _tex_uv).rgb * weight[0]; // current fragment's contribution vec3 result = texture(tex0, _tex_uv).rgb * weight[0]; // current fragment's contribution
if (horizontal) { vec2 tex_offset = vec2(1.0) / vec2(textureSize(tex0, 0).xy); // gets size of single texel
for (int i = 1; i < 5; i++) { tex_offset *= tex_offset_factor;
result += texture(tex0, _tex_uv + vec2(tex_offset.x * float(i), 0.0)).rgb * weight[i];
result += texture(tex0, _tex_uv - vec2(tex_offset.x * float(i), 0.0)).rgb * weight[i];
}
} else {
for (int i = 1; i < 5; i++) {
result += texture(tex0, _tex_uv + vec2(0.0, tex_offset.y * float(i))).rgb * weight[i];
result += texture(tex0, _tex_uv - vec2(0.0, tex_offset.y * float(i))).rgb * weight[i];
}
}
_out_color = vec4(result, 1.0);
})")
FS_CONST_MOUNT_FILE(fragmentVPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D tex0;
in vec2 _tex_uv;
//uniform bool horizontal;
const bool horizontal = false;
uniform vec2 tex_offset;
const float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
out vec4 _out_color;
void main() {
//vec2 tex_offset = vec2(1.0) / vec2(textureSize(tex0, 0)); // gets size of single texel
vec3 result = texture(tex0, _tex_uv).rgb * weight[0]; // current fragment's contribution
if (horizontal) { if (horizontal) {
for (int i = 1; i < 5; i++) { for (int i = 1; i < 5; i++) {

View File

@ -17,8 +17,7 @@ namespace MM::OpenGL::RenderTasks {
// this task expects to read and write to textures // this task expects to read and write to textures
class Blur : public RenderTask { class Blur : public RenderTask {
private: private:
std::shared_ptr<Shader> _hShader; std::shared_ptr<Shader> _shader;
std::shared_ptr<Shader> _vShader;
std::unique_ptr<Buffer> _vertexBuffer; std::unique_ptr<Buffer> _vertexBuffer;
std::unique_ptr<VertexArrayObject> _vao; std::unique_ptr<VertexArrayObject> _vao;
@ -31,19 +30,19 @@ namespace MM::OpenGL::RenderTasks {
void render(Services::OpenGLRenderer& rs, Engine& engine) override; void render(Services::OpenGLRenderer& rs, Engine& engine) override;
public: public:
const char* vertexPath = "shader/blur_render_task/vert.glsl"; const char* vertexPath {"shader/blur_render_task/vert.glsl"};
const char* fragmentHPath = "shader/blur_render_task/frag_h.glsl"; const char* fragmentPath {"shader/blur_render_task/frag_h.glsl"};
const char* fragmentVPath = "shader/blur_render_task/frag_v.glsl";
std::string io_fbo = "blur_io"; std::string out_fbo {"blur_io"};
std::string temp_fbo = "blur_temp"; std::string temp_fbo {"blur_temp"};
// bc of it beeing a 2 pass, we need to flipflop // bc of it beeing a 2 pass, we need to flipflop
std::string io_tex = "blur_io"; std::string in_tex {"blur_io"};
std::string temp_tex = "blur_temp"; std::string out_tex {"blur_io"};
std::string temp_tex {"blur_temp"};
// determines the kernel lookup offset. "ideal" is 1/tex_size // kernel lookup offset factor
glm::vec2 tex_offset {0.001f, 0.001f}; glm::vec2 tex_offset_factor {1.f, 1.f};
private: private:
void setupShaderFiles(void); void setupShaderFiles(void);

View File

@ -0,0 +1,164 @@
#include "./composition.hpp"
#include <mm/fs_const_archiver.hpp>
#include <mm/services/opengl_renderer.hpp>
#include <mm/opengl/texture.hpp>
#include <tracy/Tracy.hpp>
namespace MM::OpenGL::RenderTasks {
Composition::Composition(MM::Engine& engine) {
float vertices[] = {
-1.f, 1.f,
-1.f, -1.f,
1.f, -1.f,
1.f, -1.f,
1.f, 1.f,
-1.f, 1.f,
};
_vertexBuffer = std::make_unique<MM::OpenGL::Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW);
_vao = std::make_unique<MM::OpenGL::VertexArrayObject>();
_vao->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vao->unbind();
setupShaderFiles();
_shader = MM::OpenGL::Shader::createF(engine, vertexPath, fragmentPath);
assert(_shader != nullptr);
}
Composition::~Composition(void) {
}
void Composition::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
ZoneScopedN("RenderTasks::Composition::render");
rs.targets[target_fbo]->bind(MM::OpenGL::FrameBufferObject::W);
{ // TODO: move to fbo
GLsizei width {0};
GLsizei height {0};
{ // get size of fb <.<
auto& fbo_tex_arr = rs.targets[target_fbo]->_texAttachments;
if (fbo_tex_arr.empty()) {
//GLint o_type {0};
//glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &o_type);
//if (o_type == GL_NONE) {
//// default framebuffer or error
//SPDLOG_INFO("gl none");
//}
// nah, just assume screen res
std::tie(width, height) = engine.getService<MM::Services::SDLService>().getWindowSize();
} else {
auto& target_fbo_tex = rs.targets[target_fbo]->_texAttachments.front();
width = target_fbo_tex->width;
height = target_fbo_tex->height;
}
}
glViewport(0, 0, width, height);
}
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
_shader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto& rm = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto tex_a = rm.get(entt::hashed_string::value(color_tex.c_str()));
tex_a->bind(0);
auto tex_n = rm.get(entt::hashed_string::value(bloom_tex.c_str()));
tex_n->bind(1);
// assign image units
_shader->setUniform1i("color_tex", 0);
_shader->setUniform1i("bloom_tex", 1);
_shader->setUniform1f("bloom_factor", bloom_factor);
glDrawArrays(GL_TRIANGLES, 0, 6);
_vao->unbind();
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_shader->unbind();
}
void Composition::reloadShaders(MM::Engine& engine) {
auto tmp_shader = MM::OpenGL::Shader::createF(engine, vertexPath, fragmentPath);
if (tmp_shader) {
_shader = tmp_shader;
}
}
void Composition::setupShaderFiles(void) {
FS_CONST_MOUNT_FILE(vertexPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
in vec2 _vertexPosition;
out vec2 _uv;
void main() {
gl_Position = vec4(_vertexPosition, 0, 1);
_uv = vec2(_vertexPosition.x * 0.5 + 0.5, _vertexPosition.y * 0.5 + 0.5);
})")
FS_CONST_MOUNT_FILE(fragmentPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
#include "/shaders/builtin/tonemapping.glsl"
uniform sampler2D color_tex;
uniform sampler2D bloom_tex;
// high bc 32bit on cpu
uniform highp float bloom_factor;
in vec2 _uv;
out vec3 _out_color;
void main() {
vec3 color = texture(color_tex, _uv).rgb;
vec3 bloom = texture(bloom_tex, _uv).rgb;
vec3 comp = color + bloom * vec3(bloom_factor);
#if 0
const vec3 tint = vec3(1.5, 0.8, 1.1);
comp *= tint;
#endif
//// reinhart + gamma
//_out_color = pow(comp / (comp + vec3(1.0)), vec3(1.0 / 2.2));
// makes more sense pre bloom
//comp *= vec3(pow(2.0, -1.0)); // pre expose, -1 fstops
//_out_color = tonemapReinhard(comp);
_out_color = tonemapACESFilm(comp); // looks right
//_out_color = pow(tonemapACESFilm(pow(comp, vec3(2.2))), vec3(1.0/2.2)); // insane saturation o.O
//_out_color = pow(tonemapACESFilm(comp), vec3(1.0/2.2)); // looks just wrong
})")
}
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,42 @@
#pragma once
#include <mm/opengl/render_task.hpp>
#include <mm/opengl/shader.hpp>
#include <mm/opengl/buffer.hpp>
#include <mm/opengl/vertex_array_object.hpp>
namespace MM::OpenGL::RenderTasks {
class Composition : public MM::OpenGL::RenderTask {
private:
std::shared_ptr<MM::OpenGL::Shader> _shader;
std::unique_ptr<MM::OpenGL::Buffer> _vertexBuffer;
std::unique_ptr<MM::OpenGL::VertexArrayObject> _vao;
public:
Composition(MM::Engine& engine);
~Composition(void);
const char* name(void) override { return "Composition"; }
void render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) override;
public:
const char* vertexPath {"shader/composition_render_task/vert.glsl"};
const char* fragmentPath {"shader/composition_render_task/frag.glsl"};
std::string target_fbo {"display"};
std::string color_tex {"hdr_color"};
std::string bloom_tex {"bloom"};
float bloom_factor {1.f};
void reloadShaders(MM::Engine& engine);
private:
void setupShaderFiles(void);
};
} // MM::OpenGL::RenderTasks

View File

@ -43,6 +43,12 @@ Tilemap::Tilemap(MM::Engine& engine) {
-0.5f, 0.5f, -0.5f, 0.5f,
}; };
// bias to avoid cracks
const float vert_offset_fact = 1.001f;
for (size_t i = 0; i < 2 * 6; i++) {
vertices[i] *= vert_offset_fact;
}
_vertexBuffer = std::make_unique<MM::OpenGL::Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW); _vertexBuffer = std::make_unique<MM::OpenGL::Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW);
_vao = std::make_unique<MM::OpenGL::VertexArrayObject>(); _vao = std::make_unique<MM::OpenGL::VertexArrayObject>();
_vao->bind(); _vao->bind();

View File

@ -0,0 +1,90 @@
#include "./shaders_builtin.hpp"
#include <mm/fs_const_archiver.hpp>
namespace MM::OpenGL {
void load_builtin_shaders_fs(void) {
// ==================== sampling.glsl ==================== START
FS_CONST_MOUNT_FILE("/shaders/builtin/sampling.glsl",
R"(
#ifndef INCLUDE_BUILTIN_SAMPLING
#define INCLUDE_BUILTIN_SAMPLING
// kernel defined 9tap sampling
// samper, uv, 1.0/textureSize(), kernel, sum of kernel
vec3 sampling2D_kernel3x3_vec3(in sampler2D tex, in vec2 pos, in vec2 dir_step, in mat3x3 kernel, in float kernel_weight) {
vec3 color =
// upper row
texture(tex, pos + dir_step * vec2(-1.0, 1.0)).rgb * kernel[0][0]
+ texture(tex, pos + dir_step * vec2(0.0, 1.0)).rgb * kernel[0][1]
+ texture(tex, pos + dir_step * vec2(1.0, 1.0)).rgb * kernel[0][2]
// middle row
+ texture(tex, pos + dir_step * vec2(-1.0, 0.0)).rgb * kernel[1][0]
+ texture(tex, pos).rgb * kernel[1][1]
+ texture(tex, pos + dir_step * vec2(1.0, 0.0)).rgb * kernel[1][2]
// lower row
+ texture(tex, pos + dir_step * vec2(-1.0, -1.0)).rgb * kernel[2][0]
+ texture(tex, pos + dir_step * vec2(0.0, -1.0)).rgb * kernel[2][1]
+ texture(tex, pos + dir_step * vec2(1.0, -1.0)).rgb * kernel[2][2]
;
return color / vec3(kernel_weight);
}
// 3x3 9tap tent filter
// 1 2 1
// 2 4 2
// 1 2 1
vec3 sampling2D_tent3x3_vec3(in sampler2D tex, in vec2 pos, in vec2 dir_step) {
const mat3x3 tent_kernel = mat3x3(
vec3(1.0, 2.0, 1.0),
vec3(2.0, 4.0, 2.0),
vec3(1.0, 2.0, 1.0)
);
const float tent_kernel_weight = 16.0;
return sampling2D_kernel3x3_vec3(
tex,
pos,
dir_step,
tent_kernel,
tent_kernel_weight
);
}
#endif
)")
// ==================== sampling.glsl ==================== END
// ==================== tonemapping.glsl ==================== START
FS_CONST_MOUNT_FILE("/shaders/builtin/tonemapping.glsl",
R"(
#ifndef INCLUDE_BUILTIN_TONEMAPPING
#define INCLUDE_BUILTIN_TONEMAPPING
// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
vec3 tonemapACESFilm(vec3 x) {
const float a = 2.51;
const float b = 0.03;
const float c = 2.43;
const float d = 0.59;
const float e = 0.14;
return clamp((x*(a*x+b))/(x*(c*x+d)+e), vec3(0.0), vec3(1.0));
}
vec3 tonemapReinhard(vec3 x) {
return x / (x + vec3(1.0));
}
#endif
)")
// ==================== tonemapping.glsl ==================== END
}
} // MM::OpenGL

View File

@ -0,0 +1,12 @@
#pragma once
namespace MM::OpenGL {
// loads glsl files into const fs at "/shaders/builtin/"
// file list:
// - sampling.glsl
// - tonemapping.glsl
void load_builtin_shaders_fs(void);
} // MM::OpenGL

View File

@ -10,6 +10,7 @@
#include <mm/opengl/texture_loader.hpp> #include <mm/opengl/texture_loader.hpp>
#include "../opengl/res/default_texture.h" // data #include "../opengl/res/default_texture.h" // data
#include "../opengl/res/errig_texture.h" // data #include "../opengl/res/errig_texture.h" // data
#include "../opengl/res/shaders_builtin.hpp" // data-ish
#include <tracy/Tracy.hpp> #include <tracy/Tracy.hpp>
#ifndef MM_OPENGL_3_GLES #ifndef MM_OPENGL_3_GLES
@ -111,7 +112,7 @@ bool OpenGLRenderer::enable(Engine& engine, std::vector<UpdateStrategies::TaskIn
return false; return false;
} }
{ // default texures { // default textures
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref(); auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
if (!rm_t.contains("default"_hs)) { if (!rm_t.contains("default"_hs)) {
if (!rm_t.load<MM::OpenGL::TextureLoaderConstBuffer>("default", default_png, default_png_len)) { if (!rm_t.load<MM::OpenGL::TextureLoaderConstBuffer>("default", default_png, default_png_len)) {
@ -125,6 +126,10 @@ bool OpenGLRenderer::enable(Engine& engine, std::vector<UpdateStrategies::TaskIn
} }
} }
{ // builtin shaders
OpenGL::load_builtin_shaders_fs();
}
{ // add task { // add task
task_array.push_back( task_array.push_back(
UpdateStrategies::TaskInfo{"OpenGLRenderer::render"} UpdateStrategies::TaskInfo{"OpenGLRenderer::render"}

View File

@ -9,6 +9,17 @@ add_test(NAME opengl_renderer_s_test COMMAND opengl_renderer_s_test)
################# #################
add_executable(opengl_builtins_test ./builtins.cpp)
target_link_libraries(opengl_builtins_test
opengl_renderer_s
gtest_main
)
add_test(NAME opengl_builtins_test COMMAND opengl_builtins_test)
#################
add_executable(imgui_render_task_test imgui_render_task_test.cpp) add_executable(imgui_render_task_test imgui_render_task_test.cpp)
target_link_libraries(imgui_render_task_test target_link_libraries(imgui_render_task_test
@ -149,3 +160,30 @@ target_link_libraries(fast_sky_render_task_test
add_test(NAME fast_sky_render_task_test COMMAND fast_sky_render_task_test) add_test(NAME fast_sky_render_task_test COMMAND fast_sky_render_task_test)
################# hdr bloom example
add_executable(hdr_bloom_pipeline_example ./hdr_bloom_pipeline_example.cpp)
target_link_libraries(hdr_bloom_pipeline_example
opengl_renderer_s
organizer_scene
clear_render_task
blit_fb_render_task
simple_rect_render_task
#bloom_extraction_render_task
#blur_render_task
#bloom_combine_render_task
bloom
composition_render_task
simple_velocity_system
transform_system
random
gtest_main
)
add_test(NAME hdr_bloom_pipeline_example COMMAND hdr_bloom_pipeline_example)

View File

@ -72,9 +72,10 @@ TEST(blur_render_task, it) {
bsrr_rend.target_fbo = "blur_io"; bsrr_rend.target_fbo = "blur_io";
MM::OpenGL::RenderTasks::Blur& blur_rend = rs.addRenderTask<MM::OpenGL::RenderTasks::Blur>(engine); MM::OpenGL::RenderTasks::Blur& blur_rend = rs.addRenderTask<MM::OpenGL::RenderTasks::Blur>(engine);
blur_rend.io_tex = "blur_io"; blur_rend.in_tex = "blur_io";
blur_rend.out_tex = "blur_io";
blur_rend.temp_tex = "blur_temp"; blur_rend.temp_tex = "blur_temp";
blur_rend.io_fbo = "blur_io"; blur_rend.out_fbo = "blur_io";
blur_rend.temp_fbo = "blur_temp"; blur_rend.temp_fbo = "blur_temp";
// render to display // render to display

View File

@ -0,0 +1,59 @@
#include <gtest/gtest.h>
#include <mm/engine.hpp>
#include <mm/services/filesystem.hpp>
#include <mm/services/sdl_service.hpp>
#include <mm/services/opengl_renderer.hpp>
//#include <mm/opengl/res/shaders_builtin.hpp>
#include <mm/opengl/shader.hpp>
#include <mm/opengl/shader_builder.hpp>
#include <mm/fs_const_archiver.hpp> // include only works on files rn
static const char* argv0 = "";
TEST(builtins, all) {
MM::Engine engine;
engine.addService<MM::Services::FilesystemService>(argv0);
ASSERT_TRUE(engine.enableService<MM::Services::FilesystemService>());
engine.addService<MM::Services::SDLService>();
ASSERT_TRUE(engine.enableService<MM::Services::SDLService>());
engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<MM::Services::OpenGLRenderer>()); // adds builtins
engine.update();
FS_CONST_MOUNT_FILE("/shaders/test_frag.glsl",
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
#include "/shaders/builtin/sampling.glsl"
#include "/shaders/builtin/tonemapping.glsl"
void main() {
}
)");
auto sb = MM::OpenGL::ShaderBuilder::start();
sb.addStageVertex("void main() { gl_Position = vec4(0.0); }");
sb.addStageFragmentF(engine, "/shaders/test_frag.glsl");
auto shader = sb.finish();
ASSERT_TRUE(shader);
}
int main(int argc, char** argv) {
argv0 = argv[0];
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,291 @@
#include <gtest/gtest.h>
#include <mm/engine.hpp>
#include <mm/services/filesystem.hpp>
#include <mm/services/sdl_service.hpp>
#include <mm/services/organizer_scene.hpp>
#include <mm/services/opengl_renderer.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/organizer.hpp>
#include <mm/opengl/render_tasks/clear.hpp>
#include <mm/opengl/render_tasks/blit_fb.hpp>
#include <mm/opengl/render_tasks/simple_rect.hpp>
//#include <mm/opengl/render_tasks/bloom_extraction.hpp>
//#include <mm/opengl/render_tasks/blur.hpp>
//#include <mm/opengl/render_tasks/bloom_combine.hpp>
#include <mm/opengl/bloom.hpp>
#include <mm/opengl/render_tasks/composition.hpp>
#include <mm/components/position2d.hpp>
#include <mm/components/position2d_zoffset.hpp>
#include <mm/components/scale2d.hpp>
#include <mm/components/rotation2d.hpp>
#include <mm/components/velocity2d_rotation.hpp>
#include <mm/components/position3d.hpp>
#include <mm/components/transform4x4.hpp>
#include <mm/components/color.hpp>
#include <mm/systems/simple_velocity_system2d.hpp>
#include <mm/systems/transform.hpp>
#include <mm/opengl/fbo_builder.hpp>
#include <mm/opengl/texture_loader.hpp>
#include <mm/random/srng.hpp>
using namespace entt::literals;
const char* argv0;
static void setup_textures(MM::Engine& engine) {
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto [w, h] = engine.getService<MM::Services::SDLService>().getWindowSize();
// we dont have a gbuffer in this example
{ // gbuffer
// depth
#ifdef MM_OPENGL_3_GLES
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"depth",
GL_DEPTH_COMPONENT24, // d16 is the onlyone for gles 2 (TODO: test for 3)
w, h,
GL_DEPTH_COMPONENT, GL_UNSIGNED_INT
);
#else
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"depth",
GL_DEPTH_COMPONENT32F, // TODO: stencil?
w, h,
GL_DEPTH_COMPONENT, GL_FLOAT
);
#endif
#if 0
// albedo
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"albedo",
#ifdef MM_OPENGL_3_GLES
GL_RGB565,
#else
GL_RGBA8, // waste of A
#endif
w, h,
GL_RGB, GL_UNSIGNED_BYTE
);
#endif
#if 0
// normal
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"normal",
// prolly fine, but need to remapp to -1,1
#ifdef MM_OPENGL_3_GLES
GL_RGB565,
#else
GL_RGBA8, // waste of A
#endif
w, h,
GL_RGB, GL_UNSIGNED_BYTE
);
#endif
}
const float render_scale = 1.f;
// hdr color gles3 / webgl2
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"hdr_color",
GL_RGBA16F,
w * render_scale, h * render_scale,
GL_RGBA,
GL_HALF_FLOAT
);
{ // filter
rm_t.get("hdr_color"_hs)->bind(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
static void setup_fbos(MM::Engine& engine) {
auto& rs = engine.getService<MM::Services::OpenGLRenderer>();
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
const float render_scale = 1.f;
#if 0
rs.targets["clear_opaque"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("albedo"_hs), GL_COLOR_ATTACHMENT0)
.attachTexture(rm_t.get("opaque_depth"_hs), GL_DEPTH_ATTACHMENT)
.setResize(true)
.finish();
assert(rs.targets["clear_opaque"]);
rs.targets["clear_opaque_normal"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("normal"_hs), GL_COLOR_ATTACHMENT0)
.setResize(true)
.finish();
assert(rs.targets["clear_opaque"]);
rs.targets["opaque"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("albedo"_hs), GL_COLOR_ATTACHMENT0)
.attachTexture(rm_t.get("normal"_hs), GL_COLOR_ATTACHMENT1)
.attachTexture(rm_t.get("opaque_depth"_hs), GL_DEPTH_ATTACHMENT)
.setResize(true)
.finish();
assert(rs.targets["opaque"]);
rs.targets["tmp_read"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("normal"_hs), GL_COLOR_ATTACHMENT0)
.setResize(false)
.finish();
assert(rs.targets["tmp_read"]);
rs.targets["depth_read"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("opaque_depth"_hs), GL_DEPTH_ATTACHMENT)
.setResize(false)
.finish();
assert(rs.targets["depth_read"]);
rs.targets["deferred_shading"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("hdr_color"_hs), GL_COLOR_ATTACHMENT0)
.setResize(true)
.finish();
assert(rs.targets["deferred_shading"]);
#endif
rs.targets["game_view"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("hdr_color"_hs), GL_COLOR_ATTACHMENT0)
.attachTexture(rm_t.get("depth"_hs), GL_DEPTH_ATTACHMENT)
.setResizeFactors(render_scale, render_scale)
.setResize(true)
.finish();
assert(rs.targets["game_view"]);
}
TEST(hdr_bloom_pipeline, it) {
MM::Engine engine;
auto& sdl_ss = engine.addService<MM::Services::SDLService>();
ASSERT_TRUE(engine.enableService<MM::Services::SDLService>());
sdl_ss.createGLWindow("hdr_bloom_pipeline_example", 1280, 720);
engine.addService<MM::Services::OrganizerSceneService>();
ASSERT_TRUE(engine.enableService<MM::Services::OrganizerSceneService>());
bool provide_ret = engine.provide<MM::Services::SceneServiceInterface, MM::Services::OrganizerSceneService>();
ASSERT_TRUE(provide_ret);
auto& scene = engine.tryService<MM::Services::SceneServiceInterface>()->getScene();
engine.addService<MM::Services::FilesystemService>(argv0, "hdr_bloom_pipeline_example");
ASSERT_TRUE(engine.enableService<MM::Services::FilesystemService>());
auto& rs = engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<MM::Services::OpenGLRenderer>());
{ // setup rendering
// TODO: split vertically
setup_textures(engine);
setup_fbos(engine);
// clear
auto& clear_opaque = rs.addRenderTask<MM::OpenGL::RenderTasks::Clear>(engine);
clear_opaque.target_fbo = "game_view";
// clears all color attachments
clear_opaque.r = 0.f;
clear_opaque.g = 0.f;
clear_opaque.b = 0.f;
clear_opaque.a = 1.f;
clear_opaque.mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
{ // render, whatever
MM::OpenGL::RenderTasks::SimpleRect& bsrr_rend = rs.addRenderTask<MM::OpenGL::RenderTasks::SimpleRect>(engine);
bsrr_rend.target_fbo = "game_view";
}
// rn does rt too
MM::OpenGL::setup_bloom(engine);
// not part of setup_bloom
auto& comp = rs.addRenderTask<MM::OpenGL::RenderTasks::Composition>(engine);
comp.color_tex = "hdr_color";
comp.bloom_tex = "blur_tmp1";
comp.target_fbo = "display";
}
scene.on_construct<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::Position2D_ZOffset>>();
scene.on_construct<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::Position3D>>();
scene.on_construct<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::Transform4x4>>();
scene.on_construct<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Position2D_ZOffset>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Position3D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Scale2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Rotation2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>(); // in this example only rotation is touched
// setup v system
auto& org = scene.set<entt::organizer>();
org.emplace<MM::Systems::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");
org.emplace<MM::Systems::transform3d_rotate2d>("transform3d_rotate2d");
org.emplace<MM::Systems::transform3d_scale2d>("transform3d_scale2d");
org.emplace<MM::Systems::transform_clear_dirty>("transform_clear_dirty");
// HACK: instead you would switch to this scene
engine.getService<MM::Services::OrganizerSceneService>().updateOrganizerVertices(scene);
MM::Random::SRNG rng{42};
for (int i = 0; i < 5; i++) {
auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = 0.f;
p.pos.y = 25.f - i * 10.f;
auto& s = scene.emplace<MM::Components::Scale2D>(e);
s.scale = {50.f, i*0.2f + 0.1f};
auto& col = scene.emplace<MM::Components::Color>(e);
col.color = {3.f, 3.f, 3.f, 1.f};
}
for (int i = 0; i < 10; i++) {
auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = i * 9.f - 40;
// zoffset is created by event
auto& s = scene.emplace<MM::Components::Scale2D>(e);
s.scale = {5,5};
scene.emplace<MM::Components::Rotation2D>(e);
auto& v = scene.emplace<MM::Components::Velocity2DRotation>(e);
v.rot_vel = i * 0.3f;
auto& col = scene.emplace<MM::Components::Color>(e);
col.color = {rng.zeroToOne()*2.f, rng.zeroToOne()*2.f, rng.zeroToOne()*2.f, 1.f};
}
engine.run();
}
int main(int argc, char** argv) {
argv0 = argv[0];
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,9 +1,30 @@
#pragma once #pragma once
#include <string_view> #include <string_view>
#include <cctype>
namespace MM::std_utils { namespace MM::std_utils {
inline std::string_view trim_prefix(std::string_view sv) {
while (!sv.empty() && std::isspace(sv.front())) {
sv.remove_prefix(1);
}
return sv;
}
inline std::string_view trim_suffix(std::string_view sv) {
while (!sv.empty() && std::isspace(sv.back())) {
sv.remove_suffix(1);
}
return sv;
}
inline std::string_view trim(std::string_view sv) {
return trim_suffix(trim_prefix(sv));
}
// src : https://marcoarena.wordpress.com/2017/01/03/string_view-odi-et-amo/ // src : https://marcoarena.wordpress.com/2017/01/03/string_view-odi-et-amo/
inline std::vector<std::string_view> split(std::string_view str, const char* delims) { inline std::vector<std::string_view> split(std::string_view str, const char* delims) {
std::vector<std::string_view> ret; std::vector<std::string_view> ret;