feat: post-processing effects
This commit is contained in:
@@ -8,10 +8,19 @@ PostEffect::Param::Param(Type type, Value defValue)
|
||||
: type(type), defValue(std::move(defValue)) {
|
||||
}
|
||||
|
||||
PostEffect::PostEffect(std::unique_ptr<Shader> shader)
|
||||
: shader(std::move(shader)) {
|
||||
PostEffect::PostEffect(
|
||||
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->uniform1f("u_intensity", intensity);
|
||||
return *shader;
|
||||
}
|
||||
|
||||
void PostEffect::setIntensity(float value) {
|
||||
intensity = value;
|
||||
}
|
||||
|
||||
@@ -21,10 +21,20 @@ public:
|
||||
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:
|
||||
std::unique_ptr<Shader> shader;
|
||||
std::unordered_map<std::string, Param> params;
|
||||
float intensity = 0.0f;
|
||||
};
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
#include "Texture.hpp"
|
||||
#include "Framebuffer.hpp"
|
||||
#include "DrawContext.hpp"
|
||||
#include "PostEffect.hpp"
|
||||
#include "assets/Assets.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
PostProcessing::PostProcessing() {
|
||||
// Fullscreen quad mesh bulding
|
||||
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
|
||||
};
|
||||
VertexAttribute attrs[] {{2}, {0}};
|
||||
@@ -23,22 +25,56 @@ void PostProcessing::use(DrawContext& context) {
|
||||
const auto& vp = context.getViewport();
|
||||
if (fbo) {
|
||||
fbo->resize(vp.x, vp.y);
|
||||
fboSecond->resize(vp.x, vp.y);
|
||||
} else {
|
||||
fbo = std::make_unique<Framebuffer>(vp.x, vp.y);
|
||||
fboSecond = std::make_unique<Framebuffer>(vp.x, vp.y);
|
||||
}
|
||||
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) {
|
||||
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();
|
||||
screenShader->use();
|
||||
screenShader->uniform2i("u_screenSize", viewport);
|
||||
fbo->getTexture()->bind();
|
||||
quadMesh->draw();
|
||||
if (totalPasses == 0) {
|
||||
auto& effect = assets.require<PostEffect>("default");
|
||||
effect.use();
|
||||
fbo->getTexture()->bind();
|
||||
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() {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class Mesh;
|
||||
class Shader;
|
||||
class Assets;
|
||||
class Framebuffer;
|
||||
class DrawContext;
|
||||
class ImageData;
|
||||
class PostEffect;
|
||||
|
||||
/// @brief Framebuffer with blitting with shaders.
|
||||
/// @attention Current implementation does not support multiple render passes
|
||||
@@ -14,8 +16,10 @@ class ImageData;
|
||||
class PostProcessing {
|
||||
/// @brief Main framebuffer (lasy field)
|
||||
std::unique_ptr<Framebuffer> fbo;
|
||||
std::unique_ptr<Framebuffer> fboSecond;
|
||||
/// @brief Fullscreen quad mesh as the post-processing canvas
|
||||
std::unique_ptr<Mesh> quadMesh;
|
||||
std::vector<std::shared_ptr<PostEffect>> effectSlots;
|
||||
public:
|
||||
PostProcessing();
|
||||
~PostProcessing();
|
||||
@@ -27,9 +31,8 @@ public:
|
||||
/// @brief Render fullscreen quad using the passed shader
|
||||
/// with framebuffer texture bound
|
||||
/// @param context graphics context
|
||||
/// @param screenShader shader used for fullscreen quad
|
||||
/// @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
|
||||
std::unique_ptr<ImageData> toImage();
|
||||
|
||||
@@ -388,12 +388,7 @@ void WorldRenderer::draw(
|
||||
renderBlockOverlay(wctx);
|
||||
}
|
||||
|
||||
// Rendering fullscreen quad
|
||||
auto screenShader = assets.get<Shader>("screen");
|
||||
screenShader->use();
|
||||
screenShader->uniform1f("u_timer", timer);
|
||||
screenShader->uniform1f("u_dayTime", worldInfo.daytime);
|
||||
postProcessing.render(pctx, screenShader);
|
||||
postProcessing.render(pctx, assets, timer);
|
||||
}
|
||||
|
||||
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||
|
||||
Reference in New Issue
Block a user