Another shadowbuffer goodie: the "Halfway trick"

http://www.blender3d.org/cms/Shadow_buffer__Halfway.786.0.html

Simply said: by using the average of the nearest and 2nd nearest Z value
in Shadowbuffers you can reduce bias errors very well.
For backwards compatibility it is a new buffer type though.
This commit is contained in:
Ton Roosendaal 2006-10-15 11:50:46 +00:00
parent 3b0adf0bf4
commit 9fa438b4e9
8 changed files with 94 additions and 30 deletions

@ -580,6 +580,7 @@ void *add_lamp(void)
la->ray_samp= la->ray_sampy= la->ray_sampz= 1;
la->area_size=la->area_sizey=la->area_sizez= 1.0;
la->buffers= 1;
la->buftype= LA_SHADBUF_HALFWAY;
return la;
}

@ -115,6 +115,7 @@ typedef struct Lamp {
/* buftype, no flag */
#define LA_SHADBUF_REGULAR 0
#define LA_SHADBUF_IRREGULAR 1
#define LA_SHADBUF_HALFWAY 2
/* bufflag, auto clipping */
#define LA_SHADBUF_AUTO_START 1

@ -77,6 +77,7 @@ typedef struct ZSpan {
float zmulx, zmuly, zofsx, zofsy; /* transform from hoco to zbuf co */
int *rectz, *arectz; /* zbuffers, arectz is for transparant */
int *rectz1; /* seconday z buffer for shadowbuffer (2nd closest z) */
int *rectp; /* polygon index buffer */
APixstr *apixbuf, *curpstr; /* apixbuf for transparent */
struct ListBase *apsmbase;

@ -2155,11 +2155,15 @@ static void initshadowbuf(Render *re, LampRen *lar, float mat[][4])
shb->d= lar->clipsta;
shb->clipend= lar->clipend;
/* bias is percentage, made 2x karger because of correction for angle of incidence */
/* bias is percentage, made 2x larger because of correction for angle of incidence */
/* when a ray is closer to parallel of a face, bias value is increased during render */
shb->bias= (0.02*lar->bias)*0x7FFFFFFF;
shb->bias= shb->bias*(100/re->r.size);
/* halfway method (average of first and 2nd z) reduces bias issues */
if(lar->buftype==LA_SHADBUF_HALFWAY)
shb->bias= 0.1f*shb->bias;
}
@ -2227,6 +2231,7 @@ static LampRen *add_render_lamp(Render *re, Object *ob)
lar->shadhalostep = la->shadhalostep;
lar->clipsta = la->clipsta;
lar->clipend = la->clipend;
lar->bias = la->bias;
lar->type= la->type;

@ -478,15 +478,15 @@ void RE_SetCamera(Render *re, Object *camera)
clipend= cam->clipend;
}
else if(camera->type==OB_LAMP) {
/* fac= cos( PI*((float)(256- la->spsi))/512.0 ); */
/* phi= acos(fac); */
/* lens= 16.0*fac/sin(phi); */
lens= re->lens;
Lamp *la= camera->data;
float fac= cos( M_PI*la->spotsize/360.0 );
float phi= acos(fac);
lens= 16.0*fac/sin(phi);
if(lens==0.0f)
lens= 35.0;
clipsta= 0.1;
clipend= 1000.0;
clipsta= la->clipsta;
clipend= la->clipend;
}
else { /* envmap exception... */
lens= re->lens;

@ -369,6 +369,9 @@ void makeshadowbuf(Render *re, LampRen *lar)
float wsize, *jitbuf, twozero[2]= {0.0f, 0.0f}, angle, temp;
int *rectz, samples;
/* XXXX EVIL! this global is used in clippyra(), zbuf.c */
R.clipcrop= 1.0f;
if(lar->bufflag & (LA_SHADBUF_AUTO_START|LA_SHADBUF_AUTO_END))
shadowbuf_autoclip(re, lar);
@ -386,8 +389,8 @@ void makeshadowbuf(Render *re, LampRen *lar)
i_window(-wsize, wsize, -wsize, wsize, shb->d, shb->clipend, shb->winmat);
MTC_Mat4MulMat4(shb->persmat, shb->viewmat, shb->winmat);
if(lar->buftype==LA_SHADBUF_REGULAR) {
if(ELEM(lar->buftype, LA_SHADBUF_REGULAR, LA_SHADBUF_HALFWAY)) {
/* jitter, weights */
shb->jit= give_jitter_tab(shb->samp);
make_jitter_weight_tab(shb, lar->filtertype);

@ -682,7 +682,7 @@ static void zbufline(ZSpan *zspan, int zvlnr, float *vec1, float *vec2)
static void zbufline_onlyZ(ZSpan *zspan, int zvlnr, float *vec1, float *vec2)
{
int *rectz;
int *rectz, *rectz1= NULL;
int start, end, x, y, oldx, oldy, ofs;
int dz, vergz, maxtest= 0;
float dx, dy;
@ -716,6 +716,8 @@ static void zbufline_onlyZ(ZSpan *zspan, int zvlnr, float *vec1, float *vec2)
if(vergz>0x50000000 && dz>0) maxtest= 1; // prevent overflow
rectz= zspan->rectz + oldy*zspan->rectx+ start;
if(zspan->rectz1)
rectz1= zspan->rectz1 + oldy*zspan->rectx+ start;
if(dy<0) ofs= -zspan->rectx;
else ofs= zspan->rectx;
@ -726,18 +728,24 @@ static void zbufline_onlyZ(ZSpan *zspan, int zvlnr, float *vec1, float *vec2)
if(y!=oldy) {
oldy= y;
rectz+= ofs;
if(rectz1) rectz1+= ofs;
}
if(x>=0 && y>=0 && y<zspan->recty) {
if(vergz<*rectz) {
if(vergz < *rectz) {
if(rectz1) *rectz1= *rectz;
*rectz= vergz;
}
else if(rectz1 && vergz < *rectz1)
*rectz1= vergz;
}
v1[1]+= dy;
if(maxtest && (vergz > 0x7FFFFFF0 - dz)) vergz= 0x7FFFFFF0;
else vergz+= dz;
if(rectz1) rectz1++;
}
}
else {
@ -765,6 +773,8 @@ static void zbufline_onlyZ(ZSpan *zspan, int zvlnr, float *vec1, float *vec2)
if(vergz>0x50000000 && dz>0) maxtest= 1; // prevent overflow
rectz= zspan->rectz + start*zspan->rectx+ oldx;
if(zspan->rectz1)
rectz1= zspan->rectz1 + start*zspan->rectx+ oldx;
if(dx<0) ofs= -1;
else ofs= 1;
@ -775,17 +785,24 @@ static void zbufline_onlyZ(ZSpan *zspan, int zvlnr, float *vec1, float *vec2)
if(x!=oldx) {
oldx= x;
rectz+= ofs;
if(rectz1) rectz1+= ofs;
}
if(x>=0 && y>=0 && x<zspan->rectx) {
if(vergz<*rectz) {
if(vergz < *rectz) {
if(rectz1) *rectz1= *rectz;
*rectz= vergz;
}
else if(rectz1 && vergz < *rectz1)
*rectz1= vergz;
}
v1[0]+= dx;
if(maxtest && (vergz > 0x7FFFFFF0 - dz)) vergz= 0x7FFFFFF0;
else vergz+= dz;
if(rectz1)
rectz1+=zspan->rectx;
}
}
}
@ -1183,14 +1200,15 @@ static void zbuffillGL4(ZSpan *zspan, int zvlnr, float *v1, float *v2, float *v3
* @param v3 [4 floats, world coordinates] third vertex
*/
/* now: filling two Z values, the closest and 2nd closest */
static void zbuffillGL_onlyZ(ZSpan *zspan, int zvlnr, float *v1, float *v2, float *v3, float *v4)
{
double zxd, zyd, zy0, zverg;
float x0,y0,z0;
float x1,y1,z1,x2,y2,z2,xx1;
float *span1, *span2;
int *rz, x, y;
int sn1, sn2, rectx, *rectzofs, my0, my2;
int *rz, *rz1, x, y;
int sn1, sn2, rectx, *rectzofs, *rectzofs1= NULL, my0, my2;
/* init */
zbuf_init_span(zspan);
@ -1237,6 +1255,8 @@ static void zbuffillGL_onlyZ(ZSpan *zspan, int zvlnr, float *v1, float *v2, floa
/* start-offset in rect */
rectx= zspan->rectx;
rectzofs= (zspan->rectz+rectx*my2);
if(zspan->rectz1)
rectzofs1= (zspan->rectz1+rectx*my2);
/* correct span */
sn1= (my0 + my2)/2;
@ -1261,20 +1281,30 @@ static void zbuffillGL_onlyZ(ZSpan *zspan, int zvlnr, float *v1, float *v2, floa
if(sn2>=sn1) {
zverg= (double)sn1*zxd + zy0;
rz= rectzofs+sn1;
rz1= rectzofs1+sn1;
x= sn2-sn1;
while(x>=0) {
if( (int)zverg < *rz) {
*rz= (int)zverg;
int zvergi= (int)zverg;
/* option: maintain two depth values, closest and 2nd closest */
if(zvergi < *rz) {
if(rectzofs1) *rz1= *rz;
*rz= zvergi;
}
else if(rectzofs1 && zvergi < *rz1)
*rz1= zvergi;
zverg+= zxd;
rz++;
rz1++;
x--;
}
}
zy0-=zyd;
rectzofs-= rectx;
if(rectzofs1) rectzofs1-= rectx;
}
}
@ -1309,6 +1339,7 @@ static void clippyra(float *labda, float *v1, float *v2, int *b2, int *b3, int a
v13= v1[3];
}
else {
/* XXXXX EVIL! this is a R global, whilst this function can be called for shadow, before R was set */
dw= R.clipcrop*(v2[3]-v1[3]);
v13= R.clipcrop*v1[3];
}
@ -1425,7 +1456,7 @@ void zbufclip(ZSpan *zspan, int zvlnr, float *f1, float *f2, float *f3, int c1,
{
float *vlzp[32][3], labda[3][2];
float vez[400], *trias[40];
if(c1 | c2 | c3) { /* not in middle */
if(c1 & c2 & c3) { /* completely out */
return;
@ -1445,6 +1476,7 @@ void zbufclip(ZSpan *zspan, int zvlnr, float *f1, float *f2, float *f3, int c1,
clipflag[1]= ( (c1 & 3) | (c2 & 3) | (c3 & 3) );
clipflag[2]= ( (c1 & 12) | (c2 & 12) | (c3 & 12) );
}
else clipflag[1]=clipflag[2]= 0;
for(b=0;b<3;b++) {
@ -1454,7 +1486,7 @@ void zbufclip(ZSpan *zspan, int zvlnr, float *f1, float *f2, float *f3, int c1,
for(v=0; v<clvlo; v++) {
if(vlzp[v][0]!=0) { /* face is still there */
if(vlzp[v][0]!=NULL) { /* face is still there */
b2= b3 =0; /* clip flags */
if(b==0) arg= 2;
@ -1466,13 +1498,14 @@ void zbufclip(ZSpan *zspan, int zvlnr, float *f1, float *f2, float *f3, int c1,
clippyra(labda[2], vlzp[v][2],vlzp[v][0], &b2,&b3, arg);
if(b2==0 && b3==1) {
/* completely 'in' */;
/* completely 'in', but we copy because of last for() loop in this section */;
vlzp[clvl][0]= vlzp[v][0];
vlzp[clvl][1]= vlzp[v][1];
vlzp[clvl][2]= vlzp[v][2];
vlzp[v][0]= NULL;
clvl++;
} else if(b3==0) {
vlzp[v][0]=0;
vlzp[v][0]= NULL;
/* completely 'out' */;
} else {
b1=0;
@ -1492,7 +1525,7 @@ void zbufclip(ZSpan *zspan, int zvlnr, float *f1, float *f2, float *f3, int c1,
}
}
vlzp[v][0]=0;
vlzp[v][0]= NULL;
if(b1>2) {
for(b3=3; b3<=b1; b3++) {
vlzp[clvl][0]= trias[0];
@ -1861,11 +1894,15 @@ void zbuffer_shadow(Render *re, LampRen *lar, int *rectz, int size, float jitx,
/* the buffers */
zspan.rectz= rectz;
fillrect(rectz, size, size, 0x7FFFFFFE);
if(lar->buftype==LA_SHADBUF_HALFWAY) {
zspan.rectz1= MEM_mallocN(size*size*sizeof(int), "seconday z buffer");
fillrect(zspan.rectz1, size, size, 0x7FFFFFFE);
}
/* filling methods */
zspan.zbuflinefunc= zbufline_onlyZ;
zspan.zbuffunc= zbuffillGL_onlyZ;
for(a=0; a<re->totvlak; a++) {
if((a & 255)==0) vlr= re->blovl[a>>8];
@ -1889,6 +1926,15 @@ void zbuffer_shadow(Render *re, LampRen *lar, int *rectz, int size, float jitx,
}
}
}
/* merge buffers */
if(lar->buftype==LA_SHADBUF_HALFWAY) {
for(a=size*size -1; a>=0; a--)
rectz[a]= (rectz[a]>>1) + (zspan.rectz1[a]>>1);
MEM_freeN(zspan.rectz1);
}
zbuf_free_span(&zspan);
}

@ -2148,8 +2148,15 @@ static void lamp_panel_spot(Object *ob, Lamp *la)
uiDefButBitS(block, TOG, LA_SHAD_RAY, B_SHADRAY,"Ray Shadow",10,180,80,19,&la->mode, 0, 0, 0, 0, "Use ray tracing for shadow");
if(la->type==LA_SPOT) {
uiDefButBitS(block, TOG, LA_SHAD_BUF, B_SHADBUF, "Buf.Shadow",10,160,80,19,&la->mode, 0, 0, 0, 0, "Lets spotlight produce shadows using shadow buffer");
if(la->mode & LA_SHAD_BUF)
uiDefButC(block, MENU, B_REDR, "Classical %x0|Irregular %x1", 10,140,80,19,&la->buftype, 0, 0, 0, 0, "Buffer type, Irregular buffer produces sharp shadow always, but doesn't support ZTransp shadow receiving");
if(la->mode & LA_SHAD_BUF) {
char *tip= "Regular buffer type";
if(la->buftype==LA_SHADBUF_IRREGULAR)
tip= "Irregular buffer produces sharp shadow always, but it doesn't show up for raytracing";
else if(la->buftype==LA_SHADBUF_HALFWAY)
tip= "Regular buffer, averaginng the closest and 2nd closest Z value for reducing biasing";
uiDefButC(block, MENU, B_REDR, "Classical %x0|Classic-Halfway %x2|Irregular %x1", 10,140,80,19,&la->buftype, 0, 0, 0, 0, tip);
}
}
uiBlockEndAlign(block);
@ -2169,12 +2176,12 @@ static void lamp_panel_spot(Object *ob, Lamp *la)
uiDefButF(block, NUMSLI,B_LAMPREDRAW,"HaloInt ", 100,135,200,19,&la->haint, 0.0, 5.0, 0, 0, "Sets the intensity of the spotlight halo");
if(la->mode & LA_SHAD_BUF) {
if(la->buftype==LA_SHADBUF_REGULAR) {
if(ELEM(la->buftype, LA_SHADBUF_REGULAR, LA_SHADBUF_HALFWAY)) {
uiBlockBeginAlign(block);
uiDefButS(block, NUM,B_SBUFF,"ShadowBufferSize:", 100,110,200,19, &la->bufsize,512,10240, 0, 0, "Sets the size of the shadow buffer to nearest multiple of 16");
uiDefButS(block, ROW,B_NOP, "Box", 100,90,65,19, &la->filtertype, 0.0, LA_SHADBUF_BOX, 0, 0, "Apply Box filter for shadowbuffer");
uiDefButS(block, ROW,B_NOP, "Tent", 165,90,65,19, &la->filtertype, 0.0, LA_SHADBUF_TENT, 0, 0, "Apply Tent filter for shadowbuffer");
uiDefButS(block, ROW,B_NOP, "Gauss", 230,90,70,19, &la->filtertype, 0.0, LA_SHADBUF_GAUSS, 0, 0, "Apply Gauss filter for shadowbuffer");
uiDefButS(block, ROW,B_NOP, "Box", 100,90,65,19, &la->filtertype, 0.0, LA_SHADBUF_BOX, 0, 0, "Apply Box filter for shadowbuffer samples");
uiDefButS(block, ROW,B_NOP, "Tent", 165,90,65,19, &la->filtertype, 0.0, LA_SHADBUF_TENT, 0, 0, "Apply Tent filter for shadowbuffer samples");
uiDefButS(block, ROW,B_NOP, "Gauss", 230,90,70,19, &la->filtertype, 0.0, LA_SHADBUF_GAUSS, 0, 0, "Apply Gauss filter for shadowbuffer samples");
// uiDefButS(block, ROW,B_NOP,"SubSamples: 1", 100,90,140,19, &la->buffers, 1.0, 1.0, 0, 0, "Amount of lampbuffer subsamples, a value of larger than 1 halves the shadowbuffer size");
// uiDefButS(block, ROW,B_NOP,"4", 240,90,30,19, &la->buffers, 1.0, 4.0, 0, 0, "Amount of lampbuffer subsamples, this halves the actual shadowbuffer size");
@ -2183,7 +2190,7 @@ static void lamp_panel_spot(Object *ob, Lamp *la)
uiBlockBeginAlign(block);
uiDefButS(block, NUM,B_LAMPREDRAW,"Samples:", 100,60,100,19, &la->samp,1.0,16.0, 0, 0, "Sets the number of shadow map samples");
uiDefButS(block, NUM,B_NOP,"Halo step:", 200,60,100,19, &la->shadhalostep, 0.0, 12.0, 0, 0, "Sets the volumetric halo sampling frequency");
uiDefButF(block, NUM,B_LAMPREDRAW,"Bias:", 100,40,100,19, &la->bias, 0.01, 5.0, 1, 0, "Sets the shadow map sampling bias");
uiDefButF(block, NUM,B_LAMPREDRAW,"Bias:", 100,40,100,19, &la->bias, 0.001, 5.0, 1, 0, "Sets the shadow map sampling bias");
uiDefButF(block, NUM,B_LAMPREDRAW,"Soft:", 200,40,100,19, &la->soft,1.0,100.0, 100, 0, "Sets the size of the shadow sample area");
}
else { /* LA_SHADBUF_IRREGULAR */