Original Post
As title, I'm trying to do perspective correct texturing in a software renderer. So far I have non-perspective correct texturing by looping over the bounding box of a triangle, calculating the barycentric coords, then either rejecting the pixel or using the coords to do texturing. Simple (and slow) but works, and I'm trying not to worry *too* much about performance right now.
In particular, I'm trying to implement this as described by Erik Rufelt here, that is, some magic barycentric coord formula that I can't quite find/grasp that generates perspective correct interpolants, instead of going the long way and correcting them manually for every value to be interpolated.
Maxest's thesis lists pseudo code for this, but I can't figure out what the distance() pseudo instruction does, nor how it relates to my existing barycentric coord calculation, as it appears to be using a completely different approach.
So far, I have this:
Any pointers as to what I'm conceptually not getting would be helpful. Thanks.
In particular, I'm trying to implement this as described by Erik Rufelt here, that is, some magic barycentric coord formula that I can't quite find/grasp that generates perspective correct interpolants, instead of going the long way and correcting them manually for every value to be interpolated.
Maxest's thesis lists pseudo code for this, but I can't figure out what the distance() pseudo instruction does, nor how it relates to my existing barycentric coord calculation, as it appears to be using a completely different approach.
So far, I have this:
public void rasteriseTriangle(Vector3f ndc0, Colour4f c0, Vector2f uv0,
Vector3f ndc1, Colour4f c1, Vector2f uv1,
Vector3f ndc2, Colour4f c2, Vector2f uv2)
{
Vector2f p0 = ndcToScreen(ndc0);
Vector2f p1 = ndcToScreen(ndc1);
Vector2f p2 = ndcToScreen(ndc2);
// First find screen rect (clamped to screen bounds)
final int screenXMin = Math.max((int)Math.floor( Math.min(Math.min(p0.x, p1.x), p2.x) ), 0);
final int screenYMin = Math.max((int)Math.floor( Math.min(Math.min(p0.y, p1.y), p2.y) ), 0);
final int screenXMax = Math.min((int)Math.ceil( Math.max(Math.max(p0.x, p1.x), p2.x) ), backbuffer.getWidth()-1);
final int screenYMax = Math.min((int)Math.ceil( Math.max(Math.max(p0.y, p1.y), p2.y) ), backbuffer.getHeight()-1);
// Iterate
final float detT = (p1.y - p2.y) * (p0.x - p2.x) + (p2.x - p1.x) * (p0.y - p2.y);
// If detT == 0, then triangle is degenerate, so skip
if (detT != 0.0f)
{
// Loop over screen bounds
for (int y=screenYMin; y<=screenYMax; y++)
{
for (int x=screenXMin; x<=screenXMax; x++)
{
// l1 = (y2 - y3)(x - x3) + (x3 - x2)(y - y3) / detT
// l2 = (y3 - y1)(x - x3) + (x1 - x3)(y - y3) / detT
// l3 = 1 - l2 - l1
final float l1 = ( (p1.y - p2.y) * ((float)x - p2.x) + (p2.x - p1.x) * ((float)y - p2.y) ) / detT;
final float l2 = ( (p2.y - p0.y) * ((float)x - p2.x) + (p0.x - p2.x) * ((float)y - p2.y) ) / detT;
final float l3 = 1.0f - l2 - l1;
// If barycentric coords are all in the range [0, 1] then we lie in the triangle
// - we add a small epsilon on the compare with zero check to fill the edges correctly
// - we don't actually have to compare againct one because with the equation above none
// all of the coords sum to one
final float epsilon = -0.000000059604645f;
if (l1 >= epsilon // && l1 <= 1.0f
&& l2 >= epsilon //&& l2 <= 1.0f
&& l3 >= epsilon //&& l3 <= 1.0f
)
{
// Within triangle!
// Find depth by lerping vertex depths
final float z = ndc0.z * l1 + ndc1.z * l2 + ndc2.z * l3;
// Depth buffer just has raw depth values
// Test depth buffer
final int pixelLoc = x + y * backbuffer.getWidth();
if (z <= depthBuffer[pixelLoc]) // TODO: Also test for z<1 for far plane clipping
{
// Write depth buffer
depthBuffer[pixelLoc] = z;
// Lerp vertex colours
final float r = c0.r * l1 + c1.r * l2 + c2.r * l3;
final float g = c0.g * l1 + c1.g * l2 + c2.g * l3;
final float b = c0.b * l1 + c1.b * l2 + c2.b * l3;
final float a = c0.a * l1 + c1.a * l2 + c2.a * l3;
// Lerp texture coords
final float u = uv0.x * l1 + uv1.x * l2 + uv2.x * l3;
final float v = uv0.y * l1 + uv1.y * l2 + uv2.y * l3;
// Sample texture
Colour4f textureColour = testTexture.sample(u, v);
final int red = (int)((r * 255.0f) * (textureColour.r));
final int green = (int)((g * 255.0f) * (textureColour.g));
final int blue = (int)((b * 255.0f) * (textureColour.b));
final int alpha = (int)((a * 255.0f) * (textureColour.a));
final int argb = packRgba(red, green, blue, alpha);
int[] pixels=((DataBufferInt)(backbuffer.getRaster().getDataBuffer())).getData();
pixels[pixelLoc] = argb;
}
}
}
}
}
}Any pointers as to what I'm conceptually not getting would be helpful. Thanks.