Here’s a method I wrote to fill in textured triangles. (x1, y1, z1) to (x3, y3, z3) are the 3 points of the triangle, (u1, v1) to (u3, v3) are the UV texture coordinates, texture[] is the texture image, sidelen is the length of the sides of the texture (should be a power of 2), pixels[] is the buffer to draw the triangle into, and width/height are the buffer dimensions.
UV coordinates are specified in texture image pixels, not 0.0 to 1.0 like in OpenGL. I didn’t see too much software rendering code posted so I thought I’d put something out there! There’s probably a lot of room for improvement; I’m not sure if floats/doubles are still dismally slow but perhaps a fixed-point implementation would be better.
Here’s a screenshot of the code in action:
Have fun with it.
public void perspectiveTriangle(int x1, int y1, double z1, int x2, int y2,
double z2, int x3, int y3, double z3, int u1, int v1, int u2,
int v2, int u3, int v3, int[] texture, int sidelen, int[] pixels,
int width, int height) {
double startx, endx, startu, endu, startv, endv, startz, endz;
double slope21, slope31, slope32;
double u1i, v1i, z1r, u2i, v2i, z2r, u3i, v3i, z3r;
double du21, dv21, dz21, du31, dv31, dz31, du32, dv32, dz32, dx, dy, du, dv, dz, u, v, z;
double slopeleft, sloperight, duleft, duright, dvleft, dvright, dzleft, dzright, tempz;
int temp, off, start, end, uu, vv;
if(y2 < y1) {
temp = x2; x2 = x1; x1 = temp;
temp = y2; y2 = y1; y1 = temp;
temp = u2; u2 = u1; u1 = temp;
temp = v2; v2 = v1; v1 = temp;
tempz = z2; z2 = z1; z1 = tempz;
}
if(y3 < y1) {
temp = x3; x3 = x1; x1 = temp;
temp = y3; y3 = y1; y1 = temp;
temp = u3; u3 = u1; u1 = temp;
temp = v3; v3 = v1; v1 = temp;
tempz = z3; z3 = z1; z1 = tempz;
}
if(y3 < y2) {
temp = x3; x3 = x2; x2 = temp;
temp = y3; y3 = y2; y2 = temp;
temp = u3; u3 = u2; u2 = temp;
temp = v3; v3 = v2; v2 = temp;
tempz = z3; z3 = z2; z2 = tempz;
}
if(y1 == y3)
return;
u1i = u1 / z1; u2i = u2 / z2; u3i = u3 / z3;
v1i = v1 / z1; v2i = v2 / z2; v3i = v3 / z3;
z1r = 1.0d / z1; z2r = 1.0d / z2; z3r = 1.0d / z3;
dy = 1.0d / (y2 - y1);
slope21 = (double) (x2 - x1) * dy;
du21 = (double) (u2i - u1i) * dy;
dv21 = (double) (v2i - v1i) * dy;
dz21 = (double) (z2r - z1r) * dy;
dy = 1.0d / (y3 - y1);
slope31 = (double) (x3 - x1) * dy;
du31 = (double) (u3i - u1i) * dy;
dv31 = (double) (v3i - v1i) * dy;
dz31 = (double) (z3r - z1r) * dy;
dy = 1.0d / (y3 - y2);
slope32 = (double) (x3 - x2) * dy;
du32 = (double) (u3i - u2i) * dy;
dv32 = (double) (v3i - v2i) * dy;
dz32 = (double) (z3r - z2r) * dy;
startx = endx = x1;
startu = endu = u1i;
startv = endv = v1i;
startz = endz = z1r;
if(y1 != y2) {
if(slope21 > slope31) {
slopeleft = slope31;
sloperight = slope21;
duleft = du31;
duright = du21;
dvleft = dv31;
dvright = dv21;
dzleft = dz31;
dzright = dz21;
} else {
slopeleft = slope21;
sloperight = slope31;
duleft = du21;
duright = du31;
dvleft = dv21;
dvright = dv31;
dzleft = dz21;
dzright = dz31;
}
for(int y = y1; y != y2; y++) {
if(y > 0 && y < height) {
dx = endx - startx;
if(dx != 0) {
du = (endu - startu) / dx;
dv = (endv - startv) / dx;
dz = (endz - startz) / dx;
} else {
du = endu - startu;
dv = endv - startv;
dz = endz - startz;
}
u = startu;
v = startv;
z = startz;
off = y * width;
start = off + (int) startx;
end = off + (int) endx;
if(start < off) {
dx = -startx;
u += dx * du;
v += dx * dv;
z += dx * dz;
start = off;
}
if(end > off + width - 1)
end = off + width - 1;
while(start < end) {
uu = ((int) (u / z)) & (sidelen - 1);
vv = ((int) (v / z)) & (sidelen - 1);
pixels[start++] = texture[vv * sidelen + uu];
u += du;
v += dv;
z += dz;
}
}
startx += slopeleft;
endx += sloperight;
startu += duleft;
endu += duright;
startv += dvleft;
endv += dvright;
startz += dzleft;
endz += dzright;
}
} else {
if (x1 > x2) {
startx = x2;
endx = x1;
startu = u2i;
endu = u1i;
startv = v2i;
endv = v1i;
startz = z2r;
endz = z1r;
} else {
startx = x1;
endx = x2;
startu = u1i;
endu = u2i;
startv = v1i;
endv = v2i;
startz = z1r;
endz = z2r;
}
}
if(y2 != y3) {
if(slope32 > slope31) {
slopeleft = slope32;
sloperight = slope31;
duleft = du32;
duright = du31;
dvleft = dv32;
dvright = dv31;
dzleft = dz32;
dzright = dz31;
} else {
slopeleft = slope31;
sloperight = slope32;
duleft = du31;
duright = du32;
dvleft = dv31;
dvright = dv32;
dzleft = dz31;
dzright = dz32;
}
for(int y = y2; y != y3; y++) {
if(y > 0 && y < height) {
dx = endx - startx;
if(dx != 0) {
du = (endu - startu) / dx;
dv = (endv - startv) / dx;
dz = (endz - startz) / dx;
} else {
du = endu - startu;
dv = endv - startv;
dz = endz - startz;
}
u = startu;
v = startv;
z = startz;
off = y * width;
start = off + (int) startx;
end = off + (int) endx;
if(start < off) {
dx = -startx;
u += dx * du;
v += dx * dv;
z += dx * dz;
start = off;
}
if(end > off + width - 1)
end = off + width - 1;
while(start < end) {
uu = ((int) (u / z)) & (sidelen - 1);
vv = ((int) (v / z)) & (sidelen - 1);
pixels[start++] = texture[vv * sidelen + uu];
u += du;
v += dv;
z += dz;
}
}
startx += slopeleft;
endx += sloperight;
startu += duleft;
endu += duright;
startv += dvleft;
endv += dvright;
startz += dzleft;
endz += dzright;
}
}
}