Line data Source code
1 : /**
2 : * @file ao.cpp
3 : * @brief screenspace ambient occlusion
4 : *
5 : * Screenspace ambient occlusion is a way to simulate darkening of corners which
6 : * do not recieve as much diffuse light as other areas. SSAO relies on the depth
7 : * buffer of the scene to determine areas which appear to be creases and
8 : * darkens those areas. Various settings allow for more or less artifact-free
9 : * rendition of this darkening effect.
10 : */
11 : #include "../libprimis-headers/cube.h"
12 : #include "../../shared/geomexts.h"
13 : #include "../../shared/glemu.h"
14 : #include "../../shared/glexts.h"
15 :
16 : #include <format>
17 :
18 : #include "ao.h"
19 : #include "rendergl.h"
20 : #include "renderlights.h"
21 : #include "rendertimers.h"
22 : #include "renderwindow.h"
23 : #include "shader.h"
24 : #include "shaderparam.h"
25 : #include "texture.h"
26 :
27 : #include "interface/control.h"
28 :
29 : int aow = -1,
30 : aoh = -1;
31 : static std::array<GLuint, 4> aofbo = { 0, 0, 0, 0 };
32 : std::array<GLuint, 4> aotex = { 0, 0, 0, 0 };
33 : static GLuint aonoisetex = 0;
34 :
35 0 : VARFP(ao, 0, 1, 1, { cleanupao(); cleardeferredlightshaders(); }); //toggles ao use in general
36 : static FVARR(aoradius, 0, 5, 256);
37 : static FVAR(aocutoff, 0, 2.0f, 1e3f);
38 : static FVARR(aodark, 1e-3f, 11.0f, 1e3f);
39 : static FVARR(aosharp, 1e-3f, 1, 1e3f);
40 : static FVAR(aoprefilterdepth, 0, 1, 1e3f);
41 : FVARR(aomin, 0, 0.25f, 1);
42 0 : VARFR(aosun, 0, 1, 1, cleardeferredlightshaders()); //toggles ambient occlusion for sunlight
43 : FVARR(aosunmin, 0, 0.5f, 1);
44 : static VARP(aoblur, 0, 4, 7);
45 : static VARP(aoiter, 0, 0, 4); //number of times to run ao shader (higher is smoother)
46 0 : VARFP(aoreduce, 0, 1, 2, cleanupao());
47 0 : VARF(aoreducedepth, 0, 1, 2, cleanupao());
48 0 : static VARFP(aofloatdepth, 0, 1, 2, initwarning("AO setup", Init_Load, Change_Shaders));
49 0 : static VARFP(aoprec, 0, 1, 1, cleanupao()); //toggles between r8 and rgba8 buffer format
50 : static VAR(aodepthformat, 1, 0, 0);
51 0 : static VARF(aonoise, 0, 5, 8, cleanupao()); //power or two scale factor for ao noise effect
52 0 : VARFP(aobilateral, 0, 3, 10, cleanupao());
53 : static FVARP(aobilateraldepth, 0, 4, 1e3f);
54 0 : VARFP(aobilateralupscale, 0, 0, 1, cleanupao());
55 0 : VARF(aopackdepth, 0, 1, 1, cleanupao());
56 0 : static VARFP(aotaps, 1, 12, 12, cleanupao());
57 : static VAR(debugao, 0, 0, 4);
58 :
59 : static Shader *ambientobscuranceshader = nullptr;
60 :
61 : /**
62 : * @brief Creates a new ambient obscurance object.
63 : *
64 : * Creates a new ambient obscurance (ambient occlusion) object with values based
65 : * on current settings. This object envelops the AO shader and its accompanying
66 : * values.
67 : *
68 : * @return a new Shader object that applies an AO effect.
69 : */
70 0 : static Shader *loadambientobscuranceshader()
71 : {
72 : string opts;
73 0 : int optslen = 0;
74 :
75 0 : bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1);
76 0 : if(linear)
77 : {
78 0 : opts[optslen++] = 'l';
79 : }
80 0 : if(aobilateral && aopackdepth)
81 : {
82 0 : opts[optslen++] = 'p';
83 : }
84 0 : opts[optslen] = '\0';
85 0 : std::string name = std::format("ambientobscurance{}{}", opts, aotaps);
86 0 : return generateshader(name, "ambientobscuranceshader \"%s\" %d", opts, aotaps);
87 0 : }
88 :
89 : /**
90 : * @brief Sets the ambientobscuranceshader gvar to the value created by above fxn.
91 : *
92 : * The loadambientobscuranceshader() function is called to generate a Shader object
93 : * and is assigned to the ambient obscurance shader global variable. This global
94 : * is local to this file.
95 : */
96 0 : static void loadaoshaders()
97 : {
98 0 : ambientobscuranceshader = loadambientobscuranceshader();
99 0 : }
100 :
101 : /**
102 : * @brief un-sets the ambientobscuranceshader gvar defined by loadaoshaders
103 : *
104 : * Sets the ambientobscuranceshader variable set in loadaoshaders() to nullptr.
105 : * Does not free the object.
106 : */
107 0 : static void clearaoshaders()
108 : {
109 0 : ambientobscuranceshader = nullptr;
110 0 : }
111 :
112 0 : void setupao(int w, int h)
113 : {
114 0 : int sw = w>>aoreduce,
115 0 : sh = h>>aoreduce;
116 :
117 0 : if(sw == aow && sh == aoh)
118 : {
119 0 : return;
120 : }
121 0 : aow = sw;
122 0 : aoh = sh;
123 0 : if(!aonoisetex)
124 : {
125 0 : glGenTextures(1, &aonoisetex);
126 : }
127 0 : bvec *noise = new bvec[(1<<aonoise)*(1<<aonoise)];
128 0 : for(int k = 0; k < (1<<aonoise)*(1<<aonoise); ++k)
129 : {
130 0 : noise[k] = bvec(vec(randomfloat(2)-1, randomfloat(2)-1, 0).normalize());
131 : }
132 0 : createtexture(aonoisetex, 1<<aonoise, 1<<aonoise, noise, 0, 0, GL_RGB, GL_TEXTURE_2D);
133 0 : delete[] noise;
134 :
135 0 : bool upscale = aoreduce && aobilateral && aobilateralupscale;
136 0 : GLenum format = aoprec ? GL_R8 : GL_RGBA8,
137 0 : packformat = aobilateral && aopackdepth ? (aodepthformat ? GL_RG16F : GL_RGBA8) : format;
138 0 : int packfilter = upscale && aopackdepth && !aodepthformat ? 0 : 1;
139 0 : for(int i = 0; i < (upscale ? 3 : 2); ++i)
140 : {
141 : //create framebuffer
142 0 : if(!aotex[i])
143 : {
144 0 : glGenTextures(1, &aotex[i]);
145 : }
146 0 : if(!aofbo[i])
147 : {
148 0 : glGenFramebuffers(1, &aofbo[i]);
149 : }
150 0 : createtexture(aotex[i], upscale && i ? w : aow, upscale && i >= 2 ? h : aoh, nullptr, 3, i < 2 ? packfilter : 1, i < 2 ? packformat : format, GL_TEXTURE_RECTANGLE);
151 0 : glBindFramebuffer(GL_FRAMEBUFFER, aofbo[i]);
152 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, aotex[i], 0);
153 : //make sure we have a framebuffer
154 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
155 : {
156 0 : fatal("failed allocating AO buffer!");
157 : }
158 0 : if(!upscale && packformat == GL_RG16F)
159 : {
160 0 : glClearColor(0, 0, 0, 0);
161 0 : glClear(GL_COLOR_BUFFER_BIT);
162 : }
163 : }
164 0 : if(aoreducedepth && (aoreduce || aoreducedepth > 1))
165 : {
166 : //create framebuffer
167 0 : if(!aotex[3])
168 : {
169 0 : glGenTextures(1, &aotex[3]);
170 : }
171 0 : if(!aofbo[3])
172 : {
173 0 : glGenFramebuffers(1, &aofbo[3]);
174 : }
175 0 : createtexture(aotex[3], aow, aoh, nullptr, 3, 0, aodepthformat > 1 ? GL_R32F : (aodepthformat ? GL_R16F : GL_RGBA8), GL_TEXTURE_RECTANGLE);
176 0 : glBindFramebuffer(GL_FRAMEBUFFER, aofbo[3]);
177 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, aotex[3], 0);
178 : //make sure we have a framebuffer
179 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
180 : {
181 0 : fatal("failed allocating AO buffer!");
182 : }
183 : }
184 :
185 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
186 :
187 0 : loadaoshaders();
188 0 : loadbilateralshaders();
189 : }
190 :
191 0 : void cleanupao()
192 : {
193 0 : for(int i = 0; i < 4; ++i)
194 : {
195 0 : if(aofbo[i])
196 : {
197 0 : glDeleteFramebuffers(1, &aofbo[i]);
198 0 : aofbo[i] = 0;
199 : }
200 : }
201 0 : for(int i = 0; i < 4; ++i)
202 : {
203 0 : if(aotex[i])
204 : {
205 0 : glDeleteTextures(1, &aotex[i]);
206 0 : aotex[i] = 0;
207 : }
208 : }
209 0 : if(aonoisetex)
210 : {
211 0 : glDeleteTextures(1, &aonoisetex);
212 0 : aonoisetex = 0;
213 : }
214 0 : aow = aoh = -1;
215 :
216 0 : clearaoshaders();
217 0 : clearbilateralshaders();
218 0 : }
219 :
220 0 : void initao()
221 : {
222 0 : aodepthformat = aofloatdepth ? aofloatdepth : 0;
223 0 : }
224 :
225 0 : void viewao()
226 : {
227 0 : if(!ao || !debugao)
228 : {
229 0 : return;
230 : }
231 0 : int w = debugfullscreen ? hudw() : std::min(hudw(), hudh())/2, //if debugfullscreen, set to hudw/hudh size; if not, do small size
232 0 : h = debugfullscreen ? hudh() : (w*hudh())/hudw();
233 0 : SETSHADER(hudrect);
234 0 : gle::colorf(1, 1, 1);
235 0 : glBindTexture(GL_TEXTURE_RECTANGLE, aotex[debugao - 1]);
236 0 : int tw = aotex[2] ? gw : aow,
237 0 : th = aotex[2] ? gh : aoh;
238 0 : debugquad(0, 0, w, h, 0, 0, tw, th);
239 : }
240 :
241 0 : void GBuffer::renderao() const
242 : {
243 0 : if(!ao)
244 : {
245 0 : return;
246 : }
247 0 : timer *aotimer = begintimer("ambient obscurance");
248 :
249 0 : if(msaasamples)
250 : {
251 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
252 : }
253 : else
254 : {
255 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
256 : }
257 0 : bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1);
258 0 : float xscale = eyematrix.a.x,
259 0 : yscale = eyematrix.b.y;
260 0 : if(linear)
261 : {
262 0 : glBindFramebuffer(GL_FRAMEBUFFER, aofbo[3]);
263 0 : glViewport(0, 0, aow, aoh);
264 0 : SETSHADER(linearizedepth);
265 0 : screenquad(vieww, viewh);
266 :
267 0 : xscale *= static_cast<float>(vieww)/aow;
268 0 : yscale *= static_cast<float>(viewh)/aoh;
269 :
270 0 : glBindTexture(GL_TEXTURE_RECTANGLE, aotex[3]);
271 : }
272 :
273 0 : ambientobscuranceshader->set();
274 :
275 0 : glBindFramebuffer(GL_FRAMEBUFFER, aofbo[0]);
276 0 : glViewport(0, 0, aow, aoh);
277 0 : glActiveTexture(GL_TEXTURE1);
278 :
279 0 : if(msaasamples)
280 : {
281 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
282 : }
283 : else
284 : {
285 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gnormaltex);
286 : }
287 :
288 0 : LOCALPARAM(normalmatrix, matrix3(cammatrix));
289 0 : glActiveTexture(GL_TEXTURE2);
290 0 : glBindTexture(GL_TEXTURE_2D, aonoisetex);
291 0 : glActiveTexture(GL_TEXTURE0);
292 :
293 0 : LOCALPARAMF(tapparams, aoradius*eyematrix.d.z/xscale, aoradius*eyematrix.d.z/yscale, aoradius*aoradius*aocutoff*aocutoff);
294 0 : LOCALPARAMF(contrastparams, (2.0f*aodark)/aotaps, aosharp);
295 0 : LOCALPARAMF(offsetscale, xscale/eyematrix.d.z, yscale/eyematrix.d.z, eyematrix.d.x/eyematrix.d.z, eyematrix.d.y/eyematrix.d.z);
296 0 : LOCALPARAMF(prefilterdepth, aoprefilterdepth);
297 0 : screenquad(vieww, viewh, aow/static_cast<float>(1<<aonoise), aoh/static_cast<float>(1<<aonoise));
298 :
299 0 : if(aobilateral)
300 : {
301 0 : if(aoreduce && aobilateralupscale)
302 : {
303 0 : for(int i = 0; i < 2; ++i)
304 : {
305 0 : setbilateralshader(aobilateral, i, aobilateraldepth);
306 0 : glBindFramebuffer(GL_FRAMEBUFFER, aofbo[i+1]);
307 0 : glViewport(0, 0, vieww, i ? viewh : aoh);
308 0 : glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i]);
309 0 : glActiveTexture(GL_TEXTURE1);
310 0 : if(msaasamples)
311 : {
312 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
313 : }
314 : else
315 : {
316 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
317 : }
318 0 : glActiveTexture(GL_TEXTURE0);
319 0 : screenquad(vieww, viewh, i ? vieww : aow, aoh);
320 : }
321 0 : }
322 : else
323 : {
324 0 : for(int i = 0; i < 2 + 2*aoiter; ++i)
325 : {
326 0 : setbilateralshader(aobilateral, i%2, aobilateraldepth);
327 0 : glBindFramebuffer(GL_FRAMEBUFFER, aofbo[(i+1)%2]);
328 0 : glViewport(0, 0, aow, aoh);
329 0 : glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i%2]);
330 0 : glActiveTexture(GL_TEXTURE1);
331 0 : if(linear)
332 : {
333 0 : glBindTexture(GL_TEXTURE_RECTANGLE, aotex[3]);
334 : }
335 0 : else if(msaasamples)
336 : {
337 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
338 : }
339 : else
340 : {
341 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
342 : }
343 0 : glActiveTexture(GL_TEXTURE0);
344 0 : screenquad(vieww, viewh);
345 : }
346 : }
347 : }
348 0 : else if(aoblur)
349 : {
350 : std::array<float, maxblurradius+1> blurweights,
351 : bluroffsets;
352 0 : setupblurkernel(aoblur, blurweights, bluroffsets);
353 0 : for(int i = 0; i < 2+2*aoiter; ++i)
354 : {
355 0 : glBindFramebuffer(GL_FRAMEBUFFER, aofbo[(i+1)%2]);
356 0 : glViewport(0, 0, aow, aoh);
357 0 : setblurshader(i%2, 1, aoblur, blurweights, bluroffsets, GL_TEXTURE_RECTANGLE);
358 0 : glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i%2]);
359 0 : screenquad(aow, aoh);
360 : }
361 : }
362 :
363 0 : glBindFramebuffer(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
364 0 : glViewport(0, 0, vieww, viewh);
365 :
366 0 : endtimer(aotimer);
367 : }
|