• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Razakhel / RaZ / 23265498158

18 Mar 2026 05:29PM UTC coverage: 74.491% (-0.01%) from 74.502%
23265498158

push

github

Razakhel
[Render/Cubemap] Made the cubemap's vertices meshless

- They are now statically fetched according to the vertex ID

15 of 23 new or added lines in 6 files covered. (65.22%)

69 existing lines in 2 files now uncovered.

8632 of 11588 relevant lines covered (74.49%)

1706.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.16
/src/RaZ/Render/BloomRenderProcess.cpp
1
#include "RaZ/Render/BloomRenderProcess.hpp"
2
#include "RaZ/Render/Renderer.hpp"
3
#include "RaZ/Render/RenderGraph.hpp"
4
#include "RaZ/Render/Texture.hpp"
5

6
#include <string_view>
7

8
namespace Raz {
9

10
namespace {
11

12
constexpr int passCount = 5;
13

14
constexpr std::string_view thresholdSource = R"(
15
  in vec2 fragTexcoords;
16

17
  uniform sampler2D uniColorBuffer;
18
  uniform float uniThreshold;
19

20
  layout(location = 0) out vec4 fragColor;
21

22
  void main() {
23
    vec3 color = texture(uniColorBuffer, fragTexcoords).rgb;
24

25
    // Thresholding pixels according to their luminance: https://en.wikipedia.org/wiki/Luma_(video)#Use_of_relative_luminance
26
    float brightness = dot(color, vec3(0.2126, 0.7152, 0.0722));
27
    fragColor        = vec4(color * float(brightness >= uniThreshold), 1.0);
28
  }
29
)";
30

31
constexpr std::string_view downscaleSource = R"(
32
  in vec2 fragTexcoords;
33

34
  uniform sampler2D uniPrevDownscaledBuffer;
35
  uniform vec2 uniInvBufferSize;
36

37
  layout(location = 0) out vec4 fragColor;
38

39
  const vec2 kernelOffsets[13] = vec2[](
40
    vec2(-1.0,  1.0), vec2(1.0,  1.0),
41
    vec2(-1.0, -1.0), vec2(1.0, -1.0),
42

43
    vec2(-2.0,  2.0), vec2(0.0,  2.0), vec2(2.0,  2.0),
44
    vec2(-2.0,  0.0), vec2(0.0,  0.0), vec2(2.0,  0.0),
45
    vec2(-2.0, -2.0), vec2(0.0, -2.0), vec2(2.0, -2.0)
46
  );
47

48
  const float kernelWeights[13] = float[](
49
    // 4 inner samples: (1 / 4) * 0.5
50
    0.125, 0.125,
51
    0.125, 0.125,
52

53
    // 1 middle & 8 outer samples: (1 / 9) * 0.5
54
    0.0555555, 0.0555555, 0.0555555,
55
    0.0555555, 0.0555555, 0.0555555,
56
    0.0555555, 0.0555555, 0.0555555
57
  );
58

59
  void main() {
60
    vec3 color = vec3(0.0);
61

62
    for (int i = 0; i < 13; ++i) {
63
      vec2 normalizedTexcoords = (gl_FragCoord.xy + kernelOffsets[i]) * uniInvBufferSize;
64
      color += texture(uniPrevDownscaledBuffer, normalizedTexcoords).rgb * kernelWeights[i];
65
    }
66

67
    fragColor = vec4(color, 1.0);
68
  }
69
)";
70

71
constexpr std::string_view upscaleSource = R"(
72
  in vec2 fragTexcoords;
73

74
  uniform sampler2D uniDownscaledBuffer;
75
  uniform sampler2D uniPrevUpscaledBuffer;
76
  uniform vec2 uniInvBufferSize;
77

78
  layout(location = 0) out vec4 fragColor;
79

80
  const vec2 kernelOffsets[9] = vec2[](
81
      vec2(-1.0,  1.0), vec2(0.0,  1.0), vec2(1.0,  1.0),
82
      vec2(-1.0,  0.0), vec2(0.0,  0.0), vec2(1.0,  0.0),
83
      vec2(-1.0, -1.0), vec2(0.0, -1.0), vec2(1.0, -1.0)
84
  );
85

86
  const float kernelWeights[9] = float[](
87
      0.0625, 0.125, 0.0625,
88
      0.125,  0.25,  0.125,
89
      0.0625, 0.125, 0.0625
90
  );
91

92
  void main() {
93
    vec3 color = texture(uniDownscaledBuffer, gl_FragCoord.xy * uniInvBufferSize).rgb;
94

95
    for (int i = 0; i < 9; ++i) {
96
      vec2 normalizedTexcoords = (gl_FragCoord.xy + kernelOffsets[i]) * uniInvBufferSize;
97
      color += texture(uniPrevUpscaledBuffer, normalizedTexcoords).rgb * kernelWeights[i];
98
    }
99

100
    fragColor = vec4(color, 1.0);
101
  }
102
)";
103

104
constexpr std::string_view finalSource = R"(
105
  in vec2 fragTexcoords;
106

107
  uniform sampler2D uniOriginalColorBuffer;
108
  uniform sampler2D uniFinalUpscaledBuffer;
109

110
  layout(location = 0) out vec4 fragColor;
111

112
  void main() {
113
    vec3 originalColor = texture(uniOriginalColorBuffer, fragTexcoords).rgb;
114
    vec3 blurredColor  = texture(uniFinalUpscaledBuffer, fragTexcoords).rgb;
115

116
    // The following is technically incorrect, since tone mapping must be done on the whole scene at the very end of the rendering. This will be removed later
117
    blurredColor = blurredColor / (blurredColor + vec3(1.0)); // Tone mapping
118
    blurredColor = pow(blurredColor, vec3(1.0 / 2.2)); // Gamma correction
119

120
    fragColor = vec4(originalColor + blurredColor, 1.0);
121
  }
122
)";
123

124
} // namespace
125

126
BloomRenderProcess::BloomRenderProcess(RenderGraph& renderGraph) : RenderProcess(renderGraph) {
4✔
127
  // Based on Froyok's bloom, itself based on the one used in Unreal Engine 4/Call of Duty: Advanced Warfare
128
  // See: https://www.froyok.fr/blog/2021-12-ue4-custom-bloom/
129

130
  //////////////////
131
  // Thresholding //
132
  //////////////////
133

134
  m_thresholdPass = &renderGraph.addNode(FragmentShader::loadFromSource(thresholdSource), "Bloom thresholding");
4✔
135
  setThresholdValue(0.75f); // Tone mapping is applied before the bloom, thus no value above 1 exist here. This value will be changed later
4✔
136

137
  const auto thresholdBuffer = Texture2D::create(TextureColorspace::RGB, TextureDataType::FLOAT16);
4✔
138
  m_thresholdPass->addWriteColorTexture(thresholdBuffer, 0);
4✔
139

140
#if !defined(USE_OPENGL_ES)
141
  if (Renderer::checkVersion(4, 3)) {
4✔
142
    Renderer::setLabel(RenderObjectType::PROGRAM, m_thresholdPass->getProgram().getIndex(), "Bloom threshold program");
4✔
143
    Renderer::setLabel(RenderObjectType::SHADER, m_thresholdPass->getProgram().getVertexShader().getIndex(), "Bloom threshold vertex shader");
4✔
144
    Renderer::setLabel(RenderObjectType::SHADER, m_thresholdPass->getProgram().getFragmentShader().getIndex(), "Bloom threshold fragment shader");
4✔
145
    Renderer::setLabel(RenderObjectType::FRAMEBUFFER, m_thresholdPass->getFramebuffer().getIndex(), "Bloom threshold framebuffer");
4✔
146
    Renderer::setLabel(RenderObjectType::TEXTURE, thresholdBuffer->getIndex(), "Bloom threshold buffer");
4✔
147
  }
148
#endif
149

150
  /////////////////
151
  // Downscaling //
152
  /////////////////
153

154
  m_downscalePasses.resize(passCount);
4✔
155
  m_downscaleBuffers.resize(passCount);
4✔
156

157
  for (std::size_t downscalePassIndex = 0; downscalePassIndex < passCount; ++downscalePassIndex) {
24✔
158
    const std::string idStr = std::to_string(downscalePassIndex);
20✔
159

160
    RenderPass& downscalePass = renderGraph.addNode(FragmentShader::loadFromSource(downscaleSource), "Bloom downscale #" + idStr);
20✔
161

162
    //  ----------
163
    //  |        |
164
    //  |   D0   |
165
    //  |        |
166
    //  ----------
167
    //      |
168
    //      v prevDownscaledBuffer
169
    //    ------
170
    //    | D1 |
171
    //    ------
172
    //      |
173
    //      v prevDownscaledBuffer
174
    //     ...
175

176
    downscalePass.addReadTexture((downscalePassIndex == 0 ? thresholdBuffer : m_downscaleBuffers[downscalePassIndex - 1].lock()), "uniPrevDownscaledBuffer");
20✔
177

178
    const auto downscaledBuffer = Texture2D::create(TextureColorspace::RGB, TextureDataType::FLOAT16);
20✔
179
    downscalePass.addWriteColorTexture(downscaledBuffer, 0);
20✔
180

181
    m_downscalePasses[downscalePassIndex]  = &downscalePass;
20✔
182
    m_downscaleBuffers[downscalePassIndex] = downscaledBuffer;
20✔
183

184
    downscalePass.addParents((downscalePassIndex == 0 ? *m_thresholdPass : *m_downscalePasses[downscalePassIndex - 1]));
20✔
185

186
#if !defined(USE_OPENGL_ES)
187
    if (Renderer::checkVersion(4, 3)) {
20✔
188
      Renderer::setLabel(RenderObjectType::PROGRAM, downscalePass.getProgram().getIndex(), "Bloom downscale program #" + idStr);
20✔
189
      Renderer::setLabel(RenderObjectType::SHADER, downscalePass.getProgram().getVertexShader().getIndex(), "Bloom downscale vertex shader #" + idStr);
20✔
190
      Renderer::setLabel(RenderObjectType::SHADER, downscalePass.getProgram().getFragmentShader().getIndex(), "Bloom downscale fragment shader #" + idStr);
20✔
191
      Renderer::setLabel(RenderObjectType::FRAMEBUFFER, downscalePass.getFramebuffer().getIndex(), "Bloom downscale framebuffer #" + idStr);
20✔
192
      Renderer::setLabel(RenderObjectType::TEXTURE, downscaledBuffer->getIndex(), "Bloom downscale buffer #" + idStr);
20✔
193
    }
194
#endif
195
  }
20✔
196

197
  ///////////////
198
  // Upscaling //
199
  ///////////////
200

201
  m_upscalePasses.resize(passCount - 1);
4✔
202
  m_upscaleBuffers.resize(passCount - 1);
4✔
203

204
  for (std::size_t upscalePassIndex = 0; upscalePassIndex < passCount - 1; ++upscalePassIndex) {
20✔
205
    const std::string idStr = std::to_string(upscalePassIndex);
16✔
206

207
    RenderPass& upscalePass = renderGraph.addNode(FragmentShader::loadFromSource(upscaleSource), "Bloom upscale #" + idStr);
16✔
208

209
    //  ----------                ----------
210
    //  |        |                |        |
211
    //  |   D0   |--------------->|   U0   |
212
    //  |        |                |        |
213
    //  ----------                ----------
214
    //      |                         ^
215
    //      v                         |
216
    //    ------   downscaledBuffer ------
217
    //    | D1 |------------------->| U1 |
218
    //    ------                    ------
219
    //      |                         ^ prevUpscaledBuffer
220
    //      v                         |
221
    //     ... ------------------------
222

223
    const std::size_t correspDownscalePassIndex = passCount - upscalePassIndex - 2;
16✔
224

225
    upscalePass.addReadTexture(m_downscaleBuffers[correspDownscalePassIndex].lock(), "uniDownscaledBuffer");
32✔
226
    upscalePass.addReadTexture((upscalePassIndex == 0 ? m_downscaleBuffers.back() : m_upscaleBuffers[upscalePassIndex - 1]).lock(), "uniPrevUpscaledBuffer");
16✔
227

228
    const auto upscaledBuffer = Texture2D::create(TextureColorspace::RGB, TextureDataType::FLOAT16);
16✔
229
    upscalePass.addWriteColorTexture(upscaledBuffer, 0);
16✔
230

231
    m_upscalePasses[upscalePassIndex]  = &upscalePass;
16✔
232
    m_upscaleBuffers[upscalePassIndex] = upscaledBuffer;
16✔
233

234
    // Although each upscaling pass is technically dependent on the matching downscaling one, the render graph only needs
235
    //  direct dependencies, that is, passes that can be executed anytime after their parents have been. In this case, we need
236
    //  to execute each one sequentially whenever the previous upscaling pass has finished anyway. Hence, although we could, we
237
    //  do not set any dependency between upscaling & downscaling passes aside from the first one
238

239
    upscalePass.addParents((upscalePassIndex == 0 ? *m_downscalePasses.back() : *m_upscalePasses[upscalePassIndex - 1]));
16✔
240

241
#if !defined(USE_OPENGL_ES)
242
    if (Renderer::checkVersion(4, 3)) {
16✔
243
      Renderer::setLabel(RenderObjectType::PROGRAM, upscalePass.getProgram().getIndex(), "Bloom upscale program #" + idStr);
16✔
244
      Renderer::setLabel(RenderObjectType::SHADER, upscalePass.getProgram().getVertexShader().getIndex(), "Bloom upscale vertex shader #" + idStr);
16✔
245
      Renderer::setLabel(RenderObjectType::SHADER, upscalePass.getProgram().getFragmentShader().getIndex(), "Bloom upscale fragment shader #" + idStr);
16✔
246
      Renderer::setLabel(RenderObjectType::FRAMEBUFFER, upscalePass.getFramebuffer().getIndex(), "Bloom upscale framebuffer #" + idStr);
16✔
247
      Renderer::setLabel(RenderObjectType::TEXTURE, upscaledBuffer->getIndex(), "Bloom upscale buffer #" + idStr);
16✔
248
    }
249
#endif
250
  }
16✔
251

252
  ////////////////////////
253
  // Final display pass //
254
  ////////////////////////
255

256
  m_finalPass = &renderGraph.addNode(FragmentShader::loadFromSource(finalSource), "Bloom final pass");
4✔
257

258
  m_finalPass->addParents(*m_upscalePasses.back());
4✔
259
  m_finalPass->addReadTexture(m_upscaleBuffers.back().lock(), "uniFinalUpscaledBuffer");
8✔
260

261
#if !defined(USE_OPENGL_ES)
262
  if (Renderer::checkVersion(4, 3)) {
4✔
263
    Renderer::setLabel(RenderObjectType::PROGRAM, m_finalPass->getProgram().getIndex(), "Bloom final pass program");
4✔
264
    Renderer::setLabel(RenderObjectType::SHADER, m_finalPass->getProgram().getVertexShader().getIndex(), "Bloom final pass vertex shader");
4✔
265
    Renderer::setLabel(RenderObjectType::SHADER, m_finalPass->getProgram().getFragmentShader().getIndex(), "Bloom final pass fragment shader");
4✔
266
  }
267
#endif
268

269
  // Validating the render graph
270
  if (!renderGraph.isValid())
4✔
NEW
271
    throw std::runtime_error("[BloomRenderProcess] The bloom process is invalid");
×
272
}
4✔
273

274
bool BloomRenderProcess::isEnabled() const noexcept {
1✔
275
  return m_thresholdPass->isEnabled();
1✔
276
}
277

278
void BloomRenderProcess::setState(bool enabled) {
3✔
279
  m_thresholdPass->enable(enabled);
3✔
280

281
  for (RenderPass* downscalePass : m_downscalePasses)
18✔
282
    downscalePass->enable(enabled);
15✔
283

284
  for (RenderPass* upscalePass : m_upscalePasses)
15✔
285
    upscalePass->enable(enabled);
12✔
286

287
  m_finalPass->enable(enabled);
3✔
288
}
3✔
289

290
void BloomRenderProcess::addParent(RenderPass& parentPass) {
2✔
291
  m_thresholdPass->addParents(parentPass);
2✔
292
}
2✔
293

294
void BloomRenderProcess::addParent(RenderProcess& parentProcess) {
1✔
295
  parentProcess.addChild(*m_thresholdPass);
1✔
296
}
1✔
297

298
void BloomRenderProcess::addChild(RenderPass& childPass) {
2✔
299
  m_finalPass->addChildren(childPass);
2✔
300
}
2✔
301

302
void BloomRenderProcess::addChild(RenderProcess& childProcess) {
1✔
303
  childProcess.addParent(*m_finalPass);
1✔
304
}
1✔
305

306
void BloomRenderProcess::resizeBuffers(unsigned int width, unsigned int height) {
3✔
307
  m_thresholdPass->resizeWriteBuffers(width, height);
3✔
308
  m_finalPass->resizeWriteBuffers(width, height);
3✔
309

310
  for (std::size_t i = 0; i < m_downscaleBuffers.size(); ++i) {
15✔
311
    width  /= 2;
15✔
312
    height /= 2;
15✔
313

314
    const Vec2f invBufferSize(1.f / static_cast<float>(width), 1.f / static_cast<float>(height));
15✔
315

316
    m_downscalePasses[i]->resizeWriteBuffers(width, height);
15✔
317

318
    m_downscalePasses[i]->getProgram().setAttribute(invBufferSize, "uniInvBufferSize");
30✔
319
    m_downscalePasses[i]->getProgram().sendAttributes();
15✔
320

321
    if (i >= m_upscalePasses.size())
15✔
322
      break;
3✔
323

324
    const std::size_t correspIndex = m_downscaleBuffers.size() - i - 2;
12✔
325

326
    m_upscalePasses[correspIndex]->resizeWriteBuffers(width, height);
12✔
327

328
    m_upscalePasses[correspIndex]->getProgram().setAttribute(invBufferSize, "uniInvBufferSize");
24✔
329
    m_upscalePasses[correspIndex]->getProgram().sendAttributes();
12✔
330
  }
331
}
3✔
332

333
float BloomRenderProcess::recoverElapsedTime() const {
1✔
334
  float time = m_thresholdPass->recoverElapsedTime() + m_finalPass->recoverElapsedTime();
1✔
335

336
  for (const RenderPass* pass : m_downscalePasses)
6✔
337
    time += pass->recoverElapsedTime();
5✔
338

339
  for (const RenderPass* pass : m_upscalePasses)
5✔
340
    time += pass->recoverElapsedTime();
4✔
341

342
  return time;
1✔
343
}
344

345
void BloomRenderProcess::setInputColorBuffer(Texture2DPtr colorBuffer) {
1✔
346
  resizeBuffers(colorBuffer->getWidth(), colorBuffer->getHeight());
1✔
347

348
  m_thresholdPass->addReadTexture(colorBuffer, "uniColorBuffer");
2✔
349
  m_finalPass->addReadTexture(std::move(colorBuffer), "uniOriginalColorBuffer");
2✔
350
}
1✔
351

352
void BloomRenderProcess::setOutputBuffer(Texture2DPtr outputBuffer) {
1✔
353
  m_finalPass->addWriteColorTexture(std::move(outputBuffer), 0);
1✔
354

355
#if !defined(USE_OPENGL_ES)
356
  if (Renderer::checkVersion(4, 3))
1✔
357
    Renderer::setLabel(RenderObjectType::FRAMEBUFFER, m_finalPass->getFramebuffer().getIndex(), "Bloom final pass framebuffer");
1✔
358
#endif
359
}
1✔
360

361
void BloomRenderProcess::setThresholdValue(float threshold) const {
5✔
362
  m_thresholdPass->getProgram().setAttribute(threshold, "uniThreshold");
10✔
363
  m_thresholdPass->getProgram().sendAttributes();
5✔
364
}
5✔
365

366
} // namespace Raz
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc