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