LCOV - code coverage report
Current view: top level - engine/render - csm.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 16 248 6.5 %
Date: 2024-11-22 05:07:59 Functions: 3 11 27.3 %

          Line data    Source code
       1             : /* csm.cpp: cascaded shadow maps
       2             :  *
       3             :  * The cascaded shadow maps are used to provide levels of detail for sunlight
       4             :  * (nearer areas get higher resolution shadow maps, and farther areas are
       5             :  * covered by lower quality shadow maps). The cascading shadow maps go into the
       6             :  * shadow atlas and are treated the same as any other light (though their size
       7             :  * is typically a sizable portion of the atlas space)
       8             :  */
       9             : #include "../libprimis-headers/cube.h"
      10             : #include "../../shared/geomexts.h"
      11             : #include "../../shared/glexts.h"
      12             : 
      13             : #include "csm.h"
      14             : #include "octarender.h"
      15             : #include "rendergl.h"
      16             : #include "renderlights.h"
      17             : #include "shaderparam.h"
      18             : #include "texture.h"
      19             : 
      20             : #include "interface/cs.h"
      21             : 
      22             : #include "world/light.h"
      23             : 
      24             : cascadedshadowmap csm;
      25             : 
      26             : //====================== cascaded shadow map object ============================//
      27             : 
      28           2 : cascadedshadowmap::cascadedshadowmap() : csmmaxsize(768), csmnearplane(1),
      29           1 :     csmfarplane(1024), csmsplits(3), csmcull(true), csmshadowmap(true),
      30           1 :     csmsplitweight(0.75f), csmpradiustweak(1.f), csmdepthrange(1024.f),
      31           1 :     csmdepthmargin(0.1f), csmbias(1e-4f), csmbias2(2e-4f),
      32           9 :     csmpolyfactor(2), csmpolyfactor2(3), csmpolyoffset(0), csmpolyoffset2(0)
      33             : {
      34           1 : }
      35             : 
      36             : //attempts to set one of the csm properties, subject to bounds coded in this function
      37             : //prints a warning if the bounds were enforced
      38           1 : bool cascadedshadowmap::setcsmproperty(int index, float value)
      39             : {
      40             :     
      41           1 :     switch(index)
      42             :     {
      43           1 :         case MaxSize:
      44             :         {
      45           1 :             csmmaxsize = clampvar(false, "csmmaxsize", value, 256, 2048);
      46           1 :             clearshadowcache();
      47           1 :             return true;
      48             :         }
      49           0 :         case NearPlane:
      50             :         {
      51           0 :             csmnearplane = clampvar(false, "csmnearplane", value, 1, 16);
      52           0 :             return true;
      53             :         }
      54           0 :         case FarPlane:
      55             :         {
      56           0 :             csmfarplane = clampvar(false, "csmfarplane", value, 64, 16384);
      57           0 :             return true;
      58             :         }
      59           0 :         case Cull:
      60             :         {
      61           0 :             csmcull = value;
      62           0 :             return true;
      63             :         }
      64           0 :         case SplitWeight:
      65             :         {
      66           0 :             csmsplitweight = clampfvar("csmsplitweight", value, 0.20f, 0.95f);
      67           0 :             return true;
      68             :         }
      69           0 :         case PRadiusTweak:
      70             :         {
      71           0 :             csmpradiustweak = clampfvar("csmpradiustweak", value, 1e-3f, 1e3f);
      72           0 :             return true;
      73             :         }
      74           0 :         case DepthRange:
      75             :         {
      76           0 :             csmdepthrange = clampfvar("csmdepthrange", value, 0.f, 1e6f);
      77           0 :             return true;
      78             :         }
      79           0 :         case DepthMargin:
      80             :         {
      81           0 :             csmdepthmargin = clampfvar("csmdepthmargin", value, 0.f, 1e3f);
      82           0 :             return true;
      83             :         }
      84           0 :         case Bias:
      85             :         {
      86           0 :             csmbias = clampfvar("csmbias", value, -1e6f, 1e6f);
      87           0 :             return true;
      88             :         }
      89           0 :         case Bias2:
      90             :         {
      91           0 :             csmbias2 = clampfvar("csmbias2", value, -1e16f, 1e6f);
      92           0 :             return true;
      93             :         }
      94           0 :         case Splits:
      95             :         {
      96           0 :             csmsplits = clampvar(false, "csmsplits", value, 1, csmmaxsplits);
      97           0 :             return true;
      98             :         }
      99           0 :         case ShadowMap:
     100             :         {
     101           0 :             csmshadowmap = value;
     102           0 :             return true;
     103             :         }
     104           0 :         case PolyFactor:
     105             :         {
     106           0 :             csmpolyfactor = clampfvar("csmpolyfactor", value, -1e3f, 1e3f);
     107           0 :             return true;
     108             :         }
     109           0 :         case PolyFactor2:
     110             :         {
     111           0 :             csmpolyfactor = clampfvar("csmpolyfactor", value, -1e3f, 1e3f);
     112           0 :             return true;
     113             :         }
     114           0 :         case PolyOffset:
     115             :         {
     116           0 :             csmpolyfactor = clampfvar("csmpolyfactor", value, -1e4f, 1e4f);
     117           0 :             return true;
     118             :         }
     119           0 :         case PolyOffset2:
     120             :         {
     121           0 :             csmpolyfactor = clampfvar("csmpolyfactor", value, -1e4f, 1e4f);
     122           0 :             return true;
     123             :         }
     124           0 :         default:
     125             :         {
     126           0 :             return false;
     127             :         }
     128             :     }
     129             : }
     130             : 
     131             : //returns the csm parameter specified by the index passed, 0 if invalid index
     132           1 : float cascadedshadowmap::getcsmproperty(int index) const
     133             : {
     134           1 :     switch(index)
     135             :     {
     136           1 :         case MaxSize:
     137             :         {
     138           1 :             return csmmaxsize;
     139             :         }
     140           0 :         case NearPlane:
     141             :         {
     142           0 :             return csmnearplane;
     143             :         }
     144           0 :         case FarPlane:
     145             :         {
     146           0 :             return csmfarplane;
     147             :         }
     148           0 :         case Cull:
     149             :         {
     150           0 :             return csmcull;
     151             :         }
     152           0 :         case SplitWeight:
     153             :         {
     154           0 :             return csmsplitweight;
     155             :         }
     156           0 :         case PRadiusTweak:
     157             :         {
     158           0 :             return csmpradiustweak;
     159             :         }
     160           0 :         case DepthRange:
     161             :         {
     162           0 :             return csmdepthrange;
     163             :         }
     164           0 :         case DepthMargin:
     165             :         {
     166           0 :             return csmdepthmargin;
     167             :         }
     168           0 :         case Bias:
     169             :         {
     170           0 :             return csmbias;
     171             :         }
     172           0 :         case Bias2:
     173             :         {
     174           0 :             return csmbias2;
     175             :         }
     176           0 :         case Splits:
     177             :         {
     178           0 :             return csmsplits;
     179             :         }
     180           0 :         case ShadowMap:
     181             :         {
     182           0 :             return csmshadowmap;
     183             :         }
     184           0 :         case PolyFactor:
     185             :         {
     186           0 :             return csmpolyfactor;
     187             :         }
     188           0 :         case PolyFactor2:
     189             :         {
     190           0 :             return csmpolyfactor2;
     191             :         }
     192           0 :         case PolyOffset:
     193             :         {
     194           0 :             return csmpolyoffset;
     195             :         }
     196           0 :         case PolyOffset2:
     197             :         {
     198           0 :             return csmpolyoffset2;
     199             :         }
     200           0 :         default:
     201             :         {
     202           0 :             return 0.f;
     203             :         }
     204             :     }
     205             : }
     206             : 
     207           0 : int cascadedshadowmap::calcbbcsmsplits(const ivec &bbmin, const ivec &bbmax)
     208             : {
     209           0 :     int mask = (1<<csmsplits)-1;
     210           0 :     if(!csmcull)
     211             :     {
     212           0 :         return mask;
     213             :     }
     214           0 :     for(int i = 0; i < csmsplits; ++i)
     215             :     {
     216           0 :         const cascadedshadowmap::splitinfo &split = splits[i];
     217             :         int k;
     218           0 :         for(k = 0; k < 4; k++)
     219             :         {
     220           0 :             const plane &p = split.cull[k];
     221           0 :             ivec omin, omax;
     222           0 :             if(p.x > 0)
     223             :             {
     224           0 :                 omin.x = bbmin.x;
     225           0 :                 omax.x = bbmax.x;
     226             :             }
     227             :             else
     228             :             {
     229           0 :                 omin.x = bbmax.x;
     230           0 :                 omax.x = bbmin.x;
     231             :             }
     232           0 :             if(p.y > 0)
     233             :             {
     234           0 :                 omin.y = bbmin.y;
     235           0 :                 omax.y = bbmax.y;
     236             :             }
     237             :             else
     238             :             {
     239           0 :                 omin.y = bbmax.y;
     240           0 :                 omax.y = bbmin.y;
     241             :             }
     242           0 :             if(p.z > 0)
     243             :             {
     244           0 :                 omin.z = bbmin.z;
     245           0 :                 omax.z = bbmax.z;
     246             :             }
     247             :             else
     248             :             {
     249           0 :                 omin.z = bbmax.z;
     250           0 :                 omax.z = bbmin.z;
     251             :             }
     252           0 :             if(omax.dist(p) < 0)
     253             :             {
     254           0 :                 mask &= ~(1<<i);
     255           0 :                 goto nextsplit;//skip rest and restart loop
     256             :             }
     257           0 :             if(omin.dist(p) < 0)
     258             :             {
     259           0 :                 goto notinside;
     260             :             }
     261             :         }
     262           0 :         mask &= (2<<i)-1;
     263           0 :         break;
     264           0 :     notinside:
     265           0 :         while(++k < 4)
     266             :         {
     267           0 :             const plane &p = split.cull[k];
     268           0 :             ivec omax(p.x > 0 ? bbmax.x : bbmin.x, p.y > 0 ? bbmax.y : bbmin.y, p.z > 0 ? bbmax.z : bbmin.z);
     269           0 :             if(omax.dist(p) < 0)
     270             :             {
     271           0 :                 mask &= ~(1<<i);
     272           0 :                 break;
     273             :             }
     274             :         }
     275           0 :     nextsplit:;
     276             :     }
     277           0 :     return mask;
     278             : }
     279             : 
     280           0 : int cascadedshadowmap::calcspherecsmsplits(const vec &center, float radius) const
     281             : {
     282           0 :     int mask = (1<<csmsplits)-1;
     283           0 :     if(!csmcull)
     284             :     {
     285           0 :         return mask;
     286             :     }
     287           0 :     for(int i = 0; i < csmsplits; ++i)
     288             :     {
     289           0 :         const cascadedshadowmap::splitinfo &split = splits[i];
     290             :         int k;
     291           0 :         for(k = 0; k < 4; k++)
     292             :         {
     293           0 :             const plane &p = split.cull[k];
     294           0 :             float dist = p.dist(center);
     295           0 :             if(dist < -radius)
     296             :             {
     297           0 :                 mask &= ~(1<<i);
     298           0 :                 goto nextsplit; //skip rest and restart loop
     299             :             }
     300           0 :             if(dist < radius)
     301             :             {
     302           0 :                 goto notinside;
     303             :             }
     304             :         }
     305           0 :         mask &= (2<<i)-1;
     306           0 :         break;
     307           0 :     notinside:
     308           0 :         while(++k < 4)
     309             :         {
     310           0 :             const plane &p = split.cull[k];
     311           0 :             if(p.dist(center) < -radius)
     312             :             {
     313           0 :                 mask &= ~(1<<i);
     314           0 :                 break;
     315             :             }
     316             :         }
     317           0 :     nextsplit:;
     318             :     }
     319           0 :     return mask;
     320             : }
     321             : 
     322           0 : void cascadedshadowmap::updatesplitdist()
     323             : {
     324           0 :     float lambda = csmsplitweight,
     325           0 :           nd     = csmnearplane,
     326           0 :           fd     = csmfarplane,
     327           0 :           ratio  = fd/nd;
     328           0 :     splits[0].nearplane = nd;
     329           0 :     for(int i = 1; i < csmsplits; ++i)
     330             :     {
     331           0 :         float si = i / static_cast<float>(csmsplits);
     332           0 :         splits[i].nearplane = lambda*(nd*std::pow(ratio, si)) + (1-lambda)*(nd + (fd - nd)*si);
     333           0 :         splits[i-1].farplane = splits[i].nearplane * 1.005f;
     334             :     }
     335           0 :     splits[csmsplits-1].farplane = fd;
     336           0 : }
     337             : 
     338           0 : void cascadedshadowmap::getmodelmatrix()
     339             : {
     340           0 :     model = viewmatrix;
     341           0 :     model.rotate_around_x(sunlightpitch/RAD);
     342           0 :     model.rotate_around_z((180-sunlightyaw)/RAD);
     343           0 : }
     344             : 
     345           0 : void cascadedshadowmap::getprojmatrix()
     346             : {
     347           0 :     lightview = vec(sunlightdir).neg();
     348             : 
     349             :     // compute the split frustums
     350           0 :     updatesplitdist();
     351             : 
     352             :     // find z extent
     353           0 :     float minz = lightview.project_bb(worldmin, worldmax),
     354           0 :           maxz = lightview.project_bb(worldmax, worldmin),
     355           0 :           zmargin = std::max((maxz - minz)*csmdepthmargin, 0.5f*(csmdepthrange - (maxz - minz)));
     356           0 :     minz -= zmargin;
     357           0 :     maxz += zmargin;
     358             : 
     359             :     // compute each split projection matrix
     360           0 :     for(int i = 0; i < csmsplits; ++i)
     361             :     {
     362           0 :         splitinfo &split = splits[i];
     363           0 :         if(split.idx < 0)
     364             :         {
     365           0 :             continue;
     366             :         }
     367           0 :         const shadowmapinfo &sm = shadowmaps[split.idx];
     368             : 
     369           0 :         vec c;
     370           0 :         float radius = calcfrustumboundsphere(split.nearplane, split.farplane, camera1->o, camdir(), c);
     371             : 
     372             :         // compute the projected bounding box of the sphere
     373           0 :         vec tc;
     374           0 :         model.transform(c, tc);
     375           0 :         int border = smfilter > 2 ? smborder2 : smborder;
     376           0 :         const float pradius = std::ceil(radius * csmpradiustweak),
     377           0 :                     step    = (2*pradius) / (sm.size - 2*border);
     378           0 :         vec2 offset = vec2(tc).sub(pradius).div(step);
     379           0 :         offset.x = std::floor(offset.x);
     380           0 :         offset.y = std::floor(offset.y);
     381           0 :         split.center = vec(vec2(offset).mul(step).add(pradius), -0.5f*(minz + maxz));
     382           0 :         split.bounds = vec(pradius, pradius, 0.5f*(maxz - minz));
     383             : 
     384             :         // modify mvp with a scale and offset
     385             :         // now compute the update model view matrix for this split
     386           0 :         split.scale = vec(1/step, 1/step, -1/(maxz - minz));
     387           0 :         split.offset = vec(border - offset.x, border - offset.y, -minz/(maxz - minz));
     388             : 
     389           0 :         split.proj.identity();
     390           0 :         split.proj.settranslation(2*split.offset.x/sm.size - 1, 2*split.offset.y/sm.size - 1, 2*split.offset.z - 1);
     391           0 :         split.proj.setscale(2*split.scale.x/sm.size, 2*split.scale.y/sm.size, 2*split.scale.z);
     392             :     }
     393           0 : }
     394             : 
     395           0 : void cascadedshadowmap::gencullplanes()
     396             : {
     397           0 :     for(int i = 0; i < csmsplits; ++i)
     398             :     {
     399           0 :         splitinfo &split = splits[i];
     400           0 :         matrix4 mvp;
     401           0 :         mvp.mul(split.proj, model);
     402           0 :         vec4<float> px = mvp.rowx(),
     403           0 :              py = mvp.rowy(),
     404           0 :              pw = mvp.roww();
     405           0 :         split.cull[0] = plane(vec4<float>(pw).add(px)).normalize(); // left plane
     406           0 :         split.cull[1] = plane(vec4<float>(pw).sub(px)).normalize(); // right plane
     407           0 :         split.cull[2] = plane(vec4<float>(pw).add(py)).normalize(); // bottom plane
     408           0 :         split.cull[3] = plane(vec4<float>(pw).sub(py)).normalize(); // top plane
     409             :     }
     410           0 : }
     411             : 
     412           0 : void cascadedshadowmap::bindparams()
     413             : {
     414           0 :     GLOBALPARAM(csmmatrix, matrix3(model));
     415             : 
     416           0 :     static GlobalShaderParam csmtc("csmtc"),
     417           0 :                              csmoffset("csmoffset");
     418           0 :     vec4<float> *csmtcv = csmtc.reserve<vec4<float>>();
     419           0 :     vec  *csmoffsetv = csmoffset.reserve<vec>();
     420           0 :     for(int i = 0; i < csmsplits; ++i)
     421             :     {
     422           0 :         cascadedshadowmap::splitinfo &split = splits[i];
     423           0 :         if(split.idx < 0)
     424             :         {
     425           0 :             continue;
     426             :         }
     427           0 :         const shadowmapinfo &sm = shadowmaps[split.idx];
     428             : 
     429           0 :         csmtcv[i] = vec4<float>(vec2(split.center).mul(-split.scale.x), split.scale.x, split.bounds.x*split.scale.x);
     430             : 
     431           0 :         const float bias = (smfilter > 2 ? csmbias2 : csmbias) * (-512.0f / sm.size) * (split.farplane - split.nearplane) / (splits[0].farplane - splits[0].nearplane);
     432           0 :         csmoffsetv[i] = vec(sm.x, sm.y, 0.5f + bias).add2(0.5f*sm.size);
     433             :     }
     434           0 :     GLOBALPARAMF(csmz, splits[0].center.z*-splits[0].scale.z, splits[0].scale.z);
     435           0 : }
     436             : 
     437           0 : void cascadedshadowmap::setup()
     438             : {
     439           0 :     int size = (csmmaxsize * shadowatlaspacker.dimensions().x) / shadowatlassize;
     440           0 :     for(int i = 0; i < csmsplits; i++)
     441             :     {
     442           0 :         ushort smx = USHRT_MAX,
     443           0 :                smy = USHRT_MAX;
     444           0 :         splits[i].idx = -1;
     445           0 :         if(shadowatlaspacker.insert(smx, smy, size, size))
     446             :         {
     447           0 :             addshadowmap(smx, smy, size, splits[i].idx);
     448             :         }
     449             :     }
     450           0 :     getmodelmatrix();
     451           0 :     getprojmatrix();
     452           0 :     gencullplanes();
     453           0 : }

Generated by: LCOV version 1.14