GCC Code Coverage Report


Directory: ./
File: lib/geogram_gfx/full_screen_effects/ambient_occlusion.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 0 169 0.0%
Functions: 0 13 0.0%
Branches: 0 38 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram_gfx/full_screen_effects/ambient_occlusion.h>
41 #include <geogram_gfx/basic/GLSL.h>
42 #include <geogram/basic/logger.h>
43
44 // TODO: try to generate a mipmap pyramid from the depth buffer
45 // (with max instead of mean) and lookup lower levels when far
46 // away from query point.
47 // see
48 // http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/
49
50 namespace {
51 using namespace GEO;
52 static const index_t R_WIDTH = 32;
53 static const index_t R_HEIGHT = 32;
54 }
55
56 // Requirements for Android compile are the same
57 // as for Emscripten.
58 // TODO: something cleaner.
59 #ifdef GEO_OS_ANDROID
60 #define GEO_OS_EMSCRIPTEN
61 #endif
62
63 namespace GEO {
64
65 AmbientOcclusionImpl::AmbientOcclusionImpl() {
66 lightness_ = 10;
67 contrast_ = 10;
68 blur_width_ = 2;
69 nb_directions_ = 7;
70
71 SSAO_program_ = 0;
72 blur_program_ = 0;
73 random_tex_ = 0;
74
75 max_radius_ = 0.5;
76 step_mul_ = 1.2;
77 }
78
79 AmbientOcclusionImpl::~AmbientOcclusionImpl() {
80 glDeleteTextures(1, &random_tex_);
81 random_tex_ = 0;
82 if (SSAO_program_ != 0) {
83 glDeleteProgram(SSAO_program_);
84 }
85 if (blur_program_ != 0) {
86 glDeleteProgram(blur_program_);
87 }
88 }
89
90 double AmbientOcclusionImpl::required_GLSL_version() const {
91 #ifdef GEO_OS_EMSCRIPTEN
92 return 1.0;
93 #else
94 return 1.3;
95 #endif
96 }
97
98 void AmbientOcclusionImpl::pre_render(index_t w, index_t h) {
99 FullScreenEffectImpl::pre_render(w,h);
100 }
101
102 void AmbientOcclusionImpl::post_render() {
103 // Copies the content of draw_FBO_ to the screen.
104 FullScreenEffectImpl::post_render();
105
106 // Computes SSAO.
107 get_proj_inv();
108 compute_SSAO();
109 blur();
110
111 // Composites SSAO with previously rendered image.
112 display_final_texture();
113 reset_alpha();
114 }
115
116 void AmbientOcclusionImpl::update() {
117 if(!OK()) {
118 return;
119 }
120 if(SSAO_program_ != 0) {
121 GLSL::set_program_uniform_by_name(
122 SSAO_program_, "max_radius", float(max_radius_)
123 );
124 GLSL::set_program_uniform_by_name(
125 SSAO_program_, "step_mul", float(step_mul_)
126 );
127 GLSL::set_program_uniform_by_name(
128 SSAO_program_, "shadows_gamma", float(contrast_) / 10.0f
129 );
130 GLSL::set_program_uniform_by_name(
131 SSAO_program_, "shadows_intensity", float(lightness_) / 10.0f
132 );
133 GLSL::set_program_uniform_by_name(
134 SSAO_program_, "depth_cueing", 0.0f
135 );
136 GLSL::set_program_uniform_by_name(
137 SSAO_program_, "nb_directions", float(nb_directions_)
138 );
139 }
140 if(blur_program_ != 0) {
141 GLSL::set_program_uniform_by_name(
142 blur_program_, "blur_width", float(blur_width_)
143 );
144 }
145 }
146
147 void AmbientOcclusionImpl::initialize(index_t w, index_t h) {
148 GEO_CHECK_GL();
149 FullScreenEffectImpl::initialize(w,h);
150 GEO_CHECK_GL();
151 if(!OK()) {
152 return;
153 }
154 #if defined(GEO_OS_EMSCRIPTEN) && !defined(GEO_WEBGL2)
155 const GLint internal_format = GL_RGBA;
156 #else
157 const GLint internal_format = GL_RED;
158 #endif
159 // Shader sources are embedded in source code,
160 // Sourcecode is in:
161 // geogram_gfx/GLUP/shaders/fullscreen
162 SSAO_program_ = GLSL::compile_program_with_includes_no_link(
163 this,
164 "//stage GL_VERTEX_SHADER\n"
165 "//import <fullscreen/vertex_shader.h>\n",
166 "//stage GL_FRAGMENT_SHADER\n"
167 "//import <fullscreen/ambient_occlusion_fragment_shader.h>\n"
168 );
169 GEO_CHECK_GL();
170 glBindAttribLocation(SSAO_program_, 0, "vertex_in");
171 glBindAttribLocation(SSAO_program_, 1, "tex_coord_in");
172 GLSL::link_program(SSAO_program_);
173
174 GLSL::set_program_uniform_by_name(
175 SSAO_program_, "depth_texture", 0
176 );
177 GLSL::set_program_uniform_by_name(
178 SSAO_program_, "random_texture", 1
179 );
180 proj_inv_loc_ = glGetUniformLocation(SSAO_program_, "mat_proj_inv");
181 if( !blur_1_.initialize(
182 width(), height(), false, internal_format)
183 ) {
184 Logger::err("SSAO")
185 << "blur_1_ FBO is not initialized" << std::endl;
186 }
187 if( !blur_2_.initialize(
188 width(), height(), false, internal_format
189 )
190 ) {
191 Logger::err("SSAO")
192 << "blur_2_ FBO is not initialized" << std::endl;
193 }
194
195
196 // Shader sources are embedded in source code,
197 // Initial sourcecode is in:
198 // geogram_gfx/GLUP/shaders/fullscreen
199 blur_program_ = GLSL::compile_program_with_includes_no_link(
200 this,
201 "//stage GL_VERTEX_SHADER\n"
202 "//import <fullscreen/vertex_shader.h>\n",
203 "//stage GL_FRAGMENT_SHADER\n"
204 "//import <fullscreen/depth_dependent_blur_fragment_shader.h>\n"
205 );
206
207
208 glBindAttribLocation(blur_program_, 0, "vertex_in");
209 glBindAttribLocation(blur_program_, 1, "tex_coord_in");
210 GLSL::link_program(blur_program_);
211 create_random_tex();
212
213 GLSL::set_program_uniform_by_name(blur_program_, "source_tex", 0);
214 GLSL::set_program_uniform_by_name(blur_program_, "depth_tex", 1);
215
216 GEO_CHECK_GL();
217
218 if(ES_profile_) {
219 GLSL::set_program_uniform_by_name(
220 SSAO_program_, "source_tex_size",
221 float(blur_1_.width), float(blur_1_.height)
222 );
223
224 GLSL::set_program_uniform_by_name(
225 SSAO_program_, "depth_tex_size",
226 float(w), float(h)
227 );
228
229 GLSL::set_program_uniform_by_name(
230 SSAO_program_, "random_tex_size",
231 float(R_WIDTH), float(R_HEIGHT)
232 );
233
234 GLSL::set_program_uniform_by_name(
235 blur_program_, "source_tex_size",
236 float(blur_1_.width), float(blur_1_.height)
237 );
238 }
239
240 update();
241 }
242
243 void AmbientOcclusionImpl::resize(index_t width, index_t height) {
244 blur_1_.resize(width, height);
245 blur_2_.resize(width, height);
246
247 if(ES_profile_) {
248 GLSL::set_program_uniform_by_name(
249 SSAO_program_, "source_tex_size",
250 float(blur_1_.width), float(blur_1_.height)
251 );
252
253 GLSL::set_program_uniform_by_name(
254 SSAO_program_, "depth_tex_size",
255 float(width), float(height)
256 );
257
258 GLSL::set_program_uniform_by_name(
259 SSAO_program_, "random_tex_size",
260 float(R_WIDTH), float(R_HEIGHT)
261 );
262
263 GLSL::set_program_uniform_by_name(
264 blur_program_, "source_tex_size",
265 float(blur_1_.width), float(blur_1_.height)
266 );
267 }
268
269 FullScreenEffectImpl::resize(width, height);
270 }
271
272 void AmbientOcclusionImpl::create_random_tex() {
273 glGenTextures(1, &random_tex_);
274 glBindTexture(GL_TEXTURE_2D, random_tex_);
275 static Memory::byte tex_buff[R_WIDTH * R_HEIGHT];
276 for (unsigned int i = 0; i < R_WIDTH * R_HEIGHT; i++) {
277 tex_buff[i] = Memory::byte(Numeric::random_int32() & 255);
278 }
279 glTexImage2D(
280 GL_TEXTURE_2D, 0,
281 GL_RED,
282 R_WIDTH, R_HEIGHT, 0,
283 GL_RED,
284 GL_UNSIGNED_BYTE, tex_buff
285 );
286 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
287 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
288 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
289 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
290 glBindTexture(GL_TEXTURE_2D, 0);
291 }
292
293 void AmbientOcclusionImpl::display_final_texture() {
294 glBindFramebuffer(GL_FRAMEBUFFER, main_framebuffer_id_);
295
296 glDisable(GL_DEPTH_TEST);
297 glEnable(GL_BLEND);
298 glBlendFunc(GL_ZERO, GL_SRC_COLOR);
299 // Go back to viewport transform that covers the [-1,1]x[-1,1]
300 // normalized device coordinates space.
301 glViewport(0, 0, GLsizei(width()), GLsizei(height()));
302 blur_1_.bind_as_texture();
303 draw_unit_textured_quad(TEX_QUAD_RRR1); // expand red channel to (r,g,b)
304 glEnable(GL_DEPTH_TEST);
305 glDisable(GL_BLEND);
306 blur_1_.unbind();
307 }
308
309 void AmbientOcclusionImpl::compute_SSAO() {
310 blur_1_.bind_as_framebuffer();
311 glClearColor(1.0, 1.0, 1.0, 0.0);
312 glClear(GL_COLOR_BUFFER_BIT);
313
314 glDisable(GL_DEPTH_TEST);
315 glEnable(GL_BLEND);
316 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
317
318 glUseProgram(SSAO_program_);
319
320 // 'false' for column major order
321 glUniformMatrix4fv(proj_inv_loc_, 1, false, proj_inv_);
322
323 glActiveTexture(GL_TEXTURE0);
324 draw_FBO_.bind_depth_buffer_as_texture();
325
326 glActiveTexture(GL_TEXTURE1);
327 glBindTexture(GL_TEXTURE_2D, random_tex_);
328 glViewport(0, 0, GLsizei(width()), GLsizei(height()));
329
330 draw_unit_textured_quad();
331
332 glActiveTexture(GL_TEXTURE1);
333 glBindTexture(GL_TEXTURE_2D, 0);
334 glActiveTexture(GL_TEXTURE0);
335 glBindTexture(GL_TEXTURE_2D, 0);
336
337 glEnable(GL_DEPTH_TEST);
338 glDisable(GL_BLEND);
339
340 blur_1_.unbind();
341 draw_FBO_.unbind();
342
343 glUseProgram(0);
344 }
345
346 void AmbientOcclusionImpl::blur() {
347 if(blur_width_ < 1) {
348 return;
349 }
350 glDisable(GL_DEPTH_TEST);
351
352 glActiveTexture(GL_TEXTURE1);
353 draw_FBO_.bind_depth_buffer_as_texture();
354 glActiveTexture(GL_TEXTURE0);
355
356 // Horizontal blur: blur_1_ -> blur_2_
357 blur_2_.bind_as_framebuffer();
358 GLSL::set_program_uniform_by_name(blur_program_, "vertical", false);
359 glUseProgram(blur_program_);
360 blur_1_.bind_as_texture();
361 draw_unit_textured_quad();
362 blur_2_.unbind();
363
364 // Vertical blur: blur_2_ -> blur_1_
365 blur_1_.bind_as_framebuffer();
366 GLSL::set_program_uniform_by_name(blur_program_, "vertical", true);
367 glUseProgram(blur_program_);
368 blur_2_.bind_as_texture();
369 draw_unit_textured_quad();
370 blur_1_.unbind();
371 glUseProgram(0);
372
373 glActiveTexture(GL_TEXTURE1);
374 glBindTexture(GL_TEXTURE_2D, 0);
375
376 glActiveTexture(GL_TEXTURE0);
377 glBindTexture(GL_TEXTURE_2D, 0);
378
379 draw_FBO_.unbind();
380
381 glEnable(GL_DEPTH_TEST);
382 }
383
384 void AmbientOcclusionImpl::get_proj_inv() {
385 GLfloat proj[16];
386 glupGetMatrixfv(GLUP_PROJECTION_MATRIX, proj);
387 if(!glupInvertMatrixfv(proj_inv_, proj)) {
388 Logger::err("GLAmbientOcclusionDB")
389 << "Projection matrix is singular" << std::endl;
390 }
391 }
392
393 }
394