LCOV - code coverage report
Current view: top level - engine/render - ao.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 0.0 % 174 0
Test Date: 2026-05-09 04:28:55 Functions: 0.0 % 19 0

            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              : }
        

Generated by: LCOV version 2.0-1