use more realistic mechanism for perspective correct interpolation

This commit is contained in:
zeromus 2017-08-30 19:09:04 -05:00
parent fd6ed7b518
commit 599dca8858
1 changed files with 56 additions and 63 deletions

View File

@ -202,6 +202,8 @@ struct edge_fx_fl {
edge_fx_fl(int Top, int Bottom, VERT** verts, bool& failure); edge_fx_fl(int Top, int Bottom, VERT** verts, bool& failure);
FORCEINLINE int Step(); FORCEINLINE int Step();
VERT TOP, BOTTOM;
VERT** verts; VERT** verts;
long X, XStep, Numerator, Denominator; // DDA info for x long X, XStep, Numerator, Denominator; // DDA info for x
long ErrorTerm; long ErrorTerm;
@ -209,8 +211,6 @@ struct edge_fx_fl {
struct Interpolant { struct Interpolant {
float curr, step, stepExtra; float curr, step, stepExtra;
FORCEINLINE void doStep() { curr += step; }
FORCEINLINE void doStepExtra() { curr += stepExtra; }
FORCEINLINE void initialize(float value) { FORCEINLINE void initialize(float value) {
curr = value; curr = value;
step = 0; step = 0;
@ -225,15 +225,13 @@ struct edge_fx_fl {
} }
}; };
static const int NUM_INTERPOLANTS = 7; static const int NUM_INTERPOLANTS = 8;
union { union {
struct { struct {
Interpolant invw,z,u,v,color[3]; Interpolant invw,w,z,u,v,color[3];
}; };
Interpolant interpolants[NUM_INTERPOLANTS]; Interpolant interpolants[NUM_INTERPOLANTS];
}; };
void FORCEINLINE doStepInterpolants() { for(int i=0;i<NUM_INTERPOLANTS;i++) interpolants[i].doStep(); }
void FORCEINLINE doStepExtraInterpolants() { for(int i=0;i<NUM_INTERPOLANTS;i++) interpolants[i].doStepExtra(); }
}; };
FORCEINLINE edge_fx_fl::edge_fx_fl(int Top, int Bottom, VERT** verts, bool& failure) { FORCEINLINE edge_fx_fl::edge_fx_fl(int Top, int Bottom, VERT** verts, bool& failure) {
@ -245,6 +243,9 @@ FORCEINLINE edge_fx_fl::edge_fx_fl(int Top, int Bottom, VERT** verts, bool& fail
int XEnd = Ceil28_4((fixed28_4)verts[Bottom]->x); int XEnd = Ceil28_4((fixed28_4)verts[Bottom]->x);
int Width = XEnd - X; // can be negative int Width = XEnd - X; // can be negative
TOP = *verts[Top];
BOTTOM = *verts[Bottom];
// even if Height == 0, give some info for horizontal line poly // even if Height == 0, give some info for horizontal line poly
if(Height != 0 || Width != 0) if(Height != 0 || Width != 0)
{ {
@ -273,6 +274,7 @@ FORCEINLINE edge_fx_fl::edge_fx_fl(int Top, int Bottom, VERT** verts, bool& fail
float dx = 1/Fixed28_4ToFloat(dM); float dx = 1/Fixed28_4ToFloat(dM);
invw.initialize(1/verts[Top]->w,1/verts[Bottom]->w,dx,dy,XStep,XPrestep,YPrestep); invw.initialize(1/verts[Top]->w,1/verts[Bottom]->w,dx,dy,XStep,XPrestep,YPrestep);
w.initialize(verts[Top]->w,verts[Bottom]->w,dx,dy,XStep,XPrestep,YPrestep);
u.initialize(verts[Top]->u,verts[Bottom]->u,dx,dy,XStep,XPrestep,YPrestep); u.initialize(verts[Top]->u,verts[Bottom]->u,dx,dy,XStep,XPrestep,YPrestep);
v.initialize(verts[Top]->v,verts[Bottom]->v,dx,dy,XStep,XPrestep,YPrestep); v.initialize(verts[Top]->v,verts[Bottom]->v,dx,dy,XStep,XPrestep,YPrestep);
z.initialize(verts[Top]->z,verts[Bottom]->z,dx,dy,XStep,XPrestep,YPrestep); z.initialize(verts[Top]->z,verts[Bottom]->z,dx,dy,XStep,XPrestep,YPrestep);
@ -298,13 +300,11 @@ FORCEINLINE edge_fx_fl::edge_fx_fl(int Top, int Bottom, VERT** verts, bool& fail
FORCEINLINE int edge_fx_fl::Step() { FORCEINLINE int edge_fx_fl::Step() {
X += XStep; Y++; Height--; X += XStep; Y++; Height--;
doStepInterpolants();
ErrorTerm += Numerator; ErrorTerm += Numerator;
if(ErrorTerm >= Denominator) { if(ErrorTerm >= Denominator) {
X++; X++;
ErrorTerm -= Denominator; ErrorTerm -= Denominator;
doStepExtraInterpolants();
} }
return Height; return Height;
} }
@ -508,7 +508,7 @@ public:
} }
template<bool ISSHADOWPOLYGON> template<bool ISSHADOWPOLYGON>
FORCEINLINE void pixel(const PolygonAttributes &polyAttr, const size_t fragmentIndex, FragmentColor &dstColor, float r, float g, float b, float invu, float invv, float w, float z) FORCEINLINE void pixel(const PolygonAttributes &polyAttr, const size_t fragmentIndex, FragmentColor &dstColor, float r, float g, float b, float u, float v, float w, float z)
{ {
FragmentColor srcColor; FragmentColor srcColor;
FragmentColor shaderOutput; FragmentColor shaderOutput;
@ -585,11 +585,6 @@ public:
} }
} }
//perspective-correct the colors
r = (r * w) + 0.5f;
g = (g * w) + 0.5f;
b = (b * w) + 0.5f;
//this is a HACK: //this is a HACK:
//we are being very sloppy with our interpolation precision right now //we are being very sloppy with our interpolation precision right now
//and rather than fix it, i just want to clamp it //and rather than fix it, i just want to clamp it
@ -599,7 +594,7 @@ public:
polyAttr.alpha); polyAttr.alpha);
//pixel shader //pixel shader
shade(polyAttr.polygonMode, srcColor, shaderOutput, invu * w, invv * w); shade(polyAttr.polygonMode, srcColor, shaderOutput, u, v);
// handle alpha test // handle alpha test
if ( shaderOutput.a == 0 || if ( shaderOutput.a == 0 ||
@ -658,28 +653,6 @@ public:
width = max(1, max(abs(leftWidth), abs(rightWidth))); width = max(1, max(abs(leftWidth), abs(rightWidth)));
} }
//these are the starting values, taken from the left edge
float invw = pLeft->invw.curr;
float u = pLeft->u.curr;
float v = pLeft->v.curr;
float z = pLeft->z.curr;
float color[3] = {
pLeft->color[0].curr,
pLeft->color[1].curr,
pLeft->color[2].curr };
//our dx values are taken from the steps up until the right edge
float invWidth = 1.0f / width;
float dinvw_dx = (pRight->invw.curr - invw) * invWidth;
float du_dx = (pRight->u.curr - u) * invWidth;
float dv_dx = (pRight->v.curr - v) * invWidth;
float dz_dx = (pRight->z.curr - z) * invWidth;
float dc_dx[3] = {
(pRight->color[0].curr - color[0]) * invWidth,
(pRight->color[1].curr - color[1]) * invWidth,
(pRight->color[2].curr - color[2]) * invWidth };
size_t adr = (pLeft->Y*framebufferWidth)+XStart; size_t adr = (pLeft->Y*framebufferWidth)+XStart;
//CONSIDER: in case some other math is wrong (shouldve been clipped OK), we might go out of bounds here. //CONSIDER: in case some other math is wrong (shouldve been clipped OK), we might go out of bounds here.
@ -704,15 +677,6 @@ public:
printf("rasterizer rendering at x=%d! oops!\n",x); printf("rasterizer rendering at x=%d! oops!\n",x);
return; return;
} }
invw += dinvw_dx * -x;
u += du_dx * -x;
v += dv_dx * -x;
z += dz_dx * -x;
color[0] += dc_dx[0] * -x;
color[1] += dc_dx[1] * -x;
color[2] += dc_dx[2] * -x;
adr += -x;
width -= -x;
x = 0; x = 0;
} }
if (x+width > framebufferWidth) if (x+width > framebufferWidth)
@ -725,19 +689,34 @@ public:
width = framebufferWidth - x; width = framebufferWidth - x;
} }
float iw0 = 1 / pLeft->w.curr;
float iw1 = 1 / pRight->w.curr;
//a similar approach is used for walking down left/right edges outside this function
//but the technique is more clear here, so this is where it's documented
while (width-- > 0) while (width-- > 0)
{ {
pixel<ISSHADOWPOLYGON>(polyAttr, adr, dstColor[adr], color[0], color[1], color[2], u, v, 1.0f/invw, z); //calculate parameter for basic lerp across scanline X coord
//(probably done with steps in HW)
float a = ((float)x - pLeft->X) / (pRight->X - pLeft->X);
//courtesy of staplebutter (who has more info about the fixed point precision as well)
//calculate 'perspective corrected' lerp parameter
//this can be used to calculate perspective correct uvrgbw from the basic lerp
//without needing interpolants to be processed as inverses
float P = (a*iw1) / (((1-a)*iw0) + a*iw1);
float u = (1.0f - P) * pLeft->u.curr + P * pRight->u.curr;
float v = (1.0f - P) * pLeft->v.curr + P * pRight->v.curr;
float w = (1.0f - P) * pLeft->w.curr + P * pRight->w.curr;
float z = (1.0f - P) * pLeft->z.curr + P * pRight->z.curr;
float r = (1.0f - P) * pLeft->color[0].curr + P * pRight->color[0].curr;
float g = (1.0f - P) * pLeft->color[1].curr + P * pRight->color[1].curr;
float b = (1.0f - P) * pLeft->color[2].curr + P * pRight->color[2].curr;
pixel<ISSHADOWPOLYGON>(polyAttr, adr, dstColor[adr], r, g, b, u, v, w, z);
adr++; adr++;
x++; x++;
invw += dinvw_dx;
u += du_dx;
v += dv_dx;
z += dz_dx;
color[0] += dc_dx[0];
color[1] += dc_dx[1];
color[2] += dc_dx[2];
} }
} }
@ -758,8 +737,29 @@ public:
if(draw) drawscanline<ISSHADOWPOLYGON>(polyAttr, dstColor, framebufferWidth, framebufferHeight, left,right,lineHack); if(draw) drawscanline<ISSHADOWPOLYGON>(polyAttr, dstColor, framebufferWidth, framebufferHeight, left,right,lineHack);
} }
edge_fx_fl oldleft = *left;
edge_fx_fl oldright = *right;
float iw0[2] = {1/left->TOP.w,1/right->TOP.w};
float iw1[2] = {1/left->BOTTOM.w,1/right->BOTTOM.w};
while(Height--) while(Height--)
{ {
for(int i=0;i<2;i++)
{
edge_fx_fl* old = (i==0)?&oldleft:&oldright;
edge_fx_fl* edge = (i==0)?left:right;
float a = (edge->Y-old->TOP.y/16)/(old->BOTTOM.y/16-old->TOP.y/16);
float P = (a*iw1[i]) / (((1-a)*iw0[i]) + a*iw1[i]);
edge->u.curr = (1.0f - P) * old->TOP.u + P * old->BOTTOM.u;
edge->v.curr = (1.0f - P) * old->TOP.v + P * old->BOTTOM.v;
edge->w.curr = (1.0f - P) * old->TOP.w + P * old->BOTTOM.w;
edge->z.curr = (1.0f - P) * old->TOP.z + P * old->BOTTOM.z;
edge->color[0].curr = (1.0f - P) * old->TOP.fcolor[0] + P * old->BOTTOM.fcolor[0];
edge->color[1].curr = (1.0f - P) * old->TOP.fcolor[1] + P * old->BOTTOM.fcolor[1];
edge->color[2].curr = (1.0f - P) * old->TOP.fcolor[2] + P * old->BOTTOM.fcolor[2];
}
bool draw = (!SLI || (left->Y & SLI_MASK) == SLI_VALUE); bool draw = (!SLI || (left->Y & SLI_MASK) == SLI_VALUE);
if(draw) drawscanline<ISSHADOWPOLYGON>(polyAttr, dstColor, framebufferWidth, framebufferHeight, left,right,lineHack); if(draw) drawscanline<ISSHADOWPOLYGON>(polyAttr, dstColor, framebufferWidth, framebufferHeight, left,right,lineHack);
const int xl = left->X; const int xl = left->X;
@ -1522,19 +1522,12 @@ void SoftRasterizerRenderer::performViewportTransforms()
vert.coord[0] = (vert.coord[0]+vertw) / (2*vertw); vert.coord[0] = (vert.coord[0]+vertw) / (2*vertw);
vert.coord[1] = (vert.coord[1]+vertw) / (2*vertw); vert.coord[1] = (vert.coord[1]+vertw) / (2*vertw);
vert.coord[2] = (vert.coord[2]+vertw) / (2*vertw); vert.coord[2] = (vert.coord[2]+vertw) / (2*vertw);
vert.texcoord[0] /= vertw;
vert.texcoord[1] /= vertw;
//CONSIDER: do we need to guarantee that these are in bounds? perhaps not. //CONSIDER: do we need to guarantee that these are in bounds? perhaps not.
//vert.coord[0] = max(0.0f,min(1.0f,vert.coord[0])); //vert.coord[0] = max(0.0f,min(1.0f,vert.coord[0]));
//vert.coord[1] = max(0.0f,min(1.0f,vert.coord[1])); //vert.coord[1] = max(0.0f,min(1.0f,vert.coord[1]));
//vert.coord[2] = max(0.0f,min(1.0f,vert.coord[2])); //vert.coord[2] = max(0.0f,min(1.0f,vert.coord[2]));
//perspective-correct the colors
vert.fcolor[0] /= vertw;
vert.fcolor[1] /= vertw;
vert.fcolor[2] /= vertw;
//viewport transformation //viewport transformation
VIEWPORT viewport; VIEWPORT viewport;
viewport.decode(poly.poly->viewport); viewport.decode(poly.poly->viewport);