#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]