5354 lines
182 KiB
C++
5354 lines
182 KiB
C++
#include "renderlights.hh"
|
|
|
|
#include <climits>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <shared/command.hh>
|
|
#include <shared/glemu.hh>
|
|
#include <shared/igame.hh>
|
|
|
|
#include "aa.hh"
|
|
#include "dynlight.hh"
|
|
#include "light.hh"
|
|
#include "main.hh" // initwarning, fatal, timings
|
|
#include "material.hh"
|
|
#include "octaedit.hh" // editmode
|
|
#include "octarender.hh"
|
|
#include "pvs.hh"
|
|
#include "rendergl.hh"
|
|
#include "rendermodel.hh"
|
|
#include "renderparticles.hh"
|
|
#include "rendersky.hh"
|
|
#include "renderva.hh"
|
|
#include "stain.hh"
|
|
#include "texture.hh"
|
|
#include "world.hh"
|
|
|
|
#define CHANGE_SHADERS 0
|
|
|
|
int gw = -1, gh = -1, bloomw = -1, bloomh = -1, lasthdraccum = 0;
|
|
GLuint gfbo = 0, gdepthtex = 0, gcolortex = 0, gnormaltex = 0, gglowtex = 0;
|
|
static GLuint gdepthrb = 0, gstencilrb = 0;
|
|
bool gdepthinit = false;
|
|
int scalew = -1, scaleh = -1;
|
|
GLuint scalefbo[2] = { 0, 0 }, scaletex[2] = { 0, 0 };
|
|
GLuint hdrfbo = 0, hdrtex = 0, bloompbo = 0, bloomfbo[6] = { 0, 0, 0, 0, 0, 0 }, bloomtex[6] = { 0, 0, 0, 0, 0, 0 };
|
|
int hdrclear = 0;
|
|
GLuint refractfbo = 0, refracttex = 0;
|
|
GLenum bloomformat = 0, hdrformat = 0, stencilformat = 0;
|
|
bool hdrfloat = false;
|
|
GLuint msfbo = 0, msdepthtex = 0, mscolortex = 0, msnormaltex = 0, msglowtex = 0;
|
|
static GLuint msdepthrb = 0, msstencilrb = 0, mshdrfbo = 0, mshdrtex = 0, msrefractfbo = 0, msrefracttex = 0;
|
|
vector<vec2> msaapositions;
|
|
int aow = -1, aoh = -1;
|
|
GLuint aofbo[4] = { 0, 0, 0, 0 }, aotex[4] = { 0, 0, 0, 0 }, aonoisetex = 0;
|
|
matrix4 eyematrix, worldmatrix, linearworldmatrix, screenmatrix;
|
|
|
|
extern int amd_pf_bug;
|
|
|
|
int gethdrformat(int prec, int fallback = GL_RGB)
|
|
{
|
|
if(prec >= 3 && hasTF) return GL_RGB16F;
|
|
if(prec >= 2 && hasPF && !amd_pf_bug) return GL_R11F_G11F_B10F;
|
|
if(prec >= 1) return GL_RGB10;
|
|
return fallback;
|
|
}
|
|
|
|
extern int bloomsize, bloomprec;
|
|
|
|
void setupbloom(int w, int h)
|
|
{
|
|
int maxsize = ((1<<bloomsize)*5)/4;
|
|
while(w >= maxsize || h >= maxsize)
|
|
{
|
|
w /= 2;
|
|
h /= 2;
|
|
}
|
|
w = max(w, 1);
|
|
h = max(h, 1);
|
|
if(w == bloomw && h == bloomh) return;
|
|
bloomw = w;
|
|
bloomh = h;
|
|
|
|
loopi(5) if(!bloomtex[i]) glGenTextures(1, &bloomtex[i]);
|
|
|
|
loopi(5) if(!bloomfbo[i]) glGenFramebuffers_(1, &bloomfbo[i]);
|
|
|
|
bloomformat = gethdrformat(bloomprec);
|
|
createtexture(bloomtex[0], max(gw/2, bloomw), max(gh/2, bloomh), nullptr, 3, 1, bloomformat, GL_TEXTURE_RECTANGLE);
|
|
createtexture(bloomtex[1], max(gw/4, bloomw), max(gh/4, bloomh), nullptr, 3, 1, bloomformat, GL_TEXTURE_RECTANGLE);
|
|
createtexture(bloomtex[2], bloomw, bloomh, nullptr, 3, 1, GL_RGB, GL_TEXTURE_RECTANGLE);
|
|
createtexture(bloomtex[3], bloomw, bloomh, nullptr, 3, 1, GL_RGB, GL_TEXTURE_RECTANGLE);
|
|
if(bloomformat != GL_RGB)
|
|
{
|
|
if(!bloomtex[5]) glGenTextures(1, &bloomtex[5]);
|
|
if(!bloomfbo[5]) glGenFramebuffers_(1, &bloomfbo[5]);
|
|
createtexture(bloomtex[5], bloomw, bloomh, nullptr, 3, 1, bloomformat, GL_TEXTURE_RECTANGLE);
|
|
}
|
|
|
|
if(hwvtexunits < 4)
|
|
{
|
|
glGenBuffers_(1, &bloompbo);
|
|
glBindBuffer_(GL_PIXEL_PACK_BUFFER, bloompbo);
|
|
glBufferData_(GL_PIXEL_PACK_BUFFER, 4*(hasTF ? sizeof(GLfloat) : sizeof(GLushort))*(hasTRG ? 1 : 3), nullptr, GL_DYNAMIC_COPY);
|
|
glBindBuffer_(GL_PIXEL_PACK_BUFFER, 0);
|
|
}
|
|
|
|
static const uchar gray[12] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 };
|
|
static const float grayf[12] = { 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f };
|
|
createtexture(bloomtex[4], bloompbo ? 4 : 1, 1, hasTF ? (const void *)grayf : (const void *)gray, 3, 1, hasTF ? (hasTRG ? GL_R16F : GL_RGB16F) : (hasTRG ? GL_R16 : GL_RGB16));
|
|
|
|
loopi(5 + (bloomformat != GL_RGB ? 1 : 0))
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, bloomfbo[i]);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, i==4 ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE, bloomtex[i], 0);
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating bloom buffer!");
|
|
}
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void cleanupbloom()
|
|
{
|
|
if(bloompbo) { glDeleteBuffers_(1, &bloompbo); bloompbo = 0; }
|
|
loopi(6) if(bloomfbo[i]) { glDeleteFramebuffers_(1, &bloomfbo[i]); bloomfbo[i] = 0; }
|
|
loopi(6) if(bloomtex[i]) { glDeleteTextures(1, &bloomtex[i]); bloomtex[i] = 0; }
|
|
bloomw = bloomh = -1;
|
|
lasthdraccum = 0;
|
|
}
|
|
|
|
extern int ao, aotaps, aoreduce, aoreducedepth, aonoise, aobilateral, aobilateralupscale, aopackdepth, aodepthformat, aoprec, aoderivnormal;
|
|
|
|
static Shader *bilateralshader[2] = { nullptr, nullptr };
|
|
|
|
Shader *loadbilateralshader(int pass)
|
|
{
|
|
if(!aobilateral) return nullshader;
|
|
|
|
string opts;
|
|
int optslen = 0;
|
|
|
|
bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1),
|
|
upscale = aoreduce && aobilateralupscale,
|
|
reduce = aoreduce && (upscale || (!linear && !aopackdepth));
|
|
if(reduce)
|
|
{
|
|
opts[optslen++] = 'r';
|
|
opts[optslen++] = '0' + aoreduce;
|
|
}
|
|
if(upscale) opts[optslen++] = 'u';
|
|
else if(linear) opts[optslen++] = 'l';
|
|
if(aopackdepth) opts[optslen++] = 'p';
|
|
opts[optslen] = '\0';
|
|
|
|
defformatstring(name, "bilateral%c%s%d", 'x' + pass, opts, aobilateral);
|
|
return generateshader(name, "bilateralshader \"%s\" %d %d", opts, aobilateral, reduce ? aoreduce : 0);
|
|
}
|
|
|
|
void loadbilateralshaders()
|
|
{
|
|
loopk(2) bilateralshader[k] = loadbilateralshader(k);
|
|
}
|
|
|
|
void clearbilateralshaders()
|
|
{
|
|
loopk(2) bilateralshader[k] = nullptr;
|
|
}
|
|
|
|
void setbilateralparams(int radius, float depth)
|
|
{
|
|
float sigma = blursigma*2*radius;
|
|
LOCALPARAMF(bilateralparams, 1.0f/(M_LN2*2*sigma*sigma), 1.0f/(M_LN2*depth*depth));
|
|
}
|
|
|
|
void setbilateralshader(int radius, int pass, float depth)
|
|
{
|
|
bilateralshader[pass]->set();
|
|
setbilateralparams(radius, depth);
|
|
}
|
|
|
|
static Shader *ambientobscuranceshader = nullptr;
|
|
|
|
Shader *loadambientobscuranceshader()
|
|
{
|
|
string opts;
|
|
int optslen = 0;
|
|
|
|
bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1);
|
|
if(linear) opts[optslen++] = 'l';
|
|
if(aoderivnormal) opts[optslen++] = 'd';
|
|
if(aobilateral && aopackdepth) opts[optslen++] = 'p';
|
|
opts[optslen] = '\0';
|
|
|
|
defformatstring(name, "ambientobscurance%s%d", opts, aotaps);
|
|
return generateshader(name, "ambientobscuranceshader \"%s\" %d", opts, aotaps);
|
|
}
|
|
|
|
void loadaoshaders()
|
|
{
|
|
ambientobscuranceshader = loadambientobscuranceshader();
|
|
}
|
|
|
|
void clearaoshaders()
|
|
{
|
|
ambientobscuranceshader = nullptr;
|
|
}
|
|
|
|
void setupao(int w, int h)
|
|
{
|
|
int sw = w>>aoreduce, sh = h>>aoreduce;
|
|
|
|
if(sw == aow && sh == aoh) return;
|
|
|
|
aow = sw;
|
|
aoh = sh;
|
|
|
|
if(!aonoisetex) glGenTextures(1, &aonoisetex);
|
|
bvec *noise = new bvec[(1<<aonoise)*(1<<aonoise)];
|
|
loopk((1<<aonoise)*(1<<aonoise)) noise[k] = bvec(vec(rndscale(2)-1, rndscale(2)-1, 0).normalize());
|
|
createtexture(aonoisetex, 1<<aonoise, 1<<aonoise, noise, 0, 0, GL_RGB, GL_TEXTURE_2D);
|
|
delete[] noise;
|
|
|
|
bool upscale = aoreduce && aobilateral && aobilateralupscale;
|
|
GLenum format = aoprec && hasTRG ? GL_R8 : GL_RGBA8,
|
|
packformat = aobilateral && aopackdepth ? (aodepthformat ? GL_RG16F : GL_RGBA8) : format;
|
|
int packfilter = upscale && aopackdepth && !aodepthformat ? 0 : 1;
|
|
loopi(upscale ? 3 : 2)
|
|
{
|
|
if(!aotex[i]) glGenTextures(1, &aotex[i]);
|
|
if(!aofbo[i]) glGenFramebuffers_(1, &aofbo[i]);
|
|
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);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[i]);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, aotex[i], 0);
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating AO buffer!");
|
|
if(!upscale && packformat == GL_RG16F)
|
|
{
|
|
glClearColor(0, 0, 0, 0);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
}
|
|
|
|
if(aoreducedepth && (aoreduce || aoreducedepth > 1))
|
|
{
|
|
if(!aotex[3]) glGenTextures(1, &aotex[3]);
|
|
if(!aofbo[3]) glGenFramebuffers_(1, &aofbo[3]);
|
|
createtexture(aotex[3], aow, aoh, nullptr, 3, 0, aodepthformat > 1 ? GL_R32F : (aodepthformat ? GL_R16F : GL_RGBA8), GL_TEXTURE_RECTANGLE);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[3]);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, aotex[3], 0);
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating AO buffer!");
|
|
}
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
|
|
loadaoshaders();
|
|
loadbilateralshaders();
|
|
}
|
|
|
|
void cleanupao()
|
|
{
|
|
loopi(4) if(aofbo[i]) { glDeleteFramebuffers_(1, &aofbo[i]); aofbo[i] = 0; }
|
|
loopi(4) if(aotex[i]) { glDeleteTextures(1, &aotex[i]); aotex[i] = 0; }
|
|
if(aonoisetex) { glDeleteTextures(1, &aonoisetex); aonoisetex = 0; }
|
|
aow = aoh = -1;
|
|
|
|
clearaoshaders();
|
|
clearbilateralshaders();
|
|
}
|
|
|
|
VARFP(ao, 0, 1, 1, { cleanupao(); cleardeferredlightshaders(); });
|
|
FVARR(aoradius, 0, 5, 256);
|
|
FVAR(aocutoff, 0, 2.0f, 1e3f);
|
|
FVARR(aodark, 1e-3f, 11.0f, 1e3f);
|
|
FVARR(aosharp, 1e-3f, 1, 1e3f);
|
|
FVAR(aoprefilterdepth, 0, 1, 1e3f);
|
|
FVARR(aomin, 0, 0.25f, 1);
|
|
VARFR(aosun, 0, 1, 1, cleardeferredlightshaders());
|
|
FVARR(aosunmin, 0, 0.5f, 1);
|
|
VARP(aoblur, 0, 4, 7);
|
|
VARP(aoiter, 0, 0, 4);
|
|
VARFP(aoreduce, 0, 1, 2, cleanupao());
|
|
VARF(aoreducedepth, 0, 1, 2, cleanupao());
|
|
VARFP(aofloatdepth, 0, 1, 2, initwarning("AO setup", INIT_LOAD, CHANGE_SHADERS));
|
|
VARFP(aoprec, 0, 1, 1, cleanupao());
|
|
VAR(aodepthformat, 1, 0, 0);
|
|
VARF(aonoise, 0, 5, 8, cleanupao());
|
|
VARFP(aobilateral, 0, 3, 10, cleanupao());
|
|
FVARP(aobilateraldepth, 0, 4, 1e3f);
|
|
VARFP(aobilateralupscale, 0, 0, 1, cleanupao());
|
|
VARF(aopackdepth, 0, 1, 1, cleanupao());
|
|
VARFP(aotaps, 1, 5, 12, cleanupao());
|
|
VARF(aoderivnormal, 0, 0, 1, cleanupao());
|
|
VAR(aoderiv, -1, 1, 1);
|
|
VAR(debugao, 0, 0, 1);
|
|
|
|
void initao()
|
|
{
|
|
aodepthformat = aofloatdepth && hasTRG && hasTF ? aofloatdepth : 0;
|
|
}
|
|
|
|
void viewao()
|
|
{
|
|
if(!ao) return;
|
|
int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
|
|
SETSHADER(hudrect);
|
|
gle::colorf(1, 1, 1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, aotex[2] ? aotex[2] : aotex[0]);
|
|
int tw = aotex[2] ? gw : aow, th = aotex[2] ? gh : aoh;
|
|
debugquad(0, 0, w, h, 0, 0, tw, th);
|
|
}
|
|
|
|
void renderao()
|
|
{
|
|
if(!ao) return;
|
|
|
|
timer *aotimer = begintimer("ambient obscurance");
|
|
|
|
if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
|
|
else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
|
|
|
|
bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1);
|
|
float xscale = eyematrix.a.x, yscale = eyematrix.b.y;
|
|
if(linear)
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[3]);
|
|
glViewport(0, 0, aow, aoh);
|
|
SETSHADER(linearizedepth);
|
|
screenquad(vieww, viewh);
|
|
|
|
xscale *= float(vieww)/aow;
|
|
yscale *= float(viewh)/aoh;
|
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, aotex[3]);
|
|
}
|
|
|
|
ambientobscuranceshader->set();
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[0]);
|
|
glViewport(0, 0, aow, aoh);
|
|
glActiveTexture_(GL_TEXTURE1);
|
|
if(aoderivnormal)
|
|
{
|
|
if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
|
|
else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
|
|
}
|
|
else
|
|
{
|
|
if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
|
|
else glBindTexture(GL_TEXTURE_RECTANGLE, gnormaltex);
|
|
LOCALPARAM(normalmatrix, matrix3(cammatrix));
|
|
}
|
|
glActiveTexture_(GL_TEXTURE2);
|
|
glBindTexture(GL_TEXTURE_2D, aonoisetex);
|
|
glActiveTexture_(GL_TEXTURE0);
|
|
|
|
LOCALPARAMF(tapparams, aoradius*eyematrix.d.z/xscale, aoradius*eyematrix.d.z/yscale, aoradius*aoradius*aocutoff*aocutoff);
|
|
LOCALPARAMF(contrastparams, (2.0f*aodark)/aotaps, aosharp);
|
|
LOCALPARAMF(offsetscale, xscale/eyematrix.d.z, yscale/eyematrix.d.z, eyematrix.d.x/eyematrix.d.z, eyematrix.d.y/eyematrix.d.z);
|
|
LOCALPARAMF(prefilterdepth, aoprefilterdepth);
|
|
|
|
if(aoderiv >= 0) glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, aoderiv ? GL_NICEST : GL_FASTEST);
|
|
screenquad(vieww, viewh, aow/float(1<<aonoise), aoh/float(1<<aonoise));
|
|
if(aoderiv >= 0) glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_DONT_CARE);
|
|
|
|
if(aobilateral)
|
|
{
|
|
if(aoreduce && aobilateralupscale) loopi(2)
|
|
{
|
|
setbilateralshader(aobilateral, i, aobilateraldepth);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[i+1]);
|
|
glViewport(0, 0, vieww, i ? viewh : aoh);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i]);
|
|
glActiveTexture_(GL_TEXTURE1);
|
|
if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
|
|
else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
|
|
glActiveTexture_(GL_TEXTURE0);
|
|
screenquad(vieww, viewh, i ? vieww : aow, aoh);
|
|
}
|
|
else loopi(2 + 2*aoiter)
|
|
{
|
|
setbilateralshader(aobilateral, i%2, aobilateraldepth);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[(i+1)%2]);
|
|
glViewport(0, 0, aow, aoh);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i%2]);
|
|
glActiveTexture_(GL_TEXTURE1);
|
|
if(linear) glBindTexture(GL_TEXTURE_RECTANGLE, aotex[3]);
|
|
else if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
|
|
else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
|
|
glActiveTexture_(GL_TEXTURE0);
|
|
screenquad(vieww, viewh);
|
|
}
|
|
}
|
|
else if(aoblur)
|
|
{
|
|
float blurweights[MAXBLURRADIUS+1], bluroffsets[MAXBLURRADIUS+1];
|
|
setupblurkernel(aoblur, blurweights, bluroffsets);
|
|
loopi(2 + 2*aoiter)
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[(i+1)%2]);
|
|
glViewport(0, 0, aow, aoh);
|
|
setblurshader(i%2, 1, aoblur, blurweights, bluroffsets, GL_TEXTURE_RECTANGLE);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i%2]);
|
|
screenquad(aow, aoh);
|
|
}
|
|
}
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
|
|
glViewport(0, 0, vieww, viewh);
|
|
|
|
endtimer(aotimer);
|
|
}
|
|
|
|
void cleanupscale()
|
|
{
|
|
loopi(2) if(scalefbo[i]) { glDeleteFramebuffers_(1, &scalefbo[i]); scalefbo[i] = 0; }
|
|
loopi(2) if(scaletex[i]) { glDeleteTextures(1, &scaletex[i]); scaletex[i] = 0; }
|
|
scalew = scaleh = -1;
|
|
}
|
|
|
|
extern int gscalecubic, gscalenearest;
|
|
|
|
void setupscale(int sw, int sh, int w, int h)
|
|
{
|
|
scalew = w;
|
|
scaleh = h;
|
|
|
|
loopi(gscalecubic ? 2 : 1)
|
|
{
|
|
if(!scaletex[i]) glGenTextures(1, &scaletex[i]);
|
|
if(!scalefbo[i]) glGenFramebuffers_(1, &scalefbo[i]);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, scalefbo[i]);
|
|
|
|
createtexture(scaletex[i], sw, i ? h : sh, nullptr, 3, gscalecubic || !gscalenearest ? 1 : 0, GL_RGB, GL_TEXTURE_RECTANGLE);
|
|
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, scaletex[i], 0);
|
|
if(!i) bindgdepth();
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating scale buffer!");
|
|
}
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
|
|
if(gscalecubic)
|
|
{
|
|
useshaderbyname("scalecubicx");
|
|
useshaderbyname("scalecubicy");
|
|
}
|
|
}
|
|
|
|
GLuint shouldscale()
|
|
{
|
|
return scalefbo[0];
|
|
}
|
|
|
|
void doscale(GLuint outfbo)
|
|
{
|
|
if(!scaletex[0]) return;
|
|
|
|
timer *scaletimer = begintimer("scaling");
|
|
|
|
if(gscalecubic)
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, scalefbo[1]);
|
|
glViewport(0, 0, gw, hudh);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, scaletex[0]);
|
|
SETSHADER(scalecubicy);
|
|
screenquad(gw, gh);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
|
|
glViewport(0, 0, hudw, hudh);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, scaletex[1]);
|
|
SETSHADER(scalecubicx);
|
|
screenquad(gw, hudh);
|
|
}
|
|
else
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
|
|
glViewport(0, 0, hudw, hudh);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, scaletex[0]);
|
|
SETSHADER(scalelinear);
|
|
screenquad(gw, gh);
|
|
}
|
|
|
|
endtimer(scaletimer);
|
|
}
|
|
|
|
VARFP(glineardepth, 0, 0, 3, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
|
|
VAR(gdepthformat, 1, 0, 0);
|
|
VARF(gstencil, 0, 0, 1, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
|
|
VARF(gdepthstencil, 0, 2, 2, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
|
|
VAR(ghasstencil, 1, 0, 0);
|
|
VARFP(msaa, 0, 0, 16, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
|
|
VARF(msaadepthstencil, 0, 2, 2, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
|
|
VARF(msaastencil, 0, 0, 1, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
|
|
VARF(msaaedgedetect, 0, 1, 1, cleanupgbuffer());
|
|
VARFP(msaalineardepth, -1, -1, 3, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
|
|
VARFP(msaatonemap, 0, 0, 1, cleanupgbuffer());
|
|
VARF(msaatonemapblit, 0, 0, 1, cleanupgbuffer());
|
|
VAR(msaamaxsamples, 1, 0, 0);
|
|
VAR(msaamaxdepthtexsamples, 1, 0, 0);
|
|
VAR(msaamaxcolortexsamples, 1, 0, 0);
|
|
VAR(msaaminsamples, 1, 0, 0);
|
|
VAR(msaasamples, 1, 0, 0);
|
|
VAR(msaalight, 1, 0, 0);
|
|
VARF(msaapreserve, -1, 0, 1, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
|
|
|
|
void checkmsaasamples()
|
|
{
|
|
GLuint tex;
|
|
glGenTextures(1, &tex);
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
|
|
|
|
GLint samples;
|
|
glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaaminsamples, GL_RGBA8, 1, 1, GL_TRUE);
|
|
glGetTexLevelParameteriv(GL_TEXTURE_2D_MULTISAMPLE, 0, GL_TEXTURE_SAMPLES, &samples);
|
|
msaasamples = samples;
|
|
|
|
glDeleteTextures(1, &tex);
|
|
}
|
|
|
|
void initgbuffer()
|
|
{
|
|
msaamaxsamples = msaamaxdepthtexsamples = msaamaxcolortexsamples = msaaminsamples = msaasamples = msaalight = 0;
|
|
msaapositions.setsize(0);
|
|
|
|
if(hasFBMS && hasFBB && hasTMS)
|
|
{
|
|
GLint val;
|
|
glGetIntegerv(GL_MAX_SAMPLES, &val);
|
|
msaamaxsamples = val;
|
|
glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &val);
|
|
msaamaxdepthtexsamples = val;
|
|
glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &val);
|
|
msaamaxcolortexsamples = val;
|
|
}
|
|
|
|
int maxsamples = min(msaamaxsamples, msaamaxcolortexsamples), reqsamples = min(msaa, maxsamples);
|
|
if(reqsamples >= 2)
|
|
{
|
|
msaaminsamples = 2;
|
|
while(msaaminsamples*2 <= reqsamples) msaaminsamples *= 2;
|
|
}
|
|
|
|
int lineardepth = glineardepth;
|
|
if(msaaminsamples)
|
|
{
|
|
if(msaamaxdepthtexsamples < msaaminsamples)
|
|
{
|
|
if(msaalineardepth > 0) lineardepth = msaalineardepth;
|
|
else if(!lineardepth) lineardepth = 1;
|
|
}
|
|
else if(msaalineardepth >= 0) lineardepth = msaalineardepth;
|
|
}
|
|
|
|
if(lineardepth > 1 && (!hasAFBO || !hasTF || !hasTRG)) gdepthformat = 1;
|
|
else gdepthformat = lineardepth;
|
|
|
|
if(msaaminsamples)
|
|
{
|
|
ghasstencil = (msaadepthstencil > 1 || (msaadepthstencil && gdepthformat)) && hasDS ? 2 : (msaastencil ? 1 : 0);
|
|
|
|
checkmsaasamples();
|
|
|
|
if(msaapreserve >= 0) msaalight = hasMSS ? 3 : (msaasamples==2 ? 2 : msaapreserve);
|
|
}
|
|
else ghasstencil = (gdepthstencil > 1 || (gdepthstencil && gdepthformat)) && hasDS ? 2 : (gstencil ? 1 : 0);
|
|
|
|
initao();
|
|
}
|
|
|
|
VARF(forcepacknorm, 0, 0, 1, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
|
|
|
|
bool usepacknorm() { return forcepacknorm || msaasamples || !useavatarmask(); }
|
|
ICOMMAND(usepacknorm, "", (), intret(usepacknorm() ? 1 : 0));
|
|
|
|
void maskgbuffer(const char *mask)
|
|
{
|
|
GLenum drawbufs[4];
|
|
int numbufs = 0;
|
|
while(*mask) switch(*mask++)
|
|
{
|
|
case 'c': drawbufs[numbufs++] = GL_COLOR_ATTACHMENT0; break;
|
|
case 'n': drawbufs[numbufs++] = GL_COLOR_ATTACHMENT1; break;
|
|
case 'd': if(gdepthformat) drawbufs[numbufs++] = GL_COLOR_ATTACHMENT3; break;
|
|
case 'g': drawbufs[numbufs++] = GL_COLOR_ATTACHMENT2; break;
|
|
}
|
|
glDrawBuffers_(numbufs, drawbufs);
|
|
}
|
|
|
|
extern int hdrprec, gscale;
|
|
|
|
void cleanupmsbuffer()
|
|
{
|
|
if(msfbo) { glDeleteFramebuffers_(1, &msfbo); msfbo = 0; }
|
|
if(msdepthtex) { glDeleteTextures(1, &msdepthtex); msdepthtex = 0; }
|
|
if(mscolortex) { glDeleteTextures(1, &mscolortex); mscolortex = 0; }
|
|
if(msnormaltex) { glDeleteTextures(1, &msnormaltex); msnormaltex = 0; }
|
|
if(msglowtex) { glDeleteTextures(1, &msglowtex); msglowtex = 0; }
|
|
if(msstencilrb) { glDeleteRenderbuffers_(1, &msstencilrb); msstencilrb = 0; }
|
|
if(msdepthrb) { glDeleteRenderbuffers_(1, &msdepthrb); msdepthrb = 0; }
|
|
if(mshdrfbo) { glDeleteFramebuffers_(1, &mshdrfbo); mshdrfbo = 0; }
|
|
if(mshdrtex) { glDeleteTextures(1, &mshdrtex); mshdrtex = 0; }
|
|
if(msrefractfbo) { glDeleteFramebuffers_(1, &msrefractfbo); msrefractfbo = 0; }
|
|
if(msrefracttex) { glDeleteTextures(1, &msrefracttex); msrefracttex = 0; }
|
|
}
|
|
|
|
void bindmsdepth()
|
|
{
|
|
if(gdepthformat)
|
|
{
|
|
glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msdepthrb);
|
|
if(ghasstencil > 1) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msdepthrb);
|
|
else if(msaalight && ghasstencil) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msstencilrb);
|
|
}
|
|
else
|
|
{
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
|
|
if(ghasstencil > 1) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
|
|
else if(msaalight && ghasstencil) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msstencilrb);
|
|
}
|
|
}
|
|
|
|
void setupmsbuffer(int w, int h)
|
|
{
|
|
if(!msfbo) glGenFramebuffers_(1, &msfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, msfbo);
|
|
|
|
stencilformat = ghasstencil > 1 ? GL_DEPTH24_STENCIL8 : (ghasstencil ? GL_STENCIL_INDEX8 : 0);
|
|
|
|
if(gdepthformat)
|
|
{
|
|
if(!msdepthrb) glGenRenderbuffers_(1, &msdepthrb);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, msdepthrb);
|
|
glRenderbufferStorageMultisample_(GL_RENDERBUFFER, msaasamples, ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT24, w, h);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, 0);
|
|
}
|
|
if(msaalight && ghasstencil == 1)
|
|
{
|
|
if(!msstencilrb) glGenRenderbuffers_(1, &msstencilrb);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, msstencilrb);
|
|
glRenderbufferStorageMultisample_(GL_RENDERBUFFER, msaasamples, GL_STENCIL_INDEX8, w, h);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, 0);
|
|
}
|
|
|
|
if(!msdepthtex) glGenTextures(1, &msdepthtex);
|
|
if(!mscolortex) glGenTextures(1, &mscolortex);
|
|
if(!msnormaltex) glGenTextures(1, &msnormaltex);
|
|
|
|
maskgbuffer(msaalight ? "cndg" : "cnd");
|
|
|
|
static const GLenum depthformats[] = { GL_RGBA8, GL_R16F, GL_R32F };
|
|
GLenum depthformat = gdepthformat ? depthformats[gdepthformat-1] : (ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT24);
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
|
|
glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, depthformat, w, h, GL_TRUE);
|
|
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mscolortex);
|
|
glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, GL_RGBA8, w, h, GL_TRUE);
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
|
|
glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, GL_RGBA8, w, h, GL_TRUE);
|
|
if(msaalight)
|
|
{
|
|
if(!msglowtex) glGenTextures(1, &msglowtex);
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msglowtex);
|
|
glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, hasAFBO ? hdrformat : GL_RGBA8, w, h, GL_TRUE);
|
|
}
|
|
|
|
bindmsdepth();
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mscolortex, 0);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, msnormaltex, 0);
|
|
if(msaalight) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, msglowtex, 0);
|
|
if(gdepthformat) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
if(msaalight && hasAFBO)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msglowtex);
|
|
glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, GL_RGBA8, w, h, GL_TRUE);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, msglowtex, 0);
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating MSAA g-buffer!");
|
|
}
|
|
else fatal("failed allocating MSAA g-buffer!");
|
|
}
|
|
|
|
glClearColor(0, 0, 0, 0);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (ghasstencil ? GL_STENCIL_BUFFER_BIT : 0));
|
|
|
|
msaapositions.setsize(0);
|
|
loopi(msaasamples)
|
|
{
|
|
GLfloat vals[2];
|
|
glGetMultisamplefv_(GL_SAMPLE_POSITION, i, vals);
|
|
msaapositions.add(vec2(vals[0], vals[1]));
|
|
}
|
|
|
|
if(msaalight)
|
|
{
|
|
if(!mshdrtex) glGenTextures(1, &mshdrtex);
|
|
if(!mshdrfbo) glGenFramebuffers_(1, &mshdrfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, mshdrfbo);
|
|
|
|
bindmsdepth();
|
|
|
|
hdrformat = 0;
|
|
for(int prec = hdrprec; prec >= 0; prec--)
|
|
{
|
|
GLenum format = gethdrformat(prec);
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
|
|
glGetError();
|
|
glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, format, w, h, GL_TRUE);
|
|
if(glGetError() == GL_NO_ERROR)
|
|
{
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mshdrtex, 0);
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
hdrformat = format;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!hdrformat || glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating MSAA HDR buffer!");
|
|
|
|
if(!msrefracttex) glGenTextures(1, &msrefracttex);
|
|
if(!msrefractfbo) glGenFramebuffers_(1, &msrefractfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, msrefractfbo);
|
|
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msrefracttex);
|
|
glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, GL_RGB, w, h, GL_TRUE);
|
|
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, msrefracttex, 0);
|
|
bindmsdepth();
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating MSAA refraction buffer!");
|
|
}
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
|
|
useshaderbyname("msaaedgedetect");
|
|
useshaderbyname("msaaresolve");
|
|
useshaderbyname("msaareducew");
|
|
useshaderbyname("msaareduce");
|
|
if(!msaalight) useshaderbyname("msaaresolvedepth");
|
|
if(msaalight > 1 && msaatonemap)
|
|
{
|
|
useshaderbyname("msaatonemap");
|
|
if(msaalight > 2) useshaderbyname("msaatonemapsample");
|
|
}
|
|
}
|
|
|
|
void bindgdepth()
|
|
{
|
|
if(gdepthformat || msaalight)
|
|
{
|
|
glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gdepthrb);
|
|
if(ghasstencil > 1) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gdepthrb);
|
|
else if(!msaalight || ghasstencil) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gstencilrb);
|
|
}
|
|
else
|
|
{
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
|
|
if(ghasstencil > 1) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
|
|
else if(ghasstencil) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gstencilrb);
|
|
}
|
|
}
|
|
|
|
void setupgbuffer()
|
|
{
|
|
int sw = renderw, sh = renderh;
|
|
if(gscale != 100)
|
|
{
|
|
sw = max((renderw*gscale + 99)/100, 1);
|
|
sh = max((renderh*gscale + 99)/100, 1);
|
|
}
|
|
|
|
if(gw == sw && gh == sh && ((sw >= hudw && sh >= hudh && !scalefbo[0]) || (scalew == hudw && scaleh == hudh))) return;
|
|
|
|
cleanupscale();
|
|
cleanupbloom();
|
|
cleanupao();
|
|
cleanupvolumetric();
|
|
cleanupaa();
|
|
cleanuppostfx();
|
|
|
|
gw = sw;
|
|
gh = sh;
|
|
|
|
hdrformat = gethdrformat(hdrprec);
|
|
stencilformat = ghasstencil > 1 ? GL_DEPTH24_STENCIL8 : (ghasstencil ? GL_STENCIL_INDEX8 : 0);
|
|
|
|
if(msaasamples) setupmsbuffer(gw, gh);
|
|
|
|
hdrfloat = floatformat(hdrformat);
|
|
hdrclear = 3;
|
|
gdepthinit = false;
|
|
|
|
if(gdepthformat || msaalight)
|
|
{
|
|
if(!gdepthrb) glGenRenderbuffers_(1, &gdepthrb);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, gdepthrb);
|
|
glRenderbufferStorage_(GL_RENDERBUFFER, ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT24, gw, gh);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, 0);
|
|
}
|
|
if(!msaalight && ghasstencil == 1)
|
|
{
|
|
if(!gstencilrb) glGenRenderbuffers_(1, &gstencilrb);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, gstencilrb);
|
|
glRenderbufferStorage_(GL_RENDERBUFFER, GL_STENCIL_INDEX8, gw, gh);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, 0);
|
|
}
|
|
|
|
if(!msaalight)
|
|
{
|
|
if(!gdepthtex) glGenTextures(1, &gdepthtex);
|
|
if(!gcolortex) glGenTextures(1, &gcolortex);
|
|
if(!gnormaltex) glGenTextures(1, &gnormaltex);
|
|
if(!gglowtex) glGenTextures(1, &gglowtex);
|
|
if(!gfbo) glGenFramebuffers_(1, &gfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, gfbo);
|
|
|
|
maskgbuffer("cndg");
|
|
|
|
static const GLenum depthformats[] = { GL_RGBA8, GL_R16F, GL_R32F };
|
|
GLenum depthformat = gdepthformat ? depthformats[gdepthformat-1] : (ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT24);
|
|
createtexture(gdepthtex, gw, gh, nullptr, 3, 0, depthformat, GL_TEXTURE_RECTANGLE);
|
|
|
|
createtexture(gcolortex, gw, gh, nullptr, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
|
|
createtexture(gnormaltex, gw, gh, nullptr, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
|
|
createtexture(gglowtex, gw, gh, nullptr, 3, 0, hasAFBO ? hdrformat : GL_RGBA8, GL_TEXTURE_RECTANGLE);
|
|
|
|
bindgdepth();
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, gcolortex, 0);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_RECTANGLE, gnormaltex, 0);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_RECTANGLE, gglowtex, 0);
|
|
if(gdepthformat) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
if(hasAFBO)
|
|
{
|
|
createtexture(gglowtex, gw, gh, nullptr, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_RECTANGLE, gglowtex, 0);
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating g-buffer!");
|
|
}
|
|
else fatal("failed allocating g-buffer!");
|
|
}
|
|
|
|
glClearColor(0, 0, 0, 0);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (ghasstencil ? GL_STENCIL_BUFFER_BIT : 0));
|
|
}
|
|
|
|
if(!hdrtex) glGenTextures(1, &hdrtex);
|
|
if(!hdrfbo) glGenFramebuffers_(1, &hdrfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
|
|
|
|
createtexture(hdrtex, gw, gh, nullptr, 3, 1, hdrformat, GL_TEXTURE_RECTANGLE);
|
|
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, hdrtex, 0);
|
|
bindgdepth();
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating HDR buffer!");
|
|
|
|
if(!msaalight || (msaalight > 2 && msaatonemap && msaatonemapblit))
|
|
{
|
|
if(!refracttex) glGenTextures(1, &refracttex);
|
|
if(!refractfbo) glGenFramebuffers_(1, &refractfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, refractfbo);
|
|
|
|
createtexture(refracttex, gw, gh, nullptr, 3, 0, GL_RGB, GL_TEXTURE_RECTANGLE);
|
|
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, refracttex, 0);
|
|
bindgdepth();
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating refraction buffer!");
|
|
}
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
|
|
if(gw < hudw || gh < hudh) setupscale(gw, gh, hudw, hudh);
|
|
}
|
|
|
|
void cleanupgbuffer()
|
|
{
|
|
if(gfbo) { glDeleteFramebuffers_(1, &gfbo); gfbo = 0; }
|
|
if(gdepthtex) { glDeleteTextures(1, &gdepthtex); gdepthtex = 0; }
|
|
if(gcolortex) { glDeleteTextures(1, &gcolortex); gcolortex = 0; }
|
|
if(gnormaltex) { glDeleteTextures(1, &gnormaltex); gnormaltex = 0; }
|
|
if(gglowtex) { glDeleteTextures(1, &gglowtex); gglowtex = 0; }
|
|
if(gstencilrb) { glDeleteRenderbuffers_(1, &gstencilrb); gstencilrb = 0; }
|
|
if(gdepthrb) { glDeleteRenderbuffers_(1, &gdepthrb); gdepthrb = 0; }
|
|
if(hdrfbo) { glDeleteFramebuffers_(1, &hdrfbo); hdrfbo = 0; }
|
|
if(hdrtex) { glDeleteTextures(1, &hdrtex); hdrtex = 0; }
|
|
if(refractfbo) { glDeleteFramebuffers_(1, &refractfbo); refractfbo = 0; }
|
|
if(refracttex) { glDeleteTextures(1, &refracttex); refracttex = 0; }
|
|
gw = gh = -1;
|
|
cleanupscale();
|
|
cleanupmsbuffer();
|
|
cleardeferredlightshaders();
|
|
}
|
|
|
|
VAR(msaadepthblit, 0, 0, 1);
|
|
|
|
void resolvemsaadepth(int w = vieww, int h = viewh)
|
|
{
|
|
if(!msaasamples || msaalight) return;
|
|
|
|
timer *resolvetimer = drawtex ? nullptr : begintimer("msaa depth resolve");
|
|
|
|
if(msaadepthblit)
|
|
{
|
|
glBindFramebuffer_(GL_READ_FRAMEBUFFER, msfbo);
|
|
glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, gfbo);
|
|
if(ghasstencil) glClear(GL_STENCIL_BUFFER_BIT);
|
|
glBlitFramebuffer_(0, 0, w, h, 0, 0, w, h, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
|
}
|
|
if(!msaadepthblit || gdepthformat)
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, gfbo);
|
|
glViewport(0, 0, w, h);
|
|
maskgbuffer("d");
|
|
if(!msaadepthblit)
|
|
{
|
|
if(ghasstencil)
|
|
{
|
|
glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
|
glEnable(GL_STENCIL_TEST);
|
|
}
|
|
glDepthFunc(GL_ALWAYS);
|
|
SETSHADER(msaaresolvedepth);
|
|
}
|
|
else
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
SETSHADER(msaaresolve);
|
|
}
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
|
|
screenquad();
|
|
maskgbuffer("cnd");
|
|
if(!msaadepthblit)
|
|
{
|
|
if(ghasstencil) glDisable(GL_STENCIL_TEST);
|
|
glDepthFunc(GL_LESS);
|
|
}
|
|
else glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
endtimer(resolvetimer);
|
|
}
|
|
|
|
void resolvemsaacolor(int w = vieww, int h = viewh)
|
|
{
|
|
if(!msaalight) return;
|
|
|
|
timer *resolvetimer = drawtex ? nullptr : begintimer("msaa resolve");
|
|
|
|
glBindFramebuffer_(GL_READ_FRAMEBUFFER, mshdrfbo);
|
|
glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, hdrfbo);
|
|
glBlitFramebuffer_(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
|
|
|
|
endtimer(resolvetimer);
|
|
}
|
|
|
|
FVAR(bloomthreshold, 1e-3f, 0.85f, 1e3f);
|
|
FVARP(bloomscale, 0, 1.0f, 1e3f);
|
|
VARP(bloomblur, 0, 7, 7);
|
|
VARP(bloomiter, 0, 0, 4);
|
|
VARFP(bloomsize, 6, 9, 11, cleanupbloom());
|
|
VARFP(bloomprec, 0, 2, 3, cleanupbloom());
|
|
FVAR(hdraccumscale, 0, 0.98f, 1);
|
|
VAR(hdraccummillis, 1, 33, 1000);
|
|
VAR(hdrreduce, 0, 2, 2);
|
|
VARFP(hdrprec, 0, 2, 3, cleanupgbuffer());
|
|
FVARFP(hdrgamma, 1e-3f, 2, 1e3f, initwarning("HDR setup", INIT_LOAD, CHANGE_SHADERS));
|
|
FVARR(hdrbright, 1e-4f, 1.0f, 1e4f);
|
|
FVAR(hdrsaturate, 1e-3f, 0.85f, 1e3f);
|
|
FVAR(hdrminexposure, 0, 0.03f, 1);
|
|
FVAR(hdrmaxexposure, 0, 0.3f, 1);
|
|
VARFP(gscale, 25, 100, 100, cleanupgbuffer());
|
|
VARFP(gscalecubic, 0, 0, 1, cleanupgbuffer());
|
|
VARFP(gscalenearest, 0, 0, 1, cleanupgbuffer());
|
|
FVARFP(gscalecubicsoft, 0, 0, 1, initwarning("scaling setup", INIT_LOAD, CHANGE_SHADERS));
|
|
|
|
float ldrscale = 1.0f, ldrscaleb = 1.0f/255;
|
|
|
|
void copyhdr(int sw, int sh, GLuint fbo, int dw, int dh, bool flipx, bool flipy, bool swapxy)
|
|
{
|
|
if(!dw) dw = sw;
|
|
if(!dh) dh = sh;
|
|
|
|
if(msaalight) resolvemsaacolor(sw, sh);
|
|
GLERROR;
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, fbo);
|
|
glViewport(0, 0, dw, dh);
|
|
|
|
SETSHADER(reorient);
|
|
vec reorientx(flipx ? -0.5f : 0.5f, 0, 0.5f), reorienty(0, flipy ? -0.5f : 0.5f, 0.5f);
|
|
if(swapxy) swap(reorientx, reorienty);
|
|
reorientx.mul(sw);
|
|
reorienty.mul(sh);
|
|
LOCALPARAM(reorientx, reorientx);
|
|
LOCALPARAM(reorienty, reorienty);
|
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, hdrtex);
|
|
screenquad();
|
|
GLERROR;
|
|
|
|
hdrclear = 3;
|
|
}
|
|
|
|
void loadhdrshaders(int aa)
|
|
{
|
|
switch(aa)
|
|
{
|
|
case AA_LUMA:
|
|
useshaderbyname("hdrtonemapluma");
|
|
useshaderbyname("hdrnopluma");
|
|
if(msaalight > 1 && msaatonemap) useshaderbyname("msaatonemapluma");
|
|
break;
|
|
case AA_MASKED:
|
|
if(!msaasamples && ghasstencil) useshaderbyname("hdrtonemapstencil");
|
|
else
|
|
{
|
|
useshaderbyname("hdrtonemapmasked");
|
|
useshaderbyname("hdrnopmasked");
|
|
if(msaalight > 1 && msaatonemap) useshaderbyname("msaatonemapmasked");
|
|
}
|
|
break;
|
|
case AA_SPLIT:
|
|
useshaderbyname("msaatonemapsplit");
|
|
break;
|
|
case AA_SPLIT_LUMA:
|
|
useshaderbyname("msaatonemapsplitluma");
|
|
break;
|
|
case AA_SPLIT_MASKED:
|
|
useshaderbyname("msaatonemapsplitmasked");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void processhdr(GLuint outfbo, int aa)
|
|
{
|
|
timer *hdrtimer = begintimer("hdr processing");
|
|
|
|
GLOBALPARAMF(hdrparams, hdrbright, hdrsaturate, bloomthreshold, bloomscale);
|
|
|
|
GLuint b0fbo = bloomfbo[1], b0tex = bloomtex[1], b1fbo = bloomfbo[0], b1tex = bloomtex[0], ptex = hdrtex;
|
|
int b0w = max(vieww/4, bloomw), b0h = max(viewh/4, bloomh), b1w = max(vieww/2, bloomw), b1h = max(viewh/2, bloomh),
|
|
pw = vieww, ph = viewh;
|
|
if(msaalight)
|
|
{
|
|
if(aa < AA_SPLIT && (msaalight <= 1 || !msaatonemap))
|
|
{
|
|
glBindFramebuffer_(GL_READ_FRAMEBUFFER, mshdrfbo);
|
|
glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, hdrfbo);
|
|
glBlitFramebuffer_(0, 0, vieww, viewh, 0, 0, vieww, viewh, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
}
|
|
else if(hasFBMSBS && (vieww > bloomw || viewh > bloomh))
|
|
{
|
|
int cw = max(vieww/2, bloomw), ch = max(viewh/2, bloomh);
|
|
glBindFramebuffer_(GL_READ_FRAMEBUFFER, mshdrfbo);
|
|
glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, hdrfbo);
|
|
glBlitFramebuffer_(0, 0, vieww, viewh, 0, 0, cw, ch, GL_COLOR_BUFFER_BIT, GL_SCALED_RESOLVE_FASTEST_EXT);
|
|
pw = cw;
|
|
ph = ch;
|
|
}
|
|
else
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
|
|
if(vieww/2 >= bloomw)
|
|
{
|
|
pw = vieww/2;
|
|
if(viewh/2 >= bloomh)
|
|
{
|
|
ph = viewh/2;
|
|
glViewport(0, 0, pw, ph);
|
|
SETSHADER(msaareduce);
|
|
}
|
|
else
|
|
{
|
|
glViewport(0, 0, pw, viewh);
|
|
SETSHADER(msaareducew);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
glViewport(0, 0, vieww, viewh);
|
|
SETSHADER(msaaresolve);
|
|
}
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
|
|
screenquad(vieww, viewh);
|
|
}
|
|
}
|
|
if(hdrreduce) while(pw > bloomw || ph > bloomh)
|
|
{
|
|
GLuint cfbo = b1fbo, ctex = b1tex;
|
|
int cw = max(pw/2, bloomw), ch = max(ph/2, bloomh);
|
|
|
|
if(hdrreduce > 1 && cw/2 >= bloomw)
|
|
{
|
|
cw /= 2;
|
|
if(ch/2 >= bloomh)
|
|
{
|
|
ch /= 2;
|
|
SETSHADER(hdrreduce2);
|
|
}
|
|
else SETSHADER(hdrreduce2w);
|
|
}
|
|
else SETSHADER(hdrreduce);
|
|
if(cw == bloomw && ch == bloomh) { if(bloomfbo[5]) { cfbo = bloomfbo[5]; ctex = bloomtex[5]; } else { cfbo = bloomfbo[2]; ctex = bloomtex[2]; } }
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, cfbo);
|
|
glViewport(0, 0, cw, ch);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, ptex);
|
|
screenquad(pw, ph);
|
|
|
|
ptex = ctex;
|
|
pw = cw;
|
|
ph = ch;
|
|
swap(b0fbo, b1fbo);
|
|
swap(b0tex, b1tex);
|
|
swap(b0w, b1w);
|
|
swap(b0h, b1h);
|
|
}
|
|
|
|
if(!lasthdraccum || lastmillis - lasthdraccum >= hdraccummillis)
|
|
{
|
|
GLuint ltex = ptex;
|
|
int lw = pw, lh = ph;
|
|
for(int i = 0; lw > 2 || lh > 2; i++)
|
|
{
|
|
int cw = max(lw/2, 2), ch = max(lh/2, 2);
|
|
|
|
if(hdrreduce > 1 && cw/2 >= 2)
|
|
{
|
|
cw /= 2;
|
|
if(ch/2 >= 2)
|
|
{
|
|
ch /= 2;
|
|
if(i) SETSHADER(hdrreduce2); else SETSHADER(hdrluminance2);
|
|
}
|
|
else if(i) SETSHADER(hdrreduce2w); else SETSHADER(hdrluminance2w);
|
|
}
|
|
else if(i) SETSHADER(hdrreduce); else SETSHADER(hdrluminance);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, b1fbo);
|
|
glViewport(0, 0, cw, ch);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, ltex);
|
|
screenquad(lw, lh);
|
|
|
|
ltex = b1tex;
|
|
lw = cw;
|
|
lh = ch;
|
|
swap(b0fbo, b1fbo);
|
|
swap(b0tex, b1tex);
|
|
swap(b0w, b1w);
|
|
swap(b0h, b1h);
|
|
}
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, bloomfbo[4]);
|
|
glViewport(0, 0, bloompbo ? 4 : 1, 1);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
|
|
SETSHADER(hdraccum);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
|
|
LOCALPARAMF(accumscale, lasthdraccum ? pow(hdraccumscale, float(lastmillis - lasthdraccum)/hdraccummillis) : 0);
|
|
screenquad(2, 2);
|
|
glDisable(GL_BLEND);
|
|
|
|
if(bloompbo)
|
|
{
|
|
glBindBuffer_(GL_PIXEL_PACK_BUFFER, bloompbo);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
glReadPixels(0, 0, 4, 1, hasTRG ? GL_RED : GL_RGB, hasTF ? GL_FLOAT : GL_UNSIGNED_SHORT, nullptr);
|
|
glBindBuffer_(GL_PIXEL_PACK_BUFFER, 0);
|
|
}
|
|
|
|
lasthdraccum = lastmillis;
|
|
}
|
|
|
|
if(bloompbo)
|
|
{
|
|
gle::bindvbo(bloompbo);
|
|
gle::enablecolor();
|
|
gle::colorpointer(hasTF ? sizeof(GLfloat) : sizeof(GLushort), (const void *)0, hasTF ? GL_FLOAT : GL_UNSIGNED_SHORT, 1);
|
|
gle::clearvbo();
|
|
}
|
|
|
|
b0fbo = bloomfbo[3];
|
|
b0tex = bloomtex[3];
|
|
b1fbo = bloomfbo[2];
|
|
b1tex = bloomtex[2];
|
|
b0w = b1w = bloomw;
|
|
b0h = b1h = bloomh;
|
|
|
|
glActiveTexture_(GL_TEXTURE2);
|
|
glBindTexture(GL_TEXTURE_2D, bloomtex[4]);
|
|
glActiveTexture_(GL_TEXTURE0);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, b0fbo);
|
|
glViewport(0, 0, b0w, b0h);
|
|
SETSHADER(hdrbloom);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, ptex);
|
|
screenquad(pw, ph);
|
|
|
|
if(bloomblur)
|
|
{
|
|
float blurweights[MAXBLURRADIUS+1], bluroffsets[MAXBLURRADIUS+1];
|
|
setupblurkernel(bloomblur, blurweights, bluroffsets);
|
|
loopi(2 + 2*bloomiter)
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, b1fbo);
|
|
glViewport(0, 0, b1w, b1h);
|
|
setblurshader(i%2, 1, bloomblur, blurweights, bluroffsets, GL_TEXTURE_RECTANGLE);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
|
|
screenquad(b0w, b0h);
|
|
swap(b0w, b1w);
|
|
swap(b0h, b1h);
|
|
swap(b0tex, b1tex);
|
|
swap(b0fbo, b1fbo);
|
|
}
|
|
}
|
|
|
|
if(aa >= AA_SPLIT)
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
|
|
glViewport(0, 0, vieww, viewh);
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
|
|
glActiveTexture_(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
|
|
glActiveTexture_(GL_TEXTURE0);
|
|
switch(aa)
|
|
{
|
|
case AA_SPLIT_LUMA: SETSHADER(msaatonemapsplitluma); break;
|
|
case AA_SPLIT_MASKED:
|
|
SETSHADER(msaatonemapsplitmasked);
|
|
setaavelocityparams(GL_TEXTURE3);
|
|
break;
|
|
default: SETSHADER(msaatonemapsplit); break;
|
|
}
|
|
screenquad(vieww, viewh, b0w, b0h);
|
|
}
|
|
else if(msaalight <= 1 || !msaatonemap)
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
|
|
glViewport(0, 0, vieww, viewh);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, hdrtex);
|
|
glActiveTexture_(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
|
|
glActiveTexture_(GL_TEXTURE0);
|
|
switch(aa)
|
|
{
|
|
case AA_LUMA: SETSHADER(hdrtonemapluma); break;
|
|
case AA_MASKED:
|
|
if(!msaasamples && ghasstencil)
|
|
{
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
glStencilFunc(GL_EQUAL, 0, 0x80);
|
|
glEnable(GL_STENCIL_TEST);
|
|
SETSHADER(hdrtonemap);
|
|
screenquad(vieww, viewh, b0w, b0h);
|
|
|
|
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
|
SETSHADER(hdrtonemapstencil);
|
|
screenquad(vieww, viewh, b0w, b0h);
|
|
glDisable(GL_STENCIL_TEST);
|
|
goto done;
|
|
}
|
|
SETSHADER(hdrtonemapmasked);
|
|
setaavelocityparams(GL_TEXTURE3);
|
|
break;
|
|
default: SETSHADER(hdrtonemap); break;
|
|
}
|
|
screenquad(vieww, viewh, b0w, b0h);
|
|
}
|
|
else
|
|
{
|
|
bool blit = msaalight > 2 && msaatonemapblit && (!aa || !outfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, blit ? msrefractfbo : outfbo);
|
|
glViewport(0, 0, vieww, viewh);
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
|
|
glActiveTexture_(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
|
|
glActiveTexture_(GL_TEXTURE0);
|
|
|
|
if(blit) SETSHADER(msaatonemapsample);
|
|
else switch(aa)
|
|
{
|
|
case AA_LUMA: SETSHADER(msaatonemapluma); break;
|
|
case AA_MASKED:
|
|
SETSHADER(msaatonemapmasked);
|
|
setaavelocityparams(GL_TEXTURE3);
|
|
break;
|
|
default: SETSHADER(msaatonemap); break;
|
|
}
|
|
screenquad(vieww, viewh, b0w, b0h);
|
|
|
|
if(blit)
|
|
{
|
|
glBindFramebuffer_(GL_READ_FRAMEBUFFER, msrefractfbo);
|
|
glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, aa || !outfbo ? refractfbo : outfbo);
|
|
glBlitFramebuffer_(0, 0, vieww, viewh, 0, 0, vieww, viewh, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
|
|
if(!outfbo)
|
|
{
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
|
|
glViewport(0, 0, vieww, viewh);
|
|
if(!blit) SETSHADER(hdrnop);
|
|
else switch(aa)
|
|
{
|
|
case AA_LUMA: SETSHADER(hdrnopluma); break;
|
|
case AA_MASKED:
|
|
SETSHADER(hdrnopmasked);
|
|
setaavelocityparams(GL_TEXTURE3);
|
|
break;
|
|
default: SETSHADER(hdrnop); break;
|
|
}
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, refracttex);
|
|
screenquad(vieww, viewh);
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
if(bloompbo) gle::disablecolor();
|
|
|
|
endtimer(hdrtimer);
|
|
}
|
|
|
|
static void getavglum(int *numargs, ident *id)
|
|
{
|
|
if(!bloomfbo[4]) return;
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, bloomfbo[4]);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
float avglum = -1;
|
|
glReadPixels(0, 0, 1, 1, GL_RED, GL_FLOAT, &avglum);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
if(avglum < 0) return;
|
|
avglum *= 4;
|
|
if(*numargs < 0) floatret(avglum);
|
|
else printfvar(id, avglum);
|
|
}
|
|
|
|
COMMANDN(avglum, getavglum, "N$");
|
|
|
|
VAR(debugbloom, 0, 0, 1);
|
|
|
|
static void viewbloom()
|
|
{
|
|
int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
|
|
SETSHADER(hudrect);
|
|
gle::colorf(1, 1, 1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, bloomtex[3]);
|
|
debugquad(0, 0, w, h, 0, 0, bloomw, bloomh);
|
|
}
|
|
|
|
VAR(debugdepth, 0, 0, 1);
|
|
|
|
void viewdepth()
|
|
{
|
|
int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
|
|
SETSHADER(hudrect);
|
|
gle::colorf(1, 1, 1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
|
|
debugquad(0, 0, w, h, 0, 0, gw, gh);
|
|
}
|
|
|
|
VAR(debugstencil, 0, 0, 0xFF);
|
|
|
|
void viewstencil()
|
|
{
|
|
if(!ghasstencil || !hdrfbo) return;
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
|
|
glViewport(0, 0, gw, gh);
|
|
|
|
glClearColor(0, 0, 0, 0);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glStencilFunc(GL_NOTEQUAL, 0, debugstencil);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
glEnable(GL_STENCIL_TEST);
|
|
SETSHADER(hudnotexture);
|
|
gle::colorf(1, 1, 1);
|
|
debugquad(0, 0, hudw, hudh, 0, 0, gw, gh);
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
glViewport(0, 0, hudw, hudh);
|
|
|
|
int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
|
|
SETSHADER(hudrect);
|
|
gle::colorf(1, 1, 1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, hdrtex);
|
|
debugquad(0, 0, w, h, 0, 0, gw, gh);
|
|
}
|
|
|
|
VAR(debugrefract, 0, 0, 1);
|
|
|
|
void viewrefract()
|
|
{
|
|
int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
|
|
SETSHADER(hudrect);
|
|
gle::colorf(1, 1, 1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, refracttex);
|
|
debugquad(0, 0, w, h, 0, 0, gw, gh);
|
|
}
|
|
|
|
#define RH_MAXSPLITS 4
|
|
#define RH_MAXGRID 64
|
|
|
|
GLuint rhtex[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }, rhrb[4] = { 0, 0, 0, 0 }, rhfbo = 0;
|
|
uint rhclearmasks[2][RH_MAXSPLITS][(RH_MAXGRID+2+31)/32];
|
|
GLuint rsmdepthtex = 0, rsmcolortex = 0, rsmnormaltex = 0, rsmfbo = 0;
|
|
|
|
extern int rhrect, rhgrid, rhsplits, rhborder, rhprec, rhtaps, rhcache, rhforce, rsmprec, rsmdepthprec, rsmsize;
|
|
|
|
static Shader *radiancehintsshader = nullptr;
|
|
Shader *rsmworldshader = nullptr;
|
|
|
|
Shader *loadradiancehintsshader()
|
|
{
|
|
defformatstring(name, "radiancehints%d", rhtaps);
|
|
return generateshader(name, "radiancehintsshader %d", rhtaps);
|
|
}
|
|
|
|
void loadrhshaders()
|
|
{
|
|
if(rhborder) useshaderbyname("radiancehintsborder");
|
|
if(rhcache) useshaderbyname("radiancehintscached");
|
|
useshaderbyname("radiancehintsdisable");
|
|
radiancehintsshader = loadradiancehintsshader();
|
|
rsmworldshader = useshaderbyname("rsmworld");
|
|
useshaderbyname("rsmsky");
|
|
}
|
|
|
|
void clearrhshaders()
|
|
{
|
|
radiancehintsshader = nullptr;
|
|
rsmworldshader = nullptr;
|
|
}
|
|
|
|
void setupradiancehints()
|
|
{
|
|
GLenum rhformat = hasTF && rhprec >= 1 ? GL_RGBA16F : GL_RGBA8;
|
|
|
|
loopi(!rhrect && rhcache ? 8 : 4)
|
|
{
|
|
if(!rhtex[i]) glGenTextures(1, &rhtex[i]);
|
|
create3dtexture(rhtex[i], rhgrid+2*rhborder, rhgrid+2*rhborder, (rhgrid+2*rhborder)*rhsplits, nullptr, 7, 1, rhformat);
|
|
if(rhborder)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
|
|
GLfloat border[4] = { 0.5f, 0.5f, 0.5f, 0 };
|
|
glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, border);
|
|
}
|
|
}
|
|
|
|
if(!rhfbo) glGenFramebuffers_(1, &rhfbo);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, rhfbo);
|
|
|
|
if(rhrect) loopi(4)
|
|
{
|
|
if(!rhrb[i]) glGenRenderbuffers_(1, &rhrb[i]);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, rhrb[i]);
|
|
glRenderbufferStorage_(GL_RENDERBUFFER, rhformat, (rhgrid + 2*rhborder)*(rhgrid + 2*rhborder), (rhgrid + 2*rhborder)*rhsplits);
|
|
glBindRenderbuffer_(GL_RENDERBUFFER, 0);
|
|
glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER, rhrb[i]);
|
|
}
|
|
else loopi(4) glFramebufferTexture3D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_3D, rhtex[i], 0, 0);
|
|
|
|
static const GLenum drawbufs[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
|
|
glDrawBuffers_(4, drawbufs);
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating radiance hints buffer!");
|
|
|
|
if(!rsmdepthtex) glGenTextures(1, &rsmdepthtex);
|
|
if(!rsmcolortex) glGenTextures(1, &rsmcolortex);
|
|
if(!rsmnormaltex) glGenTextures(1, &rsmnormaltex);
|
|
|
|
if(!rsmfbo) glGenFramebuffers_(1, &rsmfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, rsmfbo);
|
|
|
|
GLenum rsmformat = gethdrformat(rsmprec, GL_RGBA8);
|
|
|
|
createtexture(rsmdepthtex, rsmsize, rsmsize, nullptr, 3, 0, rsmdepthprec > 1 ? GL_DEPTH_COMPONENT32 : (rsmdepthprec ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16), GL_TEXTURE_RECTANGLE);
|
|
createtexture(rsmcolortex, rsmsize, rsmsize, nullptr, 3, 0, rsmformat, GL_TEXTURE_RECTANGLE);
|
|
createtexture(rsmnormaltex, rsmsize, rsmsize, nullptr, 3, 0, rsmformat, GL_TEXTURE_RECTANGLE);
|
|
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, rsmdepthtex, 0);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, rsmcolortex, 0);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_RECTANGLE, rsmnormaltex, 0);
|
|
|
|
glDrawBuffers_(2, drawbufs);
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating RSM buffer!");
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
|
|
loadrhshaders();
|
|
|
|
clearradiancehintscache();
|
|
}
|
|
|
|
void cleanupradiancehints()
|
|
{
|
|
clearradiancehintscache();
|
|
|
|
loopi(8) if(rhtex[i]) { glDeleteTextures(1, &rhtex[i]); rhtex[i] = 0; }
|
|
loopi(4) if(rhrb[i]) { glDeleteRenderbuffers_(1, &rhrb[i]); rhrb[i] = 0; }
|
|
if(rhfbo) { glDeleteFramebuffers_(1, &rhfbo); rhfbo = 0; }
|
|
if(rsmdepthtex) { glDeleteTextures(1, &rsmdepthtex); rsmdepthtex = 0; }
|
|
if(rsmcolortex) { glDeleteTextures(1, &rsmcolortex); rsmcolortex = 0; }
|
|
if(rsmnormaltex) { glDeleteTextures(1, &rsmnormaltex); rsmnormaltex = 0; }
|
|
if(rsmfbo) { glDeleteFramebuffers_(1, &rsmfbo); rsmfbo = 0; }
|
|
|
|
clearrhshaders();
|
|
}
|
|
|
|
VARF(rhrect, 0, 0, 1, cleanupradiancehints());
|
|
VARF(rhsplits, 1, 2, RH_MAXSPLITS, { cleardeferredlightshaders(); cleanupradiancehints(); });
|
|
VARF(rhborder, 0, 1, 1, cleanupradiancehints());
|
|
VARF(rsmsize, 64, 384, 2048, cleanupradiancehints());
|
|
VARF(rhnearplane, 1, 1, 16, clearradiancehintscache());
|
|
VARF(rhfarplane, 64, 1024, 16384, clearradiancehintscache());
|
|
FVARF(rsmpradiustweak, 1e-3f, 1, 1e3f, clearradiancehintscache());
|
|
FVARF(rhpradiustweak, 1e-3f, 1, 1e3f, clearradiancehintscache());
|
|
FVARF(rsmdepthrange, 0, 1024, 1e6f, clearradiancehintscache());
|
|
FVARF(rsmdepthmargin, 0, 0.1f, 1e3f, clearradiancehintscache());
|
|
VARFP(rhprec, 0, 0, 1, cleanupradiancehints());
|
|
VARFP(rsmprec, 0, 0, 3, cleanupradiancehints());
|
|
VARFP(rsmdepthprec, 0, 0, 2, cleanupradiancehints());
|
|
FVAR(rhnudge, 0, 0.5f, 4);
|
|
FVARF(rhworldbias, 0, 0.5f, 10, clearradiancehintscache());
|
|
FVARF(rhsplitweight, 0.20f, 0.6f, 0.95f, clearradiancehintscache());
|
|
VARF(rhgrid, 3, 27, RH_MAXGRID, cleanupradiancehints());
|
|
FVARF(rsmspread, 0, 0.15f, 1, clearradiancehintscache());
|
|
VAR(rhclipgrid, 0, 1, 1);
|
|
VARF(rhcache, 0, 1, 1, cleanupradiancehints());
|
|
VARF(rhforce, 0, 0, 1, cleanupradiancehints());
|
|
VAR(rsmcull, 0, 1, 1);
|
|
VARFP(rhtaps, 0, 20, 32, cleanupradiancehints());
|
|
VAR(rhdyntex, 0, 0, 1);
|
|
VAR(rhdynmm, 0, 0, 1);
|
|
VARFR(gidist, 0, 384, 1024, { clearradiancehintscache(); cleardeferredlightshaders(); if(!gidist) cleanupradiancehints(); });
|
|
FVARFR(giscale, 0, 1.5f, 1e3f, { cleardeferredlightshaders(); if(!giscale) cleanupradiancehints(); });
|
|
FVARR(giaoscale, 0, 3, 1e3f);
|
|
VARFP(gi, 0, 1, 1, { cleardeferredlightshaders(); cleanupradiancehints(); });
|
|
|
|
VAR(debugrsm, 0, 0, 2);
|
|
void viewrsm()
|
|
{
|
|
int w = min(hudw, hudh)/2, h = (w*hudh)/hudw, x = hudw-w, y = hudh-h;
|
|
SETSHADER(hudrect);
|
|
gle::colorf(1, 1, 1);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, debugrsm == 2 ? rsmnormaltex : rsmcolortex);
|
|
debugquad(x, y, w, h, 0, 0, rsmsize, rsmsize);
|
|
}
|
|
|
|
VAR(debugrh, -1, 0, RH_MAXSPLITS*(RH_MAXGRID + 2));
|
|
void viewrh()
|
|
{
|
|
int w = min(hudw, hudh)/2, h = (w*hudh)/hudw, x = hudw-w, y = hudh-h;
|
|
gle::colorf(1, 1, 1);
|
|
if(debugrh < 0 && rhrect)
|
|
{
|
|
SETSHADER(hudrect);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE, rhtex[5]);
|
|
float tw = (rhgrid+2*rhborder)*(rhgrid+2*rhborder), th = (rhgrid+2*rhborder)*rhsplits;
|
|
gle::defvertex(2);
|
|
gle::deftexcoord0(2);
|
|
gle::begin(GL_TRIANGLE_STRIP);
|
|
gle::attribf(x, y); gle::attribf(0, 0);
|
|
gle::attribf(x+w, y); gle::attribf(tw, 0);
|
|
gle::attribf(x, y+h); gle::attribf(0, th);
|
|
gle::attribf(x+w, y+h); gle::attribf(tw, th);
|
|
gle::end();
|
|
}
|
|
else
|
|
{
|
|
SETSHADER(hud3d);
|
|
glBindTexture(GL_TEXTURE_3D, rhtex[1]);
|
|
float z = (max(debugrh, 1)-1+0.5f)/float((rhgrid+2*rhborder)*rhsplits);
|
|
gle::defvertex(2);
|
|
gle::deftexcoord0(3);
|
|
gle::begin(GL_TRIANGLE_STRIP);
|
|
gle::attribf(x, y); gle::attribf(0, 0, z);
|
|
gle::attribf(x+w, y); gle::attribf(1, 0, z);
|
|
gle::attribf(x, y+h); gle::attribf(0, 1, z);
|
|
gle::attribf(x+w, y+h); gle::attribf(1, 1, z);
|
|
gle::end();
|
|
}
|
|
}
|
|
|
|
#define SHADOWATLAS_SIZE 4096
|
|
|
|
PackNode shadowatlaspacker(0, 0, SHADOWATLAS_SIZE, SHADOWATLAS_SIZE);
|
|
|
|
extern int smminradius;
|
|
|
|
struct lightinfo
|
|
{
|
|
int ent, shadowmap;
|
|
ushort flags, batched;
|
|
vec o, color;
|
|
float radius, dist;
|
|
vec dir, spotx, spoty;
|
|
int spot;
|
|
float sx1, sy1, sx2, sy2, sz1, sz2;
|
|
occludequery *query;
|
|
|
|
lightinfo() {}
|
|
lightinfo(const vec &o, const vec &color, float radius, ushort flags = 0, const vec &dir = vec(0, 0, 0), int spot = 0)
|
|
: ent(-1), shadowmap(-1), flags(flags), batched(~0),
|
|
o(o), color(color), radius(radius), dist(camera1->o.dist(o)),
|
|
dir(dir), spot(spot), query(nullptr)
|
|
{
|
|
if(spot > 0) calcspot();
|
|
calcscissor();
|
|
}
|
|
lightinfo(int i, const extentity &e)
|
|
: ent(i), shadowmap(-1), flags(e.attr5), batched(~0),
|
|
o(e.o), color(vec(e.attr2, e.attr3, e.attr4).max(0)), radius(e.attr1), dist(camera1->o.dist(e.o)),
|
|
dir(0, 0, 0), spot(0), query(nullptr)
|
|
{
|
|
if(e.attached && e.attached->type == ET_SPOTLIGHT)
|
|
{
|
|
dir = vec(e.attached->o).sub(e.o).normalize();
|
|
spot = std::clamp(int(e.attached->attr1), 1, 89);
|
|
calcspot();
|
|
}
|
|
calcscissor();
|
|
}
|
|
|
|
void calcspot()
|
|
{
|
|
quat orient(dir, vec(0, 0, dir.z < 0 ? -1 : 1));
|
|
spotx = orient.invertedrotate(vec(1, 0, 0));
|
|
spoty = orient.invertedrotate(vec(0, 1, 0));
|
|
}
|
|
|
|
bool noshadow() const { return flags&L_NOSHADOW || radius <= smminradius; }
|
|
bool nospec() const { return (flags&L_NOSPEC) != 0; }
|
|
bool volumetric() const { return (flags&L_VOLUMETRIC) != 0; }
|
|
bool colorshadow() const { return (flags&L_SMALPHA) != 0; }
|
|
|
|
void addscissor(float &dx1, float &dy1, float &dx2, float &dy2) const
|
|
{
|
|
dx1 = min(dx1, sx1);
|
|
dy1 = min(dy1, sy1);
|
|
dx2 = max(dx2, sx2);
|
|
dy2 = max(dy2, sy2);
|
|
}
|
|
|
|
void addscissor(float &dx1, float &dy1, float &dx2, float &dy2, float &dz1, float &dz2) const
|
|
{
|
|
addscissor(dx1, dy1, dx2, dy2);
|
|
dz1 = min(dz1, sz1);
|
|
dz2 = max(dz2, sz2);
|
|
}
|
|
|
|
bool validscissor() const { return sx1 < sx2 && sy1 < sy2 && sz1 < sz2; }
|
|
|
|
void calcscissor()
|
|
{
|
|
sx1 = sy1 = sz1 = -1;
|
|
sx2 = sy2 = sz2 = 1;
|
|
if(spot > 0) calcspotscissor(o, radius, dir, spot, spotx, spoty, sx1, sy1, sx2, sy2, sz1, sz2);
|
|
else calcspherescissor(o, radius, sx1, sy1, sx2, sy2, sz1, sz2);
|
|
}
|
|
|
|
bool checkquery() const { return query && query->owner == this && ::checkquery(query); }
|
|
|
|
void calcbb(vec &bbmin, vec &bbmax)
|
|
{
|
|
if(spot > 0)
|
|
{
|
|
float spotscale = radius * tan360(spot);
|
|
vec up = vec(spotx).mul(spotscale).abs(), right = vec(spoty).mul(spotscale).abs(), center = vec(dir).mul(radius).add(o);
|
|
bbmin = bbmax = center;
|
|
bbmin.sub(up).sub(right);
|
|
bbmax.add(up).add(right);
|
|
bbmin.min(o);
|
|
bbmax.max(o);
|
|
}
|
|
else
|
|
{
|
|
bbmin = vec(o).sub(radius);
|
|
bbmax = vec(o).add(radius);
|
|
}
|
|
}
|
|
};
|
|
|
|
struct shadowcachekey
|
|
{
|
|
vec o;
|
|
float radius;
|
|
vec dir;
|
|
int spot;
|
|
|
|
shadowcachekey() {}
|
|
shadowcachekey(const lightinfo &l) : o(l.o), radius(l.radius), dir(l.dir), spot(l.spot) {}
|
|
};
|
|
|
|
static inline uint hthash(const shadowcachekey &k)
|
|
{
|
|
return hthash(k.o);
|
|
}
|
|
|
|
static inline bool htcmp(const shadowcachekey &x, const shadowcachekey &y)
|
|
{
|
|
return x.o == y.o && x.radius == y.radius && x.dir == y.dir && x.spot == y.spot;
|
|
}
|
|
|
|
struct shadowcacheval;
|
|
|
|
struct shadowmapinfo
|
|
{
|
|
ushort x, y, size;
|
|
uchar sidemask, transparent;
|
|
int light;
|
|
shadowcacheval *cached;
|
|
};
|
|
|
|
struct shadowcacheval
|
|
{
|
|
ushort x, y, size;
|
|
uchar sidemask, transparent;
|
|
|
|
shadowcacheval() {}
|
|
shadowcacheval(const shadowmapinfo &sm) : x(sm.x), y(sm.y), size(sm.size), sidemask(sm.sidemask), transparent(sm.transparent) {}
|
|
};
|
|
|
|
struct shadowcache : hashtable<shadowcachekey, shadowcacheval>
|
|
{
|
|
shadowcache() : hashtable<shadowcachekey, shadowcacheval>(256) {}
|
|
|
|
void reset()
|
|
{
|
|
clear();
|
|
}
|
|
};
|
|
|
|
extern int smcache, smfilter, smgather, smalpha, smalphaprec, alphashadow;
|
|
|
|
#define SHADOWCACHE_EVICT 2
|
|
|
|
GLuint shadowatlastex = 0, shadowatlasfbo = 0;
|
|
GLuint shadowcolortex = 0, shadowblanktex = 0;
|
|
GLuint shadowfiltertex = 0, shadowfilterfbo = 0;
|
|
GLenum shadowatlastarget = GL_NONE;
|
|
vector<uint> shadowcolorclears, shadowcolorblurs;
|
|
int smalign = 0;
|
|
shadowcache shadowcache;
|
|
bool shadowcachefull = false;
|
|
int evictshadowcache = 0;
|
|
Shader *smalphaworldshader = nullptr;
|
|
|
|
extern int usetexgather;
|
|
static inline bool usegatherforsm() { return smfilter > 1 && smgather && hasTG && usetexgather; }
|
|
static inline bool usesmcomparemode() { return !usegatherforsm() || (hasTG && hasGPU5 && usetexgather > 1); }
|
|
|
|
static void loadsmshaders()
|
|
{
|
|
if(smalpha && alphashadow)
|
|
{
|
|
smalphaworldshader = useshaderbyname("smalphaworld");
|
|
if(smfilter)
|
|
{
|
|
useshaderbyname("smalphaclear");
|
|
useshaderbyname(usegatherforsm() ? "smalphablur2d" : "smalphablurrect");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void clearsmshaders()
|
|
{
|
|
smalphaworldshader = nullptr;
|
|
}
|
|
|
|
static inline void setsmnoncomparemode() // use texture gather
|
|
{
|
|
glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
glTexParameteri(shadowatlastarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(shadowatlastarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
}
|
|
|
|
static inline void setsmcomparemode() // use embedded shadow cmp
|
|
{
|
|
glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
glTexParameteri(shadowatlastarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(shadowatlastarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
|
|
VAR(debugshadowatlas, 0, 0, 3);
|
|
void viewshadowatlas()
|
|
{
|
|
bool color = debugshadowatlas > 1 && shadowcolortex && (debugshadowatlas <= 2 || shadowfiltertex);
|
|
int w = min(hudw, hudh)/2, h = (w*hudh)/hudw, x = hudw-w, y = hudh-h;
|
|
float tw = 1, th = 1;
|
|
if((color && debugshadowatlas > 2) || shadowatlastarget == GL_TEXTURE_RECTANGLE)
|
|
{
|
|
tw = shadowatlaspacker.w;
|
|
th = shadowatlaspacker.h;
|
|
if(debugshadowatlas > 2) { tw /= 2; th /= 2; }
|
|
SETSHADER(hudrect);
|
|
}
|
|
else hudshader->set();
|
|
gle::colorf(1, 1, 1);
|
|
if(color)
|
|
{
|
|
if(debugshadowatlas > 2) glBindTexture(GL_TEXTURE_RECTANGLE, shadowcolortex);
|
|
else glBindTexture(shadowatlastarget, shadowcolortex);
|
|
}
|
|
else
|
|
{
|
|
glBindTexture(shadowatlastarget, shadowatlastex);
|
|
if(usesmcomparemode()) setsmnoncomparemode();
|
|
}
|
|
debugquad(x, y, w, h, 0, 0, tw, th);
|
|
if(!color && usesmcomparemode()) setsmcomparemode();
|
|
}
|
|
|
|
extern int smdepthprec, smsize;
|
|
|
|
void setupshadowatlas()
|
|
{
|
|
int size = min((1<<smsize), hwtexsize);
|
|
shadowatlaspacker.resize(size, size);
|
|
|
|
if(!shadowatlastex) glGenTextures(1, &shadowatlastex);
|
|
|
|
shadowatlastarget = usegatherforsm() ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE;
|
|
createtexture(shadowatlastex, shadowatlaspacker.w, shadowatlaspacker.h, nullptr, 3, 1, smdepthprec > 1 ? GL_DEPTH_COMPONENT32 : (smdepthprec ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16), shadowatlastarget);
|
|
glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
|
|
smalign = 0;
|
|
if(smalpha && alphashadow)
|
|
{
|
|
if(!shadowcolortex) glGenTextures(1, &shadowcolortex);
|
|
|
|
GLenum colcomp = smalphaprec > 1 ? GL_RGB10 : (smalphaprec ? (hasES2 ? GL_RGB565 : GL_RGB5) : GL_R3_G3_B2);
|
|
createtexture(shadowcolortex, shadowatlaspacker.w, shadowatlaspacker.h, nullptr, 3, 1, colcomp, shadowatlastarget);
|
|
|
|
if(!shadowblanktex) glGenTextures(1, &shadowblanktex);
|
|
static const uchar blank[4] = {255, 255, 255, 255};
|
|
createtexture(shadowblanktex, 1, 1, blank, 3, 1, GL_RGB, GL_TEXTURE_RECTANGLE);
|
|
|
|
if(smfilter)
|
|
{
|
|
smalign = 1;
|
|
|
|
if(!shadowfiltertex) glGenTextures(1, &shadowfiltertex);
|
|
createtexture(shadowfiltertex, shadowatlaspacker.w/2, shadowatlaspacker.h/2, nullptr, 3, 1, colcomp, GL_TEXTURE_RECTANGLE);
|
|
|
|
if(!shadowfilterfbo) glGenFramebuffers_(1, &shadowfilterfbo);
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, shadowfilterfbo);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, shadowfiltertex, 0);
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("Failed allocating shadow filter buffer!");
|
|
}
|
|
}
|
|
|
|
if(!shadowatlasfbo) glGenFramebuffers_(1, &shadowatlasfbo);
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, shadowatlasfbo);
|
|
|
|
if(shadowcolortex) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, shadowatlastarget, shadowcolortex, 0);
|
|
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowatlastarget, shadowatlastex, 0);
|
|
|
|
if(!shadowcolortex) glDrawBuffer(GL_NONE);
|
|
glReadBuffer(GL_NONE);
|
|
|
|
if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
fatal("failed allocating shadow atlas!");
|
|
|
|
glBindFramebuffer_(GL_FRAMEBUFFER, 0);
|
|
|
|
loadsmshaders();
|
|
}
|
|
|
|
void cleanupshadowatlas()
|
|
{
|
|
if(shadowatlastex) { glDeleteTextures(1, &shadowatlastex); shadowatlastex = 0; }
|
|
if(shadowcolortex) { glDeleteTextures(1, &shadowcolortex); shadowcolortex = 0; }
|
|
if(shadowblanktex) { glDeleteTextures(1, &shadowblanktex); shadowblanktex = 0; }
|
|
if(shadowatlasfbo) { glDeleteFramebuffers_(1, &shadowatlasfbo); shadowatlasfbo = 0; }
|
|
if(shadowfiltertex) { glDeleteTextures(1, &shadowfiltertex); shadowfiltertex = 0; }
|
|
if(shadowfilterfbo) { glDeleteFramebuffers_(1, &shadowfilterfbo); shadowfilterfbo = 0; }
|
|
clearshadowcache();
|
|
clearsmshaders();
|
|
}
|
|
|
|
const matrix4 cubeshadowviewmatrix[6] =
|
|
{
|
|
// sign-preserving cubemap projections
|
|
matrix4(vec(0, 0, 1), vec(0, 1, 0), vec(-1, 0, 0)), // +X
|
|
matrix4(vec(0, 0, 1), vec(0, 1, 0), vec( 1, 0, 0)), // -X
|
|
matrix4(vec(1, 0, 0), vec(0, 0, 1), vec(0, -1, 0)), // +Y
|
|
matrix4(vec(1, 0, 0), vec(0, 0, 1), vec(0, 1, 0)), // -Y
|
|
matrix4(vec(1, 0, 0), vec(0, 1, 0), vec(0, 0, -1)), // +Z
|
|
matrix4(vec(1, 0, 0), vec(0, 1, 0), vec(0, 0, 1)) // -Z
|
|
};
|
|
|
|
FVAR(smpolyfactor, -1e3f, 1, 1e3f);
|
|
FVAR(smpolyoffset, -1e3f, 0, 1e3f);
|
|
FVAR(smbias, -1e6f, 0.01f, 1e6f);
|
|
FVAR(smpolyfactor2, -1e3f, 1.5f, 1e3f);
|
|
FVAR(smpolyoffset2, -1e3f, 0, 1e3f);
|
|
FVAR(smbias2, -1e6f, 0.02f, 1e6f);
|
|
FVAR(smprec, 1e-3f, 1, 1e3f);
|
|
FVAR(smcubeprec, 1e-3f, 1, 1e3f);
|
|
FVAR(smspotprec, 1e-3f, 1, 1e3f);
|
|
|
|
VARFP(smsize, 10, 12, 14, cleanupshadowatlas());
|
|
VARFP(smdepthprec, 0, 0, 2, cleanupshadowatlas());
|
|
VAR(smsidecull, 0, 1, 1);
|
|
VAR(smviscull, 0, 1, 1);
|
|
VAR(smborder, 0, 3, 16);
|
|
VAR(smborder2, 0, 4, 16);
|
|
VAR(smminradius, 0, 16, 10000);
|
|
VAR(smminsize, 1, 96, 1024);
|
|
VAR(smmaxsize, 1, 384, 1024);
|
|
//VAR(smmaxsize, 1, 4096, 4096);
|
|
VAR(smused, 1, 0, 0);
|
|
VAR(smquery, 0, 1, 1);
|
|
VARF(smcullside, 0, 1, 1, cleanupshadowatlas());
|
|
VARF(smcache, 0, 1, 2, cleanupshadowatlas());
|
|
VARFP(smfilter, 0, 2, 3, { cleardeferredlightshaders(); cleanupshadowatlas(); cleanupvolumetric(); });
|
|
VARFP(smgather, 0, 0, 1, { cleardeferredlightshaders(); cleanupshadowatlas(); cleanupvolumetric(); });
|
|
VAR(smnoshadow, 0, 0, 1);
|
|
VAR(smdynshadow, 0, 1, 1);
|
|
VARF(smalpha, 0, 2, 2, { cleardeferredlightshaders(); cleanupshadowatlas(); });
|
|
VARFP(smalphaprec, 0, 0, 2, cleanupshadowatlas());
|
|
VAR(lightpassesused, 1, 0, 0);
|
|
VAR(lightsvisible, 1, 0, 0);
|
|
VAR(lightsoccluded, 1, 0, 0);
|
|
VARN(lightbatches, lightbatchesused, 1, 0, 0);
|
|
VARN(lightbatchrects, lightbatchrectsused, 1, 0, 0);
|
|
VARN(lightbatchstacks, lightbatchstacksused, 1, 0, 0);
|
|
|
|
VARFR(alphashadow, 0, 0, 2, { cleardeferredlightshaders(); cleanupshadowatlas(); });
|
|
FVARFR(alphashadowscale, 0, 1, 2, clearshadowcache());
|
|
|
|
enum
|
|
{
|
|
MAXLIGHTTILEBATCH = 8
|
|
};
|
|
|
|
VARF(lighttilebatch, 0, MAXLIGHTTILEBATCH, MAXLIGHTTILEBATCH, cleardeferredlightshaders());
|
|
VARF(batchsunlight, 0, 2, 2, cleardeferredlightshaders());
|
|
|
|
int shadowmapping = 0;
|
|
|
|
struct lightrect
|
|
{
|
|
uchar x1, y1, x2, y2;
|
|
|
|
lightrect() {}
|
|
lightrect(uchar x1, uchar y1, uchar x2, uchar y2) : x1(x1), y1(y1), x2(x2), y2(y2) {}
|
|
lightrect(const lightinfo &l)
|
|
{
|
|
calctilebounds(l.sx1, l.sy1, l.sx2, l.sy2, x1, y1, x2, y2);
|
|
}
|
|
|
|
bool outside(const lightrect &o) const
|
|
{
|
|
return x1 >= o.x2 || x2 <= o.x1 || y1 >= o.y2 || y2 <= o.y1;
|
|
}
|
|
|
|
bool inside(const lightrect &o) const
|
|
{
|
|
return x1 >= o.x1 && x2 <= o.x2 && y1 >= o.y1 && y2 <= o.y2;
|
|
}
|
|
|
|
void intersect(const lightrect &o)
|
|
{
|
|
x1 = max(x1, o.x1);
|
|
y1 = max(y1, o.y1);
|
|
x2 = min(x2, o.x2);
|
|
y2 = min(y2, o.y2);
|
|
}
|
|
|
|
bool overlaps(int tx1, int ty1, int tx2, int ty2, const uint *tilemask) const
|
|
{
|
|
if(int(x2) <= tx1 || int(x1) >= tx2 || int(y2) <= ty1 || int(y1) >= ty2) return false;
|
|
if(!tilemask) return true;
|
|
uint xmask = (1<<x2) - (1<<x1);
|
|
for(int y = max(int(y1), ty1), end = min(int(y2), ty2); y < end; y++) if(tilemask[y] & xmask) return true;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
enum
|
|
{
|
|
BF_SPOTLIGHT = 1<<0,
|
|
BF_NOSHADOW = 1<<1,
|
|
BF_SMALPHA = 1<<2,
|
|
BF_NOSUN = 1<<3
|
|
};
|
|
|
|
struct lightbatchkey
|
|
{
|
|
uchar flags, numlights;
|
|
ushort lights[MAXLIGHTTILEBATCH];
|
|
};
|
|
|
|
struct lightbatch : lightbatchkey
|
|
{
|
|
vector<lightrect> rects;
|
|
|
|
void reset()
|
|
{
|
|
rects.setsize(0);
|
|
}
|
|
|
|
bool overlaps(int tx1, int ty1, int tx2, int ty2, const uint *tilemask) const
|
|
{
|
|
if(!tx1 && !ty1 && tx2 >= lighttilew && ty2 >= lighttileh && !tilemask) return true;
|
|
loopv(rects) if(rects[i].overlaps(tx1, ty1, tx2, ty2, tilemask)) return true;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
static inline void htrecycle(lightbatch &l)
|
|
{
|
|
l.reset();
|
|
}
|
|
|
|
static inline uint hthash(const lightbatchkey &l)
|
|
{
|
|
uint h = 0;
|
|
loopi(l.numlights) h = ((h<<8)+h)^l.lights[i];
|
|
return h;
|
|
}
|
|
|
|
static inline bool htcmp(const lightbatchkey &x, const lightbatchkey &y)
|
|
{
|
|
return x.flags == y.flags &&
|
|
x.numlights == y.numlights &&
|
|
(!x.numlights || !memcmp(x.lights, y.lights, x.numlights*sizeof(x.lights[0])));
|
|
}
|
|
|
|
vector<lightinfo> lights;
|
|
vector<int> lightorder;
|
|
hashset<lightbatch> lightbatcher(128);
|
|
vector<lightbatch *> lightbatches;
|
|
vector<shadowmapinfo> shadowmaps;
|
|
|
|
void clearshadowcache()
|
|
{
|
|
shadowmaps.setsize(0);
|
|
|
|
clearradiancehintscache();
|
|
clearshadowmeshes();
|
|
}
|
|
|
|
static shadowmapinfo *addshadowmap(ushort x, ushort y, int size, int &idx, int light = -1, shadowcacheval *cached = nullptr)
|
|
{
|
|
idx = shadowmaps.length();
|
|
shadowmapinfo *sm = &shadowmaps.add();
|
|
sm->x = x;
|
|
sm->y = y;
|
|
sm->size = size;
|
|
sm->light = light;
|
|
sm->sidemask = 0;
|
|
sm->transparent = 0;
|
|
sm->cached = cached;
|
|
return sm;
|
|
}
|
|
|
|
#define CSM_MAXSPLITS 8
|
|
|
|
VARF(csmmaxsize, 256, 768, 2048, clearshadowcache());
|
|
VARF(csmsplits, 1, 3, CSM_MAXSPLITS, { cleardeferredlightshaders(); clearshadowcache(); });
|
|
FVAR(csmsplitweight, 0.20f, 0.75f, 0.95f);
|
|
VARF(csmshadowmap, 0, 1, 1, { cleardeferredlightshaders(); clearshadowcache(); });
|
|
|
|
// cascaded shadow maps
|
|
struct cascadedshadowmap
|
|
{
|
|
struct splitinfo
|
|
{
|
|
float nearplane; // split distance to near plane
|
|
float farplane; // split distance to farplane
|
|
matrix4 proj; // one projection per split
|
|
vec scale, offset; // scale and offset of the projection
|
|
int idx; // shadowmapinfo indices
|
|
vec center, bounds; // max extents of shadowmap in sunlight model space
|
|
plane cull[4]; // world space culling planes of the split's projected sides
|
|
};
|
|
matrix4 model; // model view is shared by all splits
|
|
splitinfo splits[CSM_MAXSPLITS]; // per-split parameters
|
|
vec lightview; // view vector for light
|
|
int rendered;
|
|
void setup(); // insert shadowmaps for each split frustum if there is sunlight
|
|
void updatesplitdist(); // compute split frustum distances
|
|
void getmodelmatrix(); // compute the shared model matrix
|
|
void getprojmatrix(); // compute each cropped projection matrix
|
|
void gencullplanes(); // generate culling planes for the mvp matrix
|
|
void bindparams(); // bind any shader params necessary for lighting
|
|
};
|
|
|
|
void cascadedshadowmap::setup()
|
|
{
|
|
int size = ((csmmaxsize * shadowatlaspacker.w) / SHADOWATLAS_SIZE + smalign)&~smalign;
|
|
loopi(csmsplits)
|
|
{
|
|
ushort smx = USHRT_MAX, smy = USHRT_MAX;
|
|
splits[i].idx = -1;
|
|
if(shadowatlaspacker.insert(smx, smy, size, size))
|
|
addshadowmap(smx, smy, size, splits[i].idx);
|
|
}
|
|
getmodelmatrix();
|
|
getprojmatrix();
|
|
gencullplanes();
|
|
}
|
|
|
|
VAR(csmnearplane, 1, 1, 16);
|
|
VAR(csmfarplane, 64, 1024, 16384);
|
|
FVAR(csmpradiustweak, 1e-3f, 1, 1e3f);
|
|
FVAR(csmdepthrange, 0, 1024, 1e6f);
|
|
FVAR(csmdepthmargin, 0, 0.1f, 1e3f);
|
|
FVAR(csmpolyfactor, -1e3f, 2, 1e3f);
|
|
FVAR(csmpolyoffset, -1e4f, 0, 1e4f);
|
|
FVAR(csmbias, -1e6f, 1e-4f, 1e6f);
|
|
FVAR(csmpolyfactor2, -1e3f, 3, 1e3f);
|
|
FVAR(csmpolyoffset2, -1e4f, 0, 1e4f);
|
|
FVAR(csmbias2, -1e16f, 2e-4f, 1e6f);
|
|
VAR(csmcull, 0, 1, 1);
|
|
|
|
void cascadedshadowmap::updatesplitdist()
|
|
{
|
|
float lambda = csmsplitweight, nd = csmnearplane, fd = csmfarplane, ratio = fd/nd;
|
|
splits[0].nearplane = nd;
|
|
for(int i = 1; i < csmsplits; ++i)
|
|
{
|
|
float si = i / float(csmsplits);
|
|
splits[i].nearplane = lambda*(nd*pow(ratio, si)) + (1-lambda)*(nd + (fd - nd)*si);
|
|
splits[i-1].farplane = splits[i].nearplane * 1.005f;
|
|
}
|
|
splits[csmsplits-1].farplane = fd;
|
|
}
|
|
|
|
void cascadedshadowmap::getmodelmatrix()
|
|
{
|
|
model = viewmatrix;
|
|
model.rotate_around_x(sunlightpitch*RAD);
|
|
model.rotate_around_z((180-sunlightyaw)*RAD);
|
|
}
|
|
|
|
void cascadedshadowmap::getprojmatrix()
|
|
{
|
|
lightview = vec(sunlightdir).neg();
|
|
|
|
// compute the split frustums
|
|
updatesplitdist();
|
|
|
|
// find z extent
|
|
float minz = lightview.project_bb(worldmin, worldmax), maxz = lightview.project_bb(worldmax, worldmin),
|
|
zmargin = max((maxz - minz)*csmdepthmargin, 0.5f*(csmdepthrange - (maxz - minz)));
|
|
minz -= zmargin;
|
|
maxz += zmargin;
|
|
|
|
// compute each split projection matrix
|
|
loopi(csmsplits)
|
|
{
|
|
splitinfo &split = splits[i];
|
|
if(split.idx < 0) continue;
|
|
const shadowmapinfo &sm = shadowmaps[split.idx];
|
|
|
|
vec c;
|
|
float radius = calcfrustumboundsphere(split.nearplane, split.farplane, camera1->o, camdir, c);
|
|
|
|
// compute the projected bounding box of the sphere
|
|
vec tc;
|
|
model.transform(c, tc);
|
|
int border = smfilter > 2 ? smborder2 : smborder;
|
|
const float pradius = ceil(radius * csmpradiustweak) + smalign, step = (2*pradius) / (sm.size - 2*border);
|
|
vec2 offset = vec2(tc).sub(pradius).div(step*(1+smalign));
|
|
offset.x = floor(offset.x)*(1+smalign);
|
|
offset.y = floor(offset.y)*(1+smalign);
|
|
split.center = vec(vec2(offset).mul(step).add(pradius), -0.5f*(minz + maxz));
|
|
split.bounds = vec(pradius, pradius, 0.5f*(maxz - minz));
|
|
|
|
// modify mvp with a scale and offset
|
|
// now compute the update model view matrix for this split
|
|
split.scale = vec(1/step, 1/step, -1/(maxz - minz));
|
|
split.offset = vec(border - offset.x, border - offset.y, -minz/(maxz - minz));
|
|
|
|
split.proj.identity();
|
|
split.proj.settranslation(2*split.offset.x/sm.size - 1, 2*split.offset.y/sm.size - 1, 2*split.offset.z - 1);
|
|
split.proj.setscale(2*split.scale.x/sm.size, 2*split.scale.y/sm.size, 2*split.scale.z);
|
|
}
|
|
}
|
|
|
|
void cascadedshadowmap::gencullplanes()
|
|
{
|
|
loopi(csmsplits)
|
|
{
|
|
splitinfo &split = splits[i];
|
|
matrix4 mvp;
|
|
mvp.mul(split.proj, model);
|
|
vec4 px = mvp.rowx(), py = mvp.rowy(), pw = mvp.roww();
|
|
split.cull[0] = plane(vec4(pw).add(px)).normalize(); // left plane
|
|
split.cull[1] = plane(vec4(pw).sub(px)).normalize(); // right plane
|
|
split.cull[2] = plane(vec4(pw).add(py)).normalize(); // bottom plane
|
|
split.cull[3] = plane(vec4(pw).sub(py)).normalize(); // top plane
|
|
}
|
|
}
|
|
|
|
void cascadedshadowmap::bindparams()
|
|
{
|
|
GLOBALPARAM(csmmatrix, matrix3(model));
|
|
|
|
static GlobalShaderParam csmtc("csmtc"), csmoffset("csmoffset");
|
|
vec4 *csmtcv = csmtc.reserve<vec4>(csmsplits);
|
|
vec *csmoffsetv = csmoffset.reserve<vec>(csmsplits);
|
|
loopi(csmsplits)
|
|
{
|
|
cascadedshadowmap::splitinfo &split = splits[i];
|
|
if(split.idx < 0) continue;
|
|
const shadowmapinfo &sm = shadowmaps[split.idx];
|
|
|
|
csmtcv[i] = vec4(vec2(split.center).mul(-split.scale.x), split.scale.x, split.bounds.x*split.scale.x);
|
|
|
|
const float bias = (smfilter > 2 ? csmbias2 : csmbias) * (-512.0f / sm.size) * (split.farplane - split.nearplane) / (splits[0].farplane - splits[0].nearplane);
|
|
csmoffsetv[i] = vec(sm.x, sm.y, 0.5f + bias).add2(0.5f*sm.size);
|
|
}
|
|
GLOBALPARAMF(csmz, splits[0].center.z*-splits[0].scale.z, splits[0].scale.z);
|
|
}
|
|
|
|
cascadedshadowmap csm;
|
|
|
|
int calcbbcsmsplits(const ivec &bbmin, const ivec &bbmax)
|
|
{
|
|
int mask = (1<<csmsplits)-1;
|
|
if(!csmcull) return mask;
|
|
loopi(csmsplits)
|
|
{
|
|
const cascadedshadowmap::splitinfo &split = csm.splits[i];
|
|
int k;
|
|
for(k = 0; k < 4; k++)
|
|
{
|
|
const plane &p = split.cull[k];
|
|
ivec omin, omax;
|
|
if(p.x > 0) { omin.x = bbmin.x; omax.x = bbmax.x; } else { omin.x = bbmax.x; omax.x = bbmin.x; }
|
|
if(p.y > 0) { omin.y = bbmin.y; omax.y = bbmax.y; } else { omin.y = bbmax.y; omax.y = bbmin.y; }
|
|
if(p.z > 0) { omin.z = bbmin.z; omax.z = bbmax.z; } else { omin.z = bbmax.z; omax.z = bbmin.z; }
|
|
if(omax.dist(p) < 0) { mask &= ~(1<<i); goto nextsplit; }
|
|
if(omin.dist(p) < 0) goto notinside;
|
|
}
|
|
mask &= (2<<i)-1;
|
|
break;
|
|
notinside:
|
|
while(++k < 4)
|
|
{
|
|
const plane &p = split.cull[k];
|
|
ivec omax(p.x > 0 ? bbmax.x : bbmin.x, p.y > 0 ? bbmax.y : bbmin.y, p.z > 0 ? bbmax.z : bbmin.z);
|
|
if(omax.dist(p) < 0) { mask &= ~(1<<i); break; }
|
|
}
|
|
nextsplit:;
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
int calcspherecsmsplits(const vec ¢er, float radius)
|
|
{
|
|
int mask = (1<<csmsplits)-1;
|
|
if(!csmcull) return mask;
|
|
loopi(csmsplits)
|
|
{
|
|
const cascadedshadowmap::splitinfo &split = csm.splits[i];
|
|
int k;
|
|
for(k = 0; k < 4; k++)
|
|
{
|
|
const plane &p = split.cull[k];
|
|
float dist = p.dist(center);
|
|
if(dist < -radius) { mask &= ~(1<<i); goto nextsplit; }
|
|
if(dist < radius) goto notinside;
|
|
}
|
|
mask &= (2<<i)-1;
|
|
break;
|
|
notinside:
|
|
while(++k < |