Lesson 04 : Buggy Perspective Projection
aditya-c2512 opened this issue · 1 comments
aditya-c2512 commented
My current code is similar to the one explained in the commit linked in Lesson 04, but I am not getting perfect results. For reference,
here is my main()
:
int main(int argc, char** argv)
{
if (3==argc)
{
model = new Model(argv[1],argv[2]);
} else
{
model = new Model("assets/obj/test.obj","assets/textures/test/diffuse.tga");
}
float *zbuffer = new float[width*height];
for (int i=width*height; i--; zbuffer[i] = -depth);
Vec3f camera(0,0,3);
Matrix Projection = Matrix::identity();
Matrix ViewPort = viewport(width/8, height/8, width*3/4, height*3/4);
Projection[3][2] = -1.f/camera.z;
TGAImage image(width, height, TGAImage::RGB);
Vec3f light_dir(0,0,-1);
TGAImage diffuse = model->diffuse();
for (int i=0; i<model->nfaces(); i++)
{
std::vector<int> face = model->face(i);
Vec3f world_coords[3];
Vec3f pts[3];
for (int j=0; j<3; j++)
{
Vec3f v = model->vert(face[j]);
pts[j] = m2v(ViewPort * Projection * v2m(v));
// pts[j] = world2screen(v);
world_coords[j] = v;
}
Vec3f n = cross((world_coords[2]-world_coords[0]),(world_coords[1]-world_coords[0]));
n.normalize();
float intensity = n*light_dir;
if(intensity > 0)
{
Vec2f uvs[3];
for (int k=0; k<3; k++) {
uvs[k] = model->uv(i, k) * diffuse.get_width();
}
std::cout << pts[0] << "\t||\t" << pts[1] << "\t||\t" << pts[2] << std::endl;
triangle(pts, zbuffer, image, diffuse, uvs, intensity);
}
}
image.flip_vertically(); // i want to have the origin at the left bottom corner of the image
image.write_tga_file("renders/lesson_04.tga");
delete model;
return 0;
}
Here are my helper functions
triangle(...)
:
void triangle(Vec3f *pts, float *zbuffer, TGAImage &image, TGAImage& diffuse, Vec2f* uvs, float intensity)
{
Vec2f bboxmin( std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
Vec2f bboxmax(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max());
Vec2f clamp(image.get_width()-1, image.get_height()-1);
for (int i=0; i<3; i++)
{
for (int j=0; j<2; j++)
{
bboxmin[j] = std::max(0.f, std::min(bboxmin[j], pts[i][j]));
bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j]));
}
}
Vec3f P;
for (P.x=bboxmin.x; P.x<=bboxmax.x; P.x++)
{
for (P.y=bboxmin.y; P.y<=bboxmax.y; P.y++)
{
Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P);
if (bc_screen.x<0 || bc_screen.y<0 || bc_screen.z<0) continue;
P.z = 0;
Vec2f uv;
for(int i=0; i<3; i++)
{
P.z += pts[i][2]*bc_screen[i];
uv.x += uvs[i].x*bc_screen[i];
uv.y += uvs[i].y*bc_screen[i];
}
TGAColor color = diffuse.get(uv.x, uv.y);
color = TGAColor(color.r * intensity, color.g * intensity, color.b * intensity, 255);
if (zbuffer[int(P.x+P.y*width)]<P.z)
{
zbuffer[int(P.x+P.y*width)] = P.z;
image.set(P.x, P.y, color);
}
}
}
}
Vec3f m2v(Matrix41 m)
{
return Vec3f(m[0][0]/m[3][0], m[1][0]/m[3][0], m[2][0]/m[3][0]);
}
Matrix41 v2m(Vec3f v) {
Matrix41 m;
m[0][0] = v.x;
m[1][0] = v.y;
m[2][0] = v.z;
m[3][0] = 1.f;
return m;
}
Matrix viewport(int x, int y, int w, int h) {
Matrix m = Matrix::identity();
m[0][3] = x+w/2.f;
m[1][3] = y+h/2.f;
m[2][3] = depth/2.f;
m[0][0] = w/2.f;
m[1][1] = h/2.f;
m[2][2] = depth/2.f;
return m;
}
I am guessing there is something wrong with the Z-Buffer probably but after seeing the contents of z_buffer, I dont see any problems.
aditya-c2512 commented
NOTE :
For debugging purposes, I replaced the projection steps with a similar step :
for (int j=0; j<3; j++)
{
Vec3f v = model->vert(face[j]);
pts[j] = world2screen(m2v(Projection * v2m(v)));
world_coords[j] = v;
}
and it now gives the expected result. Only when I use pts[j] = m2v(ViewPort * Projection * v2m(v));
I seem to get the above buggy behavior. What seems to be the issue in my ViewPort matrix?