101 changes: 41 additions & 60 deletions src/client/render/pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,

TextureBuffer::~TextureBuffer()
{
if (m_render_target)
m_driver->removeRenderTarget(m_render_target);
m_render_target = nullptr;
for (u32 index = 0; index < m_textures.size(); index++)
m_driver->removeTexture(m_textures[index]);
m_textures.clear();
}

video::ITexture *TextureBuffer::getTexture(u8 index)
{
if (index == m_depth_texture_index)
return m_depth_texture;
if (index >= m_textures.size())
return nullptr;
return m_textures[index];
Expand All @@ -52,9 +47,6 @@ void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::str
if (m_definitions.size() <= index)
m_definitions.resize(index + 1);

if (m_depth_texture_index == index)
m_depth_texture_index = NO_DEPTH_TEXTURE;

auto &definition = m_definitions[index];
definition.valid = true;
definition.dirty = true;
Expand All @@ -71,9 +63,6 @@ void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &na
if (m_definitions.size() <= index)
m_definitions.resize(index + 1);

if (m_depth_texture_index == index)
m_depth_texture_index = NO_DEPTH_TEXTURE;

auto &definition = m_definitions[index];
definition.valid = true;
definition.dirty = true;
Expand All @@ -83,20 +72,6 @@ void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &na
definition.format = format;
}

void TextureBuffer::setDepthTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format)
{
assert(index != NO_DEPTH_TEXTURE);
setTexture(index, size, name, format);
m_depth_texture_index = index;
}

void TextureBuffer::setDepthTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format)
{
assert(index != NO_DEPTH_TEXTURE);
setTexture(index, scale_factor, name, format);
m_depth_texture_index = index;
}

void TextureBuffer::reset(PipelineContext &context)
{
if (!m_driver)
Expand All @@ -116,41 +91,14 @@ void TextureBuffer::reset(PipelineContext &context)
m_textures.push_back(nullptr);

// change textures to match definitions
bool modified = false;
for (u32 i = 0; i < m_definitions.size(); i++) {
video::ITexture **ptr = &m_textures[i];
if (i == m_depth_texture_index) {
if (*ptr) {
m_driver->removeTexture(*ptr);
*ptr = nullptr;
}
ptr = &m_depth_texture;
}

if (ensureTexture(ptr, m_definitions[i], context))
modified = true;

ensureTexture(ptr, m_definitions[i], context);
m_definitions[i].dirty = false;
}

// make sude depth texture is removed and reset
if (m_depth_texture_index == NO_DEPTH_TEXTURE && m_depth_texture) {
m_driver->removeTexture(m_depth_texture);
m_depth_texture = nullptr;
}

if (!m_render_target)
m_render_target = m_driver->addRenderTarget();

if (modified)
m_render_target->setTexture(m_textures, m_depth_texture);

RenderTarget::reset(context);
}

void TextureBuffer::activate(PipelineContext &context)
{
m_driver->setRenderTargetEx(m_render_target, m_clear ? video::ECBF_DEPTH | video::ECBF_COLOR : 0, context.clear_color);
RenderTarget::activate(context);
RenderSource::reset(context);
}

bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context)
Expand Down Expand Up @@ -186,15 +134,48 @@ bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefini
}

TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, u8 _texture_index)
: buffer(_buffer), texture_index(_texture_index)
: buffer(_buffer), texture_map({_texture_index})
{}

TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector<u8> &_texture_map)
: buffer(_buffer), texture_map(_texture_map)
{}

TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector<u8> &_texture_map, u8 _depth_stencil)
: buffer(_buffer), texture_map(_texture_map), depth_stencil(_depth_stencil)
{}

TextureBufferOutput::~TextureBufferOutput()
{
if (render_target && driver)
driver->removeRenderTarget(render_target);
}

void TextureBufferOutput::activate(PipelineContext &context)
{
auto texture = buffer->getTexture(texture_index);
auto driver = context.device->getVideoDriver();
driver->setRenderTarget(texture, m_clear, m_clear, context.clear_color);
driver->OnResize(texture->getSize());
if (!driver)
driver = context.device->getVideoDriver();

if (!render_target)
render_target = driver->addRenderTarget();

core::array<video::ITexture *> textures;
core::dimension2du size(0, 0);
for (size_t i = 0; i < texture_map.size(); i++) {
video::ITexture *texture = buffer->getTexture(texture_map[i]);
textures.push_back(texture);
if (texture && size.Width == 0)
size = texture->getSize();
}

video::ITexture *depth_texture = nullptr;
if (depth_stencil != NO_DEPTH_TEXTURE)
depth_texture = buffer->getTexture(depth_stencil);

render_target->setTexture(textures, depth_texture);

driver->setRenderTargetEx(render_target, m_clear ? video::ECBF_ALL : video::ECBF_NONE, context.clear_color);
driver->OnResize(size);

RenderTarget::activate(context);
}
Expand Down
36 changes: 10 additions & 26 deletions src/client/render/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class RenderTarget : virtual public RenderPipelineObject
*
* @note Use of TextureBuffer requires use of gl_FragData[] in the shader
*/
class TextureBuffer : public RenderSource, public RenderTarget
class TextureBuffer : public RenderSource
{
public:
virtual ~TextureBuffer() override;
Expand All @@ -138,29 +138,8 @@ class TextureBuffer : public RenderSource, public RenderTarget
*/
void setTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format);

/**
* @Configure depth texture and assign index
*
* @param index index to use for the depth texture
* @param size width and height of the texture in pixels
* @param name unique name for the texture
* @param format color format
*/
void setDepthTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format);

/**
* @Configure depth texture and assign index
*
* @param index index to use for the depth texture
* @param scale_factor relation of the texture dimensions to the screen dimensions
* @param name unique name for the texture
* @param format color format
*/
void setDepthTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format);

virtual u8 getTextureCount() override { return m_textures.size(); }
virtual video::ITexture *getTexture(u8 index) override;
virtual void activate(PipelineContext &context) override;
virtual void reset(PipelineContext &context) override;
private:
static const u8 NO_DEPTH_TEXTURE = 255;
Expand Down Expand Up @@ -189,9 +168,6 @@ class TextureBuffer : public RenderSource, public RenderTarget
video::IVideoDriver *m_driver { nullptr };
std::vector<TextureDefinition> m_definitions;
core::array<video::ITexture *> m_textures;
video::ITexture *m_depth_texture { nullptr };
u8 m_depth_texture_index { NO_DEPTH_TEXTURE };
video::IRenderTarget *m_render_target { nullptr };
};

/**
Expand All @@ -201,10 +177,18 @@ class TextureBufferOutput : public RenderTarget
{
public:
TextureBufferOutput(TextureBuffer *buffer, u8 texture_index);
TextureBufferOutput(TextureBuffer *buffer, const std::vector<u8> &texture_map);
TextureBufferOutput(TextureBuffer *buffer, const std::vector<u8> &texture_map, u8 depth_stencil);
virtual ~TextureBufferOutput() override;
void activate(PipelineContext &context) override;
private:
static const u8 NO_DEPTH_TEXTURE = 255;

TextureBuffer *buffer;
u8 texture_index;
std::vector<u8> texture_map;
u8 depth_stencil { NO_DEPTH_TEXTURE };
video::IRenderTarget* render_target { nullptr };
video::IVideoDriver* driver { nullptr };
};

/**
Expand Down
66 changes: 56 additions & 10 deletions src/client/render/secondstage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,31 +88,77 @@ void PostProcessingStep::run(PipelineContext &context)
driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2);
}

void PostProcessingStep::setBilinearFilter(u8 index, bool value)
{
assert(index < video::MATERIAL_MAX_TEXTURES);
material.TextureLayer[index].BilinearFilter = value;
}

RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client)
{
auto buffer = pipeline->createOwned<TextureBuffer>();
static const u8 TEXTURE_COLOR = 0;
static const u8 TEXTURE_DEPTH = 3;
auto driver = client->getSceneManager()->getVideoDriver();

// init post-processing buffer
buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", video::ECF_A8R8G8B8);
// configure texture formats
video::ECOLOR_FORMAT color_format = video::ECF_A8R8G8B8;
if (driver->queryTextureFormat(video::ECF_A16B16G16R16F))
color_format = video::ECF_A16B16G16R16F;

video::ECOLOR_FORMAT depth_format = video::ECF_D16; // fallback depth format
auto driver = client->getSceneManager()->getVideoDriver();
if (driver->queryTextureFormat(video::ECF_D32))
depth_format = video::ECF_D32;
else if (driver->queryTextureFormat(video::ECF_D24S8))
depth_format = video::ECF_D24S8;
buffer->setDepthTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);


// init post-processing buffer
static const u8 TEXTURE_COLOR = 0;
static const u8 TEXTURE_DEPTH = 1;
static const u8 TEXTURE_BLOOM = 2;
static const u8 TEXTURE_BLUR = 3;
static const u8 TEXTURE_BLUR_SECONDARY = 4;

buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format);
buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);

// attach buffer to the previous step
previousStep->setRenderTarget(buffer);
previousStep->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, std::vector<u8> { TEXTURE_COLOR }, TEXTURE_DEPTH));

// post-processing stage
// set up shader
u32 shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
// set up bloom
if (g_settings->getBool("enable_bloom")) {

buffer->setTexture(TEXTURE_BLUR, scale * 0.5, "blur", color_format);
buffer->setTexture(TEXTURE_BLOOM, scale * 0.5, "bloom", color_format);
u8 bloom_input_texture = TEXTURE_BLOOM;

if (g_settings->getBool("enable_bloom_dedicated_texture")) {
buffer->setTexture(TEXTURE_BLUR_SECONDARY, scale * 0.5, "blur2", color_format);
bloom_input_texture = TEXTURE_BLUR_SECONDARY;
}

// get bright spots
u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH);
RenderStep *extract_bloom = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR });
extract_bloom->setRenderSource(buffer);
extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, bloom_input_texture));
// horizontal blur
shader_id = client->getShaderSource()->getShader("blur_h", TILE_MATERIAL_PLAIN, NDT_MESH);
RenderStep *blur_h = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { bloom_input_texture });
blur_h->setRenderSource(buffer);
blur_h->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLUR));
// vertical blur
shader_id = client->getShaderSource()->getShader("blur_v", TILE_MATERIAL_PLAIN, NDT_MESH);
RenderStep *blur_v = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_BLUR });
blur_v->setRenderSource(buffer);
blur_v->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM));
}

RenderStep *effect = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR });
// final post-processing
u32 shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_BLOOM });
pipeline->addStep(effect);
effect->setBilinearFilter(1, true); // apply filter to the bloom
effect->setRenderSource(buffer);
return effect;
}
18 changes: 17 additions & 1 deletion src/client/render/secondstage.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,33 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "stereo.h"
#include "pipeline.h"

/**
* Step to apply post-processing filter to the rendered image
*/
class PostProcessingStep : public RenderStep
{
public:
/**
* Construct a new PostProcessingStep object
*
* @param shader_id ID of the shader in IShaderSource
* @param texture_map Map of textures to be chosen from the render source
*/
PostProcessingStep(u32 shader_id, const std::vector<u8> &texture_map);


void setRenderSource(RenderSource *source) override;
void setRenderTarget(RenderTarget *target) override;
void reset(PipelineContext &context) override;
void run(PipelineContext &context) override;

/**
* Configure bilinear filtering for a specific texture layer
*
* @param index Index of the texture layer
* @param value true to enable the bilinear filter, false to disable
*/
void setBilinearFilter(u8 index, bool value);
private:
u32 shader_id;
std::vector<u8> texture_map;
Expand Down
4 changes: 4 additions & 0 deletions src/client/renderingengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ class RenderingCore;
class RenderingEngine
{
public:
/// Default color factor before applying effects like bloom or tomemapping
/// this is derived from tonemapping code and tuned empirically
static constexpr float DEFAULT_EXPOSURE_FACTOR = 2.5f;

RenderingEngine(IEventReceiver *eventReceiver);
~RenderingEngine();

Expand Down
13 changes: 13 additions & 0 deletions src/client/shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,13 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
)";
}

// map legacy semantic texture names to texture identifiers
fragment_header += R"(
#define baseTexture texture0
#define normalTexture texture1
#define textureFlags texture2
)";

// Since this is the first time we're using the GL bindings be extra careful.
// This should be removed before 5.6.0 or similar.
if (!GL.GetString) {
Expand Down Expand Up @@ -771,6 +778,12 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n";
}

if (g_settings->getBool("enable_bloom")) {
shaders_header << "#define ENABLE_BLOOM 1\n";
if (g_settings->getBool("enable_bloom_debug"))
shaders_header << "#define ENABLE_BLOOM_DEBUG 1\n";
}

shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics

std::string common_header = shaders_header.str();
Expand Down
6 changes: 6 additions & 0 deletions src/defaultsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ void set_default_settings()
settings->setDefault("water_wave_speed", "5.0");
settings->setDefault("enable_waving_leaves", "false");
settings->setDefault("enable_waving_plants", "false");
settings->setDefault("exposure_factor", "1.0");
settings->setDefault("enable_bloom", "false");
settings->setDefault("enable_bloom_debug", "false");
settings->setDefault("enable_bloom_dedicated_texture", "false");
settings->setDefault("bloom_intensity", "0.05");
settings->setDefault("bloom_radius", "16");

// Effects Shadows
settings->setDefault("enable_dynamic_shadows", "false");
Expand Down