#include <bits/stdc++.h>
#include "model.h"
#include "tgaimage.h"
using namespace std;
const int width = 1000;
const int height = 1000;
const int depth = 255;
vec3 light_dir = {0, 0, -1};
vec3 camera_pos = {0, 0, 3};
float fov = 45.0f;
float aspect = width / float(height);
float near = 0.1f;
float far = 1000.0f;
float t = tan(fov * 0.5 * M_PI / 180.0f) * near;
float b = -t;
float r = t * aspect;
float l = -r;
// 模型变换:模型的缩放、平移、旋转
mat<4, 4> model_mat() {
return identity<4>();
}
// 观察变换:摄像机位置和角度
mat<4, 4> view_mat() {
mat<4, 4> m = identity<4>();
m[2][3] = -camera_pos.z;
return m;
}
// 透视变换:将视锥变为标准立方体
mat<4, 4> projection_mat(float near,
float far,
float l,
float r,
float b,
float t) {
mat<4, 4> m;
m[0][0] = 2 * near / (r - l);
m[0][2] = (r + l) / (r - l);
m[1][1] = 2 * near / (t - b);
m[1][2] = (t + b) / (t - b);
m[2][2] = -(far + near) / (far - near);
m[2][3] = -2 * far * near / (far - near);
m[3][2] = -1;
m[3][3] = 0;
return m;
}
// 视口变换:从[-1,1]映射到屏幕
mat<4, 4> viewport_mat(int x, int y, int width, int height, int depth) {
mat<4, 4> m = identity<4>();
// 平移中心
m[0][3] = x + width / 2.;
m[1][3] = y + height / 2.;
m[2][3] = depth / 2.;
// 缩放
m[0][0] = width / 2.;
m[1][1] = height / 2.;
m[2][2] = depth / 2.;
return m;
}
// 求重心坐标
vec3 barycentric(vec3* pts, vec3 p) {
vec3 u = cross(
vec3{(pts[0].x - pts[2].x), (pts[1].x - pts[2].x), (pts[2].x - p.x)},
vec3{(pts[0].y - pts[2].y), (pts[1].y - pts[2].y), (pts[2].y - p.y)});
if (abs(u.z) < 1)
return vec3{-1, 1, 1};
return vec3{u.x / u.z, u.y / u.z, 1 - u.x / u.z - u.y / u.z};
}
// 画三角形
void triangle5(vec3* pts,
vec2* uvs,
TGAImage& image,
float* zbuffer,
Model* model,
float intensity) {
// 确定包围盒大小
vec2 bboxmin = {image.width() - 1., image.height() - 1.};
vec2 bboxmax = {0., 0.};
vec2 clamp = {image.width() - 1., image.height() - 1.};
for (int i = 0; i < 3; i++) {
bboxmin.x = max(0., min(bboxmin.x, pts[i].x));
bboxmin.y = max(0., min(bboxmin.y, pts[i].y));
bboxmax.x = min(clamp.x, max(bboxmax.x, pts[i].x));
bboxmax.y = min(clamp.y, max(bboxmax.y, pts[i].y));
}
// 遍历包围盒中的点
for (int x = bboxmin.x; x <= bboxmax.x; x++) {
for (int y = bboxmin.y; y <= bboxmax.y; y++) {
vec3 p = {static_cast<float>(x), static_cast<float>(y), 0.};
vec2 uvp = {0., 0.};
vec3 bc = barycentric(pts, p);
if (bc.x < 0 || bc.y < 0 || bc.z < 0)
continue;
// 计算zbuffer和纹理坐标
float z = 0.;
for (int i = 0; i < 3; i++) {
z += pts[i].z * bc[i];
uvp[0] += uvs[i][0] * bc[i];
uvp[1] += uvs[i][1] * bc[i];
}
if (z < zbuffer[static_cast<int>(x + y * width)]) {
zbuffer[static_cast<int>(x + y * width)] = z;
int tex_x = min(int(uvp.x * model->diffuse().width()),
model->diffuse().width() - 1);
int tex_y = min(int(uvp.y * model->diffuse().height()),
model->diffuse().height() - 1);
TGAColor color = model->diffuse().get(tex_x, tex_y);
unsigned char r = color[2] * intensity;
unsigned char g = color[1] * intensity;
unsigned char b = color[0] * intensity;
image.set(x, y, TGAColor{r, g, b});
}
}
}
}
int main(int argc, char** argv) {
Model* model = nullptr;
if (2 == argc) {
model = new Model(argv[1]);
} else {
model = new Model("obj/african_head.obj");
}
// zbuffer记录深度值
float* zbuffer = new float[width * height];
for (int i = 0; i < width * height; ++i) {
zbuffer[i] = numeric_limits<float>::infinity();
}
// 矩阵用于变换
mat<4, 4> Model = model_mat();
mat<4, 4> View = view_mat();
mat<4, 4> Projection = projection_mat(near, far, l, r, b, t);
mat<4, 4> MVP = Projection * View * Model;
mat<4, 4> Viewport = viewport_mat(0, 0, width, height, 255);
// 遍历三角形处理
TGAImage image(width, height, TGAImage::RGB);
for (int i = 0; i < model->nfaces(); i++) {
vec3 screen_coords[3];
vec3 world_coords[3];
for (int j = 0; j < 3; j++) {
vec3 v0 = model->vert(i, j);
world_coords[j] = v0;
vec4 v4 = pt2homo(v0);
vec4 proj = Viewport * MVP * v4;
proj = proj / proj[3];
screen_coords[j] = homo2pt(proj);
}
// 用叉乘计算法向量并单位化
vec3 n = cross(world_coords[2] - world_coords[0],
world_coords[1] - world_coords[0]);
vec3 unit_n = normalized(n);
// 计算光照强度
float intensity = unit_n * light_dir;
if (intensity > 0) {
vec2 uvs[3];
for (int j = 0; j < 3; j++) {
uvs[j] = model->uv(i, j);
}
triangle5(screen_coords, uvs, image, zbuffer, model, intensity);
}
}
image.write_tga_file("tri_head_proj.tga");
cerr << "Image written to tga" << endl;
delete[] zbuffer;
return 0;
}