#include "tgaimage.h"
#include "model.h"
#include "geometry.h"
#include <iostream>
#include <map>
using namespace std;

const int width = 800;
const int height = 800;

Vec3f barycentric_(Vec3f* pts, Vec3f P) {
    Vec3f u = Vec3f(pts[2].x - pts[0].x, pts[1].x - pts[0].x, pts[0].x - P.x)
        ^ Vec3f(pts[2].y - pts[0].y, pts[1].y - pts[0].y, pts[0].y - P.y);
    if (std::abs(u.z) < 1) return Vec3f(-1, 1, 1);
    return Vec3f(1.f - (u.x + u.y) / u.z, u.y / u.z, u.x / u.z);
}

void triangle_barycentricCoordinates(Vec3f *pts, float *zbuffer, TGAImage &image, TGAColor color)
{
    // 定义包围框的左上角点和右下角点
    Vec2f leftTopPoint = Vec2f(numeric_limits<float>::max(), numeric_limits<float>::max());
    Vec2f rightBottomPoint = Vec2f(numeric_limits<float>::min(), numeric_limits<float>::min());
    // 计算3个顶点所构成三角形的包围框
    for (int i = 0; i < 3; i++) 
    {
        if (pts[i].x < leftTopPoint.x) 
        {
            leftTopPoint.x = pts[i].x;
        }
        if (pts[i].y < leftTopPoint.y) 
        {
            leftTopPoint.y = pts[i].y;
        }
        if (pts[i].y > rightBottomPoint.y) 
        {
            rightBottomPoint.y = pts[i].y;
        }
        if (pts[i].x > rightBottomPoint.x) 
        {
            rightBottomPoint.x = pts[i].x;
        }
    }
    // 遍历包围框的点,使用重心性质来计算每个点是否在三角形内部
    Vec3f P;
    for (P.x = leftTopPoint.x; P.x <= rightBottomPoint.x; P.x++)
    {
        for (P.y = leftTopPoint.y; P.y <= rightBottomPoint.y; P.y++)
        {
            // 计算P点在三角形内的重心坐标
            Vec3f barycentricP = barycentric_(pts, P);
            // 当重心坐标任一分量小于0,说明P点在三角形外部
            if (barycentricP.x < 0 || barycentricP.y < 0 || barycentricP.z < 0)
            {
                continue;
            }
            // 将三个顶点的z坐标值按照重心坐标比例加权相加,得到该点在三角形内的深度值
            P.z = 0;
            for (int i = 0; i < 3; i++) 
            {
                P.z += pts[i].z * barycentricP.x;
                P.z += pts[i].z * barycentricP.y;
                P.z += pts[i].z * barycentricP.z;
            }
            // 将缓存中的深度值与该点的深度值进行比较
            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 world2screen(Vec3f v) {
    return Vec3f(int((v.x + 1.) * width / 2. + .5), int((v.y + 1.) * height / 2. + .5), v.z);
}

int main()
{ 
    Model* model = NULL;
    model = new Model("african_head.obj");
    TGAImage image(width, height, TGAImage::RGB);

    float* zbuffer = new float[width * height];
    for (int i = width * height; i--; zbuffer[i] = -std::numeric_limits<float>::max());

    Vec3f light_dir(0, 0, -1); // define light_dir
    for (int i = 0; i < model->nfaces(); i++) {
        // 获取一个三角形面,一个面有3个点的索引
        vector<int> face = model->face(i);

        // 将三角形的三个顶点转成屏幕坐标存储到pts中
        Vec3f pts[3];
        for (int i = 0; i < 3; i++) pts[i] = world2screen(model->vert(face[i]));
        Vec2i screen_coords[3];
        Vec3f world_coords[3];
        for (int j = 0; j < 3; j++) {
            Vec3f v = model->vert(face[j]);
            // 获得三角形的三个顶点的屏幕坐标
            screen_coords[j] = Vec2i((v.x + 1.) * width / 2., (v.y + 1.) * height / 2.);
            // 获得世界坐标系中的三角形的三个顶点的坐标
            world_coords[j] = v;
        }

        // 计算三角形法向量
        Vec3f n = (world_coords[2] - world_coords[0]) ^ (world_coords[1] - world_coords[0]);
        n.normalize();

        // 计算光照强度,并渲染三角形
        float intensity = n * light_dir;
        if (intensity > 0) {
            
            triangle_barycentricCoordinates(pts, zbuffer, image, TGAColor(intensity * 255, intensity * 255, intensity * 255, 255));
        }

        
    }

    image.flip_vertically(); // i want to have the origin at the left bottom corner of the image
    image.write_tga_file("african_head_barycentric.tga");
    delete model;

    return 0;
}

运行结果与源文件[185103222023]