# Perspective correct texture mapping problem

3 replies to this topic

### #1kurlyak

Member

• Members
• 93 posts

Posted 01 February 2013 - 12:58 PM

Hi folks!

Now I am learning perspective correct texture mapping. I had wrote a bit of code. My code do not works correctly. I understand common sense of perspective correct text.map. x/z and y/z -> u/z and v/z. But x and y in 3D space but u and v in 2D space- what is means? How works perspective correct texture mapping inside? I read many articles in Internet by this issue- but I had caught only common comprehend. My code I posted below. What is wrong in my code?

void CMy3DApp::Draw_Textured_Triangle(My3DTriangle tri,
UCHAR *dest_buffer,   // pointer to video buffer
int mem_pitch)		// bytes per line, 320, 640 etc.
{
double du,dv,dudyl,dvdyl,dudyr,dvdyr,ui,vi,ul,vl,ur,vr;
int dx,dy,dyl,dyr,		  // general deltas
xi,yi,				  // the current interpolated x,y
xstart,
xend,
ystart,
yrestart,
yend,
xl,
dxdyl,
xr,
dxdyr;
int x0,y0,	// cached vertices
x1,y1,
x2,y2;
double tu0,tv0,	// cached vertices
tu1,tv1,
tu2,tv2;
int texture_shift2 = logbase2ofx[64];
irestart = INTERP_LHS;
// degenerate triangle
if ( ((tri.v1.x==tri.v2.x) && (tri.v2.x==tri.v3.x)) ||
((tri.v1.y==tri.v2.y) && (tri.v2.y==tri.v3.y)))
return;
My3DTexturedVert temp_1;
int temp;

// sort vertices
if (tri.v2.y < tri.v1.y)
{SWAP(tri.v1,tri.v2,temp_1);}
if (tri.v3.y < tri.v1.y)
{SWAP(tri.v1,tri.v3,temp_1);}
if (tri.v3.y < tri.v2.y)
{SWAP(tri.v2,tri.v3,temp_1);}

z0 = (double)1.0/ tri.v1.z;
z1 = (double)1.0/ tri.v2.z;
z2 = (double)1.0/ tri.v3.z;
x0  = (int)(tri.v1.x+0.5);
y0  = (int)(tri.v1.y+0.5);

x1  = (int)(tri.v2.x+0.5);
y1  = (int)(tri.v2.y+0.5);
x2  = (int)(tri.v3.x+0.5);
y2  = (int)(tri.v3.y+0.5);

tu0 = (double)(tri.v1.tu/ tri.v1.z);
tv0 = (double)(tri.v1.tv/ tri.v1.z);
tu1 = (double)(tri.v2.tu/ tri.v2.z);
tv1 = (double)(tri.v2.tv/ tri.v2.z);

tu2 = (double)(tri.v3.tu/ tri.v3.z);
tv2 = (double)(tri.v3.tv/ tri.v3.z);
// compute all deltas
// LHS
dyl = (y1 - y0);
dxdyl = ((x1  - x0)  << FIXP16_SHIFT)/dyl;
dudyl = (tu1 - tu0)/dyl;
dvdyl = (tv1 - tv0)/dyl;
dzdyl = z1 - z0;
// RHS
dyr = (y2 - y0);
dxdyr = ((x2 - x0)   << FIXP16_SHIFT)/dyr;
dudyr = (tu2 - tu0) /dyr;
dvdyr = (tv2 - tv0) /dyr;
dzdyr = z2 - z0;
xl = (x0 << FIXP16_SHIFT);
xr = (x0 << FIXP16_SHIFT);
ul = tu0;
vl = tv0;
ur = tu0;
vr = tv0;
zl = z0;
zr = z0;
ystart = y0;
// test if we need swap to keep rendering left to right
if (dxdyr < dxdyl)
{
SWAP(dxdyl,dxdyr,temp);
SWAP(dudyl,dudyr,temp);
SWAP(dvdyl,dvdyr,temp);
SWAP(xl,xr,temp);
SWAP(ul,ur,temp);
SWAP(vl,vr,temp);
SWAP(x1,x2,temp);
SWAP(y1,y2,temp);
SWAP(tu1,tu2,temp);
SWAP(tv1,tv2,temp);
// set interpolation restart
irestart = INTERP_RHS;
}
screen_ptr = dest_buffer + (ystart * mem_pitch);

for (yi = ystart; yi<=yend; yi++)
{
// compute span endpoints
xstart = ((xl + FIXP16_ROUND_UP) >> FIXP16_SHIFT);
xend   = ((xr + FIXP16_ROUND_UP) >> FIXP16_SHIFT);
// compute starting points for u,v interpolants
ui = ul;
vi = vl;
zi = zl;
// compute u,v interpolants
if ((dx = (xend - xstart))>0)
{
du = (ur - ul)/dx;
dv = (vr - vl)/dx;
dz = (zr - zl)/dx;
} // end if
else
{
du = (ur - ul);
dv = (vr - vl);
dz = (zr - zl);
}
int indx = xstart*4;
for (xi=xstart; xi<=xend; xi++)
{
double z = 1.0 / (1.0/zi);
int u = (ui / z) * z;
int v = (vi / z) * z;
int t = (u ) + ((v) << texture_shift2);
t= t*3;
screen_ptr[indx] = pRes[t];
indx++;
screen_ptr[indx] = pRes[t+1];
indx++;
screen_ptr[indx] = pRes[t+2];
indx++;
screen_ptr[indx] = 0;
indx++;
ui+=du;
vi+=dv;
zi+=dz;
} // end for xi
// interpolate u,v,x along right and left edge
xl+=dxdyl;
ul+=dudyl;
vl+=dvdyl;
zl+=dzdyl;
xr+=dxdyr;
ur+=dudyr;
vr+=dvdyr;
zr+=dzdyr;
screen_ptr+=mem_pitch;
if (yi==yrestart)
{
if (irestart == INTERP_LHS)
{

// LHS
dyl = (y2 - y1);
dxdyl = ((x2 - x1)   << FIXP16_SHIFT)/dyl;
dudyl = (tu2 - tu1)/dyl;
dvdyl = (tv2 - tv1)/dyl;
dzdyl = z2 - z1;
// set starting values
xl = (x1  << FIXP16_SHIFT);
ul = tu1;
vl = tv1;
zl = z1;
// interpolate down on LHS to even up
xl+=dxdyl;
ul+=dudyl;
vl+=dvdyl;
zl+=dzdyl;
}
else
{
// RHS

dyr = (y1 - y2);
dxdyr = ((x1 - x2)   << FIXP16_SHIFT)/dyr;
dudyr = (tu1 - tu2)/dyr;
dvdyr = (tv1 - tv2)/dyr;
dzdyr = z1 - z2;
// set starting values
xr = (x2  << FIXP16_SHIFT);
ur = tu2;
vr = tv2;
zr = z2;
// interpolate down on RHS to even up
xr+=dxdyr;
ur+=dudyr;
vr+=dvdyr;
zr+=dzdyr;

}
} //end for y



### #2Nick

Senior Member

• Members
• 1227 posts

Posted 02 February 2013 - 02:42 AM

With Haswell around the corner, now is a great time to learn software rendering!

Here's an excellent paper on perspective correct interpolation: Triangle Scan Conversion using 2D Homogeneous Coordinates (section 4 in particular). It might also be useful to read my article on Advanced Rasterization.

### #3Vilem Otte

Valued Member

• Members
• 345 posts

Posted 02 February 2013 - 05:11 PM

I second the Nick's awesome article on Advanced Rasterization. If you wish to see some *real* (=code where it works & could be used for some game, after like tons of optimizations) implementation of software renderer - look here - http://www.otte.cz/G..._Sample1.tar.gz - It's more likely proof-of-concept of how could useful software renderer work. For more real (=useful) implementation one would need to utilize multiple cores and hopefully SIMD intrinsics. On the other hand the code in current state is understandable, with intrinsics everywhere it would be a lot harder.

I also had an WIP article somewhere, which I wanted to publish here (talked with Reedbeta about it some time ago, though it still needs quite a bit of work on it), the problem was (and still is) that my work came in and I'm still too busy to work on the article.
My blog about game development (and not just game development) - http://gameprogramme...y.blogspot.com/

If you don't know how to speed up application, go "roarrrrrr!", hit the compiler with the club and use -O3 :D

### #4kurlyak

Member

• Members
• 93 posts

Posted 03 February 2013 - 11:02 AM