feat: post-processing effects
This commit is contained in:
parent
22e97b1766
commit
ba170035ef
@ -5,10 +5,12 @@
|
|||||||
"main",
|
"main",
|
||||||
"lines",
|
"lines",
|
||||||
"entity",
|
"entity",
|
||||||
"screen",
|
|
||||||
"background",
|
"background",
|
||||||
"skybox_gen"
|
"skybox_gen"
|
||||||
],
|
],
|
||||||
|
"post-effects": [
|
||||||
|
"default"
|
||||||
|
],
|
||||||
"textures": [
|
"textures": [
|
||||||
"gui/menubg",
|
"gui/menubg",
|
||||||
"gui/delete_icon",
|
"gui/delete_icon",
|
||||||
|
|||||||
13
res/shaders/effect.glslf
Normal file
13
res/shaders/effect.glslf
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
in vec2 v_uv;
|
||||||
|
out vec4 f_color;
|
||||||
|
|
||||||
|
uniform sampler2D u_screen;
|
||||||
|
uniform ivec2 u_screenSize;
|
||||||
|
uniform float u_intensity;
|
||||||
|
|
||||||
|
#include <__effect__>
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_color = effect();
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,13 +1,10 @@
|
|||||||
layout (location = 0) in vec2 v_position;
|
layout (location = 0) in vec2 v_position;
|
||||||
|
|
||||||
out vec2 v_coord;
|
out vec2 v_uv;
|
||||||
|
|
||||||
uniform ivec2 u_screenSize;
|
uniform ivec2 u_screenSize;
|
||||||
uniform float u_timer;
|
|
||||||
uniform float u_dayTime;
|
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
v_coord = v_position * 0.5 + 0.5;
|
v_uv = v_position * 0.5 + 0.5;
|
||||||
gl_Position = vec4(v_position, 0.0, 1.0);
|
gl_Position = vec4(v_position, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
3
res/shaders/effects/default.glsl
Normal file
3
res/shaders/effects/default.glsl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
vec4 effect() {
|
||||||
|
return texture(u_screen, v_uv);
|
||||||
|
}
|
||||||
@ -1,25 +0,0 @@
|
|||||||
in vec2 v_coord;
|
|
||||||
out vec4 f_color;
|
|
||||||
|
|
||||||
uniform sampler2D u_texture0;
|
|
||||||
uniform ivec2 u_screenSize;
|
|
||||||
|
|
||||||
// Vignette
|
|
||||||
vec4 apply_vignette(vec4 color) {
|
|
||||||
vec2 position = (gl_FragCoord.xy / u_screenSize) - vec2(0.5);
|
|
||||||
float dist = length(position);
|
|
||||||
|
|
||||||
float radius = 1.3;
|
|
||||||
float softness = 1.0;
|
|
||||||
float vignette = smoothstep(radius, radius - softness, dist);
|
|
||||||
|
|
||||||
color.rgb = color.rgb * vignette;
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
f_color = texture(u_texture0, v_coord);
|
|
||||||
f_color = apply_vignette(f_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -16,7 +16,16 @@
|
|||||||
|
|
||||||
class Assets;
|
class Assets;
|
||||||
|
|
||||||
enum class AssetType { TEXTURE, SHADER, FONT, ATLAS, LAYOUT, SOUND, MODEL };
|
enum class AssetType {
|
||||||
|
TEXTURE,
|
||||||
|
SHADER,
|
||||||
|
FONT,
|
||||||
|
ATLAS,
|
||||||
|
LAYOUT,
|
||||||
|
SOUND,
|
||||||
|
MODEL,
|
||||||
|
POST_EFFECT
|
||||||
|
};
|
||||||
|
|
||||||
namespace assetload {
|
namespace assetload {
|
||||||
/// @brief final work to do in the main thread
|
/// @brief final work to do in the main thread
|
||||||
@ -91,6 +100,20 @@ public:
|
|||||||
return static_cast<T*>(found->second.get());
|
return static_cast<T*>(found->second.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::shared_ptr<T> getShared(const std::string& name) const {
|
||||||
|
const auto& mapIter = assets.find(typeid(T));
|
||||||
|
if (mapIter == assets.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto& map = mapIter->second;
|
||||||
|
const auto& found = map.find(name);
|
||||||
|
if (found == map.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::static_pointer_cast<T>(found->second);
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T& require(const std::string& name) const {
|
T& require(const std::string& name) const {
|
||||||
T* asset = get<T>(name);
|
T* asset = get<T>(name);
|
||||||
|
|||||||
@ -34,6 +34,7 @@ AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths
|
|||||||
addLoader(AssetType::LAYOUT, assetload::layout);
|
addLoader(AssetType::LAYOUT, assetload::layout);
|
||||||
addLoader(AssetType::SOUND, assetload::sound);
|
addLoader(AssetType::SOUND, assetload::sound);
|
||||||
addLoader(AssetType::MODEL, assetload::model);
|
addLoader(AssetType::MODEL, assetload::model);
|
||||||
|
addLoader(AssetType::POST_EFFECT, assetload::posteffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
|
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
|
||||||
@ -131,6 +132,8 @@ static std::string assets_def_folder(AssetType tag) {
|
|||||||
return SOUNDS_FOLDER;
|
return SOUNDS_FOLDER;
|
||||||
case AssetType::MODEL:
|
case AssetType::MODEL:
|
||||||
return MODELS_FOLDER;
|
return MODELS_FOLDER;
|
||||||
|
case AssetType::POST_EFFECT:
|
||||||
|
return POST_EFFECTS_FOLDER;
|
||||||
}
|
}
|
||||||
return "<error>";
|
return "<error>";
|
||||||
}
|
}
|
||||||
@ -196,6 +199,7 @@ void AssetsLoader::processPreloadConfig(const io::path& file) {
|
|||||||
processPreloadList(AssetType::TEXTURE, root["textures"]);
|
processPreloadList(AssetType::TEXTURE, root["textures"]);
|
||||||
processPreloadList(AssetType::SOUND, root["sounds"]);
|
processPreloadList(AssetType::SOUND, root["sounds"]);
|
||||||
processPreloadList(AssetType::MODEL, root["models"]);
|
processPreloadList(AssetType::MODEL, root["models"]);
|
||||||
|
processPreloadList(AssetType::POST_EFFECT, root["post-effects"]);
|
||||||
// layouts are loaded automatically
|
// layouts are loaded automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -60,13 +60,7 @@ assetload::postfunc assetload::texture(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assetload::postfunc assetload::shader(
|
static auto process_program(const ResPaths& paths, const std::string& filename) {
|
||||||
AssetsLoader*,
|
|
||||||
const ResPaths& paths,
|
|
||||||
const std::string& filename,
|
|
||||||
const std::string& name,
|
|
||||||
const std::shared_ptr<AssetCfg>&
|
|
||||||
) {
|
|
||||||
io::path vertexFile = paths.find(filename + ".glslv");
|
io::path vertexFile = paths.find(filename + ".glslv");
|
||||||
io::path fragmentFile = paths.find(filename + ".glslf");
|
io::path fragmentFile = paths.find(filename + ".glslf");
|
||||||
|
|
||||||
@ -75,8 +69,25 @@ assetload::postfunc assetload::shader(
|
|||||||
|
|
||||||
auto& preprocessor = *Shader::preprocessor;
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
|
|
||||||
vertexSource = preprocessor.process(vertexFile, vertexSource).code;
|
auto vertex = preprocessor.process(vertexFile, vertexSource);
|
||||||
fragmentSource = preprocessor.process(fragmentFile, fragmentSource).code;
|
auto fragment = preprocessor.process(fragmentFile, fragmentSource);
|
||||||
|
return std::make_pair(vertex, fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
assetload::postfunc assetload::shader(
|
||||||
|
AssetsLoader*,
|
||||||
|
const ResPaths& paths,
|
||||||
|
const std::string& filename,
|
||||||
|
const std::string& name,
|
||||||
|
const std::shared_ptr<AssetCfg>&
|
||||||
|
) {
|
||||||
|
auto [vertex, fragment] = process_program(paths, filename);
|
||||||
|
|
||||||
|
io::path vertexFile = paths.find(filename + ".glslv");
|
||||||
|
io::path fragmentFile = paths.find(filename + ".glslf");
|
||||||
|
|
||||||
|
std::string vertexSource = std::move(vertex.code);
|
||||||
|
std::string fragmentSource = std::move(fragment.code);
|
||||||
|
|
||||||
return [=](auto assets) {
|
return [=](auto assets) {
|
||||||
assets->store(
|
assets->store(
|
||||||
@ -91,6 +102,40 @@ assetload::postfunc assetload::shader(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assetload::postfunc assetload::posteffect(
|
||||||
|
AssetsLoader*,
|
||||||
|
const ResPaths& paths,
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& name,
|
||||||
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
|
) {
|
||||||
|
io::path effectFile = paths.find(file + ".glsl");
|
||||||
|
std::string effectSource = io::read_string(effectFile);
|
||||||
|
|
||||||
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
|
preprocessor.addHeader(
|
||||||
|
"__effect__", preprocessor.process(effectFile, effectSource, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto [vertex, fragment] = process_program(paths, SHADERS_FOLDER + "/effect");
|
||||||
|
auto params = std::move(fragment.params);
|
||||||
|
|
||||||
|
std::string vertexSource = std::move(vertex.code);
|
||||||
|
std::string fragmentSource = std::move(fragment.code);
|
||||||
|
|
||||||
|
return [=](auto assets) {
|
||||||
|
auto program = Shader::create(
|
||||||
|
effectFile.string(),
|
||||||
|
effectFile.string(),
|
||||||
|
vertexSource,
|
||||||
|
fragmentSource
|
||||||
|
);
|
||||||
|
assets->store(
|
||||||
|
std::make_shared<PostEffect>(std::move(program), params), name
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
|
static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
|
||||||
std::string name = file.stem();
|
std::string name = file.stem();
|
||||||
// skip duplicates
|
// skip duplicates
|
||||||
|
|||||||
@ -62,4 +62,11 @@ namespace assetload {
|
|||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::shared_ptr<AssetCfg>& settings
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
);
|
);
|
||||||
|
postfunc posteffect(
|
||||||
|
AssetsLoader*,
|
||||||
|
const ResPaths& paths,
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& name,
|
||||||
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,21 +27,21 @@ void GLSLExtension::loadHeader(const std::string& name) {
|
|||||||
}
|
}
|
||||||
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
||||||
std::string source = io::read_string(file);
|
std::string source = io::read_string(file);
|
||||||
addHeader(name, "");
|
addHeader(name, {});
|
||||||
|
addHeader(name, process(file, source, true));
|
||||||
auto [code, _] = process(file, source, true);
|
|
||||||
addHeader(name, std::move(code));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSLExtension::addHeader(const std::string& name, std::string source) {
|
void GLSLExtension::addHeader(const std::string& name, ProcessingResult header) {
|
||||||
headers[name] = std::move(source);
|
headers[name] = std::move(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSLExtension::define(const std::string& name, std::string value) {
|
void GLSLExtension::define(const std::string& name, std::string value) {
|
||||||
defines[name] = std::move(value);
|
defines[name] = std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& GLSLExtension::getHeader(const std::string& name) const {
|
const GLSLExtension::ProcessingResult& GLSLExtension::getHeader(
|
||||||
|
const std::string& name
|
||||||
|
) const {
|
||||||
auto found = headers.find(name);
|
auto found = headers.find(name);
|
||||||
if (found == headers.end()) {
|
if (found == headers.end()) {
|
||||||
throw std::runtime_error("no header '" + name + "' loaded");
|
throw std::runtime_error("no header '" + name + "' loaded");
|
||||||
@ -161,7 +161,11 @@ public:
|
|||||||
if (!glsl.hasHeader(headerName)) {
|
if (!glsl.hasHeader(headerName)) {
|
||||||
glsl.loadHeader(headerName);
|
glsl.loadHeader(headerName);
|
||||||
}
|
}
|
||||||
ss << glsl.getHeader(headerName) << '\n';
|
const auto& header = glsl.getHeader(headerName);
|
||||||
|
for (const auto& [name, param] : header.params) {
|
||||||
|
params[name] = param;
|
||||||
|
}
|
||||||
|
ss << header.code << '\n';
|
||||||
source_line(ss, line);
|
source_line(ss, line);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,13 +11,20 @@ class ResPaths;
|
|||||||
|
|
||||||
class GLSLExtension {
|
class GLSLExtension {
|
||||||
public:
|
public:
|
||||||
|
using ParamsMap = std::unordered_map<std::string, PostEffect::Param>;
|
||||||
|
|
||||||
|
struct ProcessingResult {
|
||||||
|
std::string code;
|
||||||
|
ParamsMap params;
|
||||||
|
};
|
||||||
|
|
||||||
void setPaths(const ResPaths* paths);
|
void setPaths(const ResPaths* paths);
|
||||||
|
|
||||||
void define(const std::string& name, std::string value);
|
void define(const std::string& name, std::string value);
|
||||||
void undefine(const std::string& name);
|
void undefine(const std::string& name);
|
||||||
void addHeader(const std::string& name, std::string source);
|
void addHeader(const std::string& name, ProcessingResult header);
|
||||||
|
|
||||||
const std::string& getHeader(const std::string& name) const;
|
const ProcessingResult& getHeader(const std::string& name) const;
|
||||||
const std::string& getDefine(const std::string& name) const;
|
const std::string& getDefine(const std::string& name) const;
|
||||||
|
|
||||||
const std::unordered_map<std::string, std::string>& getDefines() const;
|
const std::unordered_map<std::string, std::string>& getDefines() const;
|
||||||
@ -25,12 +32,7 @@ public:
|
|||||||
bool hasHeader(const std::string& name) const;
|
bool hasHeader(const std::string& name) const;
|
||||||
bool hasDefine(const std::string& name) const;
|
bool hasDefine(const std::string& name) const;
|
||||||
void loadHeader(const std::string& name);
|
void loadHeader(const std::string& name);
|
||||||
|
|
||||||
struct ProcessingResult {
|
|
||||||
std::string code;
|
|
||||||
std::unordered_map<std::string, PostEffect::Param> params;
|
|
||||||
};
|
|
||||||
|
|
||||||
ProcessingResult process(
|
ProcessingResult process(
|
||||||
const io::path& file,
|
const io::path& file,
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
@ -39,7 +41,7 @@ public:
|
|||||||
|
|
||||||
static inline std::string VERSION = "330 core";
|
static inline std::string VERSION = "330 core";
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, std::string> headers;
|
std::unordered_map<std::string, ProcessingResult> headers;
|
||||||
std::unordered_map<std::string, std::string> defines;
|
std::unordered_map<std::string, std::string> defines;
|
||||||
|
|
||||||
const ResPaths* paths = nullptr;
|
const ResPaths* paths = nullptr;
|
||||||
|
|||||||
@ -68,5 +68,6 @@ inline const std::string LAYOUTS_FOLDER = "layouts";
|
|||||||
inline const std::string SOUNDS_FOLDER = "sounds";
|
inline const std::string SOUNDS_FOLDER = "sounds";
|
||||||
inline const std::string MODELS_FOLDER = "models";
|
inline const std::string MODELS_FOLDER = "models";
|
||||||
inline const std::string SKELETONS_FOLDER = "skeletons";
|
inline const std::string SKELETONS_FOLDER = "skeletons";
|
||||||
|
inline const std::string POST_EFFECTS_FOLDER = "shaders/effects";
|
||||||
|
|
||||||
inline const std::string FONT_DEFAULT = "normal";
|
inline const std::string FONT_DEFAULT = "normal";
|
||||||
|
|||||||
@ -8,10 +8,19 @@ PostEffect::Param::Param(Type type, Value defValue)
|
|||||||
: type(type), defValue(std::move(defValue)) {
|
: type(type), defValue(std::move(defValue)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PostEffect::PostEffect(std::unique_ptr<Shader> shader)
|
PostEffect::PostEffect(
|
||||||
: shader(std::move(shader)) {
|
std::unique_ptr<Shader> shader,
|
||||||
|
std::unordered_map<std::string, Param> params
|
||||||
|
)
|
||||||
|
: shader(std::move(shader)), params(std::move(params)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostEffect::use() {
|
Shader& PostEffect::use() {
|
||||||
shader->use();
|
shader->use();
|
||||||
|
shader->uniform1f("u_intensity", intensity);
|
||||||
|
return *shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostEffect::setIntensity(float value) {
|
||||||
|
intensity = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,10 +21,20 @@ public:
|
|||||||
Param(Type type, Value defValue);
|
Param(Type type, Value defValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
PostEffect(std::unique_ptr<Shader> shader);
|
PostEffect(
|
||||||
|
std::unique_ptr<Shader> shader,
|
||||||
|
std::unordered_map<std::string, Param> params
|
||||||
|
);
|
||||||
|
|
||||||
void use();
|
Shader& use();
|
||||||
|
|
||||||
|
void setIntensity(float value);
|
||||||
|
|
||||||
|
bool isActive() {
|
||||||
|
return intensity > 1e-4f;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Shader> shader;
|
std::unique_ptr<Shader> shader;
|
||||||
std::unordered_map<std::string, Param> params;
|
std::unordered_map<std::string, Param> params;
|
||||||
|
float intensity = 0.0f;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,13 +4,15 @@
|
|||||||
#include "Texture.hpp"
|
#include "Texture.hpp"
|
||||||
#include "Framebuffer.hpp"
|
#include "Framebuffer.hpp"
|
||||||
#include "DrawContext.hpp"
|
#include "DrawContext.hpp"
|
||||||
|
#include "PostEffect.hpp"
|
||||||
|
#include "assets/Assets.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
PostProcessing::PostProcessing() {
|
PostProcessing::PostProcessing() {
|
||||||
// Fullscreen quad mesh bulding
|
// Fullscreen quad mesh bulding
|
||||||
float vertices[] {
|
float vertices[] {
|
||||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||||
};
|
};
|
||||||
VertexAttribute attrs[] {{2}, {0}};
|
VertexAttribute attrs[] {{2}, {0}};
|
||||||
@ -23,22 +25,56 @@ void PostProcessing::use(DrawContext& context) {
|
|||||||
const auto& vp = context.getViewport();
|
const auto& vp = context.getViewport();
|
||||||
if (fbo) {
|
if (fbo) {
|
||||||
fbo->resize(vp.x, vp.y);
|
fbo->resize(vp.x, vp.y);
|
||||||
|
fboSecond->resize(vp.x, vp.y);
|
||||||
} else {
|
} else {
|
||||||
fbo = std::make_unique<Framebuffer>(vp.x, vp.y);
|
fbo = std::make_unique<Framebuffer>(vp.x, vp.y);
|
||||||
|
fboSecond = std::make_unique<Framebuffer>(vp.x, vp.y);
|
||||||
}
|
}
|
||||||
context.setFramebuffer(fbo.get());
|
context.setFramebuffer(fbo.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::render(const DrawContext& context, Shader* screenShader) {
|
void PostProcessing::render(
|
||||||
|
const DrawContext& context, const Assets& assets, float timer
|
||||||
|
) {
|
||||||
if (fbo == nullptr) {
|
if (fbo == nullptr) {
|
||||||
throw std::runtime_error("'use(...)' was never called");
|
throw std::runtime_error("'use(...)' was never called");
|
||||||
}
|
}
|
||||||
|
int totalPasses = 0;
|
||||||
|
for (const auto& effect : effectSlots) {
|
||||||
|
totalPasses += (effect != nullptr && effect->isActive());
|
||||||
|
}
|
||||||
|
|
||||||
const auto& viewport = context.getViewport();
|
if (totalPasses == 0) {
|
||||||
screenShader->use();
|
auto& effect = assets.require<PostEffect>("default");
|
||||||
screenShader->uniform2i("u_screenSize", viewport);
|
effect.use();
|
||||||
fbo->getTexture()->bind();
|
fbo->getTexture()->bind();
|
||||||
quadMesh->draw();
|
quadMesh->draw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentPass = 1;
|
||||||
|
for (const auto& effect : effectSlots) {
|
||||||
|
if (effect == nullptr || !effect->isActive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& shader = effect->use();
|
||||||
|
|
||||||
|
const auto& viewport = context.getViewport();
|
||||||
|
shader.uniform1i("u_screen", 0);
|
||||||
|
shader.uniform2i("u_screenSize", viewport);
|
||||||
|
shader.uniform1f("u_timer", timer);
|
||||||
|
|
||||||
|
fbo->getTexture()->bind();
|
||||||
|
if (currentPass < totalPasses) {
|
||||||
|
fboSecond->bind();
|
||||||
|
}
|
||||||
|
quadMesh->draw();
|
||||||
|
if (currentPass < totalPasses) {
|
||||||
|
fboSecond->unbind();
|
||||||
|
std::swap(fbo, fboSecond);
|
||||||
|
}
|
||||||
|
currentPass++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ImageData> PostProcessing::toImage() {
|
std::unique_ptr<ImageData> PostProcessing::toImage() {
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class Mesh;
|
class Mesh;
|
||||||
class Shader;
|
class Assets;
|
||||||
class Framebuffer;
|
class Framebuffer;
|
||||||
class DrawContext;
|
class DrawContext;
|
||||||
class ImageData;
|
class ImageData;
|
||||||
|
class PostEffect;
|
||||||
|
|
||||||
/// @brief Framebuffer with blitting with shaders.
|
/// @brief Framebuffer with blitting with shaders.
|
||||||
/// @attention Current implementation does not support multiple render passes
|
/// @attention Current implementation does not support multiple render passes
|
||||||
@ -14,8 +16,10 @@ class ImageData;
|
|||||||
class PostProcessing {
|
class PostProcessing {
|
||||||
/// @brief Main framebuffer (lasy field)
|
/// @brief Main framebuffer (lasy field)
|
||||||
std::unique_ptr<Framebuffer> fbo;
|
std::unique_ptr<Framebuffer> fbo;
|
||||||
|
std::unique_ptr<Framebuffer> fboSecond;
|
||||||
/// @brief Fullscreen quad mesh as the post-processing canvas
|
/// @brief Fullscreen quad mesh as the post-processing canvas
|
||||||
std::unique_ptr<Mesh> quadMesh;
|
std::unique_ptr<Mesh> quadMesh;
|
||||||
|
std::vector<std::shared_ptr<PostEffect>> effectSlots;
|
||||||
public:
|
public:
|
||||||
PostProcessing();
|
PostProcessing();
|
||||||
~PostProcessing();
|
~PostProcessing();
|
||||||
@ -27,9 +31,8 @@ public:
|
|||||||
/// @brief Render fullscreen quad using the passed shader
|
/// @brief Render fullscreen quad using the passed shader
|
||||||
/// with framebuffer texture bound
|
/// with framebuffer texture bound
|
||||||
/// @param context graphics context
|
/// @param context graphics context
|
||||||
/// @param screenShader shader used for fullscreen quad
|
|
||||||
/// @throws std::runtime_error if use(...) wasn't called before
|
/// @throws std::runtime_error if use(...) wasn't called before
|
||||||
void render(const DrawContext& context, Shader* screenShader);
|
void render(const DrawContext& context, const Assets& assets, float timer);
|
||||||
|
|
||||||
/// @brief Make an image from the last rendered frame
|
/// @brief Make an image from the last rendered frame
|
||||||
std::unique_ptr<ImageData> toImage();
|
std::unique_ptr<ImageData> toImage();
|
||||||
|
|||||||
@ -388,12 +388,7 @@ void WorldRenderer::draw(
|
|||||||
renderBlockOverlay(wctx);
|
renderBlockOverlay(wctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rendering fullscreen quad
|
postProcessing.render(pctx, assets, timer);
|
||||||
auto screenShader = assets.get<Shader>("screen");
|
|
||||||
screenShader->use();
|
|
||||||
screenShader->uniform1f("u_timer", timer);
|
|
||||||
screenShader->uniform1f("u_dayTime", worldInfo.daytime);
|
|
||||||
postProcessing.render(pctx, screenShader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
|
|
||||||
using scriptenv = std::shared_ptr<int>;
|
using scriptenv = std::shared_ptr<int>;
|
||||||
|
|
||||||
|
|||||||
@ -6,10 +6,13 @@
|
|||||||
TEST(GLSLExtension, processing) {
|
TEST(GLSLExtension, processing) {
|
||||||
GLSLExtension glsl;
|
GLSLExtension glsl;
|
||||||
glsl.addHeader("sum",
|
glsl.addHeader("sum",
|
||||||
"// sum function for glsl\n"
|
glsl.process("sum.glsl",
|
||||||
"float sum(float a, float b) {\n"
|
"// sum function for glsl\n"
|
||||||
" return a + b;\n"
|
"float sum(float a, float b) {\n"
|
||||||
"}\n"
|
" return a + b;\n"
|
||||||
|
"}\n",
|
||||||
|
true
|
||||||
|
)
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
auto processed = glsl.process("test.glsl",
|
auto processed = glsl.process("test.glsl",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user