]> git.lizzy.rs Git - minetest.git/blob - src/client/render/secondstage.cpp
Improve bloom effect (#12916)
[minetest.git] / src / client / render / secondstage.cpp
1 /*
2 Minetest
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>
6
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.
11
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.
16
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.
20 */
21
22 #include "secondstage.h"
23 #include "client/client.h"
24 #include "client/shader.h"
25 #include "client/tile.h"
26
27 PostProcessingStep::PostProcessingStep(u32 _shader_id, const std::vector<u8> &_texture_map) :
28         shader_id(_shader_id), texture_map(_texture_map)
29 {
30         assert(texture_map.size() <= video::MATERIAL_MAX_TEXTURES);
31         configureMaterial();
32 }
33
34 void PostProcessingStep::configureMaterial()
35 {
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;
45         }
46 }
47
48 void PostProcessingStep::setRenderSource(RenderSource *_source)
49 {
50         source = _source;
51 }
52
53 void PostProcessingStep::setRenderTarget(RenderTarget *_target)
54 {
55         target = _target;
56 }
57
58 void PostProcessingStep::reset(PipelineContext &context)
59 {
60 }
61
62 void PostProcessingStep::run(PipelineContext &context)
63 {
64         if (target)
65                 target->activate(context);
66
67         // attach the shader
68         material.MaterialType = context.client->getShaderSource()->getShaderInfo(shader_id).material;
69
70         auto driver = context.device->getVideoDriver();
71
72         for (u32 i = 0; i < texture_map.size(); i++)
73                 material.TextureLayer[i].Texture = source->getTexture(texture_map[i]);
74
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,
78                                         color, 1.0, 0.0),
79                         video::S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
80                                         color, 0.0, 0.0),
81                         video::S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
82                                         color, 0.0, 1.0),
83                         video::S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
84                                         color, 1.0, 1.0),
85         };
86         static const u16 indices[6] = {0, 1, 2, 2, 3, 0};
87         driver->setMaterial(material);
88         driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2);
89 }
90
91 void PostProcessingStep::setBilinearFilter(u8 index, bool value)
92 {
93         assert(index < video::MATERIAL_MAX_TEXTURES);
94         material.TextureLayer[index].BilinearFilter = value;
95 }
96
97 RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client)
98 {
99         auto buffer = pipeline->createOwned<TextureBuffer>();
100         auto driver = client->getSceneManager()->getVideoDriver();
101
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;
106
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;
112
113
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_BLOOM_DOWN = 10;
119         static const u8 TEXTURE_BLOOM_UP = 20;
120
121         buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format);
122         buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);
123
124         // attach buffer to the previous step
125         previousStep->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, std::vector<u8> { TEXTURE_COLOR }, TEXTURE_DEPTH));
126
127         // shared variables
128         u32 shader_id;
129
130         // post-processing stage
131         // set up bloom
132         if (g_settings->getBool("enable_bloom")) {
133
134
135                 buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", color_format);
136
137                 const u8 MIPMAP_LEVELS = 4;
138                 v2f downscale = scale * 0.5;
139                 for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
140                         buffer->setTexture(TEXTURE_BLOOM_DOWN + i, downscale, std::string("bloom_down") + std::to_string(i), color_format);
141                         buffer->setTexture(TEXTURE_BLOOM_UP + i, downscale, std::string("bloom_up") + std::to_string(i), color_format);
142                         downscale *= 0.5;
143                 }
144
145                 // get bright spots
146                 u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH);
147                 RenderStep *extract_bloom = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR });
148                 extract_bloom->setRenderSource(buffer);
149                 extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM));
150
151                 // downsample
152                 shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH);
153                 u8 source = TEXTURE_BLOOM;
154                 for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
155                         auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source });
156                         step->setRenderSource(buffer);
157                         step->setBilinearFilter(0, true);
158                         step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM_DOWN + i));
159                         source = TEXTURE_BLOOM_DOWN + i;
160                 }
161
162                 // upsample
163                 shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH);
164                 for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) {
165                         auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { u8(TEXTURE_BLOOM_DOWN + i - 1), source });
166                         step->setRenderSource(buffer);
167                         step->setBilinearFilter(0, true);
168                         step->setBilinearFilter(1, true);
169                         step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, u8(TEXTURE_BLOOM_UP + i - 1)));
170                         source = TEXTURE_BLOOM_UP + i - 1;
171                 }
172         }
173
174         // final post-processing
175         shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
176         PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_BLOOM_UP });
177         pipeline->addStep(effect);
178         effect->setBilinearFilter(1, true); // apply filter to the bloom
179         effect->setRenderSource(buffer);
180         return effect;
181 }