3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2017 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru>
5 Copyright (C) 2020 appgurueu, Lars Mueller <appgurulars@gmx.de>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "secondstage.h"
23 #include "client/client.h"
24 #include "client/shader.h"
25 #include "client/tile.h"
27 PostProcessingStep::PostProcessingStep(u32 _shader_id, const std::vector<u8> &_texture_map) :
28 shader_id(_shader_id), texture_map(_texture_map)
30 assert(texture_map.size() <= video::MATERIAL_MAX_TEXTURES);
34 void PostProcessingStep::configureMaterial()
36 material.UseMipMaps = false;
37 material.ZBuffer = true;
38 material.ZWriteEnable = video::EZW_ON;
39 for (u32 k = 0; k < texture_map.size(); ++k) {
40 material.TextureLayer[k].AnisotropicFilter = false;
41 material.TextureLayer[k].BilinearFilter = false;
42 material.TextureLayer[k].TrilinearFilter = false;
43 material.TextureLayer[k].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
44 material.TextureLayer[k].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
48 void PostProcessingStep::setRenderSource(RenderSource *_source)
53 void PostProcessingStep::setRenderTarget(RenderTarget *_target)
58 void PostProcessingStep::reset(PipelineContext &context)
62 void PostProcessingStep::run(PipelineContext &context)
65 target->activate(context);
68 material.MaterialType = context.client->getShaderSource()->getShaderInfo(shader_id).material;
70 auto driver = context.device->getVideoDriver();
72 for (u32 i = 0; i < texture_map.size(); i++)
73 material.TextureLayer[i].Texture = source->getTexture(texture_map[i]);
75 static const video::SColor color = video::SColor(0, 0, 0, 255);
76 static const video::S3DVertex vertices[4] = {
77 video::S3DVertex(1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
79 video::S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
81 video::S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
83 video::S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
86 static const u16 indices[6] = {0, 1, 2, 2, 3, 0};
87 driver->setMaterial(material);
88 driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2);
91 void PostProcessingStep::setBilinearFilter(u8 index, bool value)
93 assert(index < video::MATERIAL_MAX_TEXTURES);
94 material.TextureLayer[index].BilinearFilter = value;
97 RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client)
99 auto buffer = pipeline->createOwned<TextureBuffer>();
100 auto driver = client->getSceneManager()->getVideoDriver();
102 // configure texture formats
103 video::ECOLOR_FORMAT color_format = video::ECF_A8R8G8B8;
104 if (driver->queryTextureFormat(video::ECF_A16B16G16R16F))
105 color_format = video::ECF_A16B16G16R16F;
107 video::ECOLOR_FORMAT depth_format = video::ECF_D16; // fallback depth format
108 if (driver->queryTextureFormat(video::ECF_D32))
109 depth_format = video::ECF_D32;
110 else if (driver->queryTextureFormat(video::ECF_D24S8))
111 depth_format = video::ECF_D24S8;
114 // init post-processing buffer
115 static const u8 TEXTURE_COLOR = 0;
116 static const u8 TEXTURE_DEPTH = 1;
117 static const u8 TEXTURE_BLOOM = 2;
118 static const u8 TEXTURE_EXPOSURE_1 = 3;
119 static const u8 TEXTURE_EXPOSURE_2 = 4;
120 static const u8 TEXTURE_BLOOM_DOWN = 10;
121 static const u8 TEXTURE_BLOOM_UP = 20;
123 buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format);
124 buffer->setTexture(TEXTURE_EXPOSURE_1, core::dimension2du(1,1), "exposure_1", color_format, /*clear:*/ true);
125 buffer->setTexture(TEXTURE_EXPOSURE_2, core::dimension2du(1,1), "exposure_2", color_format, /*clear:*/ true);
126 buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);
128 // attach buffer to the previous step
129 previousStep->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, std::vector<u8> { TEXTURE_COLOR }, TEXTURE_DEPTH));
134 // Number of mipmap levels of the bloom downsampling texture
135 const u8 MIPMAP_LEVELS = 4;
137 const bool enable_bloom = g_settings->getBool("enable_bloom");
138 const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure");
140 // post-processing stage
142 u8 source = TEXTURE_COLOR;
144 // common downsampling step for bloom or autoexposure
145 if (enable_bloom || enable_auto_exposure) {
147 v2f downscale = scale * 0.5;
148 for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
149 buffer->setTexture(TEXTURE_BLOOM_DOWN + i, downscale, std::string("downsample") + std::to_string(i), color_format);
151 buffer->setTexture(TEXTURE_BLOOM_UP + i, downscale, std::string("upsample") + std::to_string(i), color_format);
156 buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", color_format);
159 u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH);
160 RenderStep *extract_bloom = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_EXPOSURE_1 });
161 extract_bloom->setRenderSource(buffer);
162 extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM));
163 source = TEXTURE_BLOOM;
167 shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH);
168 for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
169 auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source });
170 step->setRenderSource(buffer);
171 step->setBilinearFilter(0, true);
172 step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM_DOWN + i));
173 source = TEXTURE_BLOOM_DOWN + i;
179 shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH);
180 for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) {
181 auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { u8(TEXTURE_BLOOM_DOWN + i - 1), source });
182 step->setRenderSource(buffer);
183 step->setBilinearFilter(0, true);
184 step->setBilinearFilter(1, true);
185 step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, u8(TEXTURE_BLOOM_UP + i - 1)));
186 source = TEXTURE_BLOOM_UP + i - 1;
190 if (enable_auto_exposure) {
191 shader_id = client->getShaderSource()->getShader("update_exposure", TILE_MATERIAL_PLAIN, NDT_MESH);
192 auto update_exposure = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_EXPOSURE_1, u8(TEXTURE_BLOOM_DOWN + MIPMAP_LEVELS - 1) });
193 update_exposure->setBilinearFilter(1, true);
194 update_exposure->setRenderSource(buffer);
195 update_exposure->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_EXPOSURE_2));
198 // final post-processing
199 shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
200 PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_BLOOM_UP, TEXTURE_EXPOSURE_2 });
201 pipeline->addStep(effect);
202 effect->setBilinearFilter(1, true); // apply filter to the bloom
203 effect->setRenderSource(buffer);
205 if (enable_auto_exposure) {
206 pipeline->addStep<SwapTexturesStep>(buffer, TEXTURE_EXPOSURE_1, TEXTURE_EXPOSURE_2);