#include "my_gl.h"
using namespace std;
// MV变换
mat<4, 4> mv_mat(vec3 eye, vec3 center, vec3 up) {
vec3 gaze = normalized(center - eye);
vec3 z = -1 * gaze;
vec3 x = normalized(cross(gaze, up));
vec3 y = cross(z, x);
mat<4, 4> T = identity<4>();
T[0][3] = -eye.x;
T[1][3] = -eye.y;
T[2][3] = -eye.z;
mat<4, 4> R = identity<4>();
for (int i = 0; i < 3; i++) {
R[0][i] = x[i];
R[1][i] = y[i];
R[2][i] = z[i];
}
return R * T;
}
// 透视变换:将视锥变为标准立方体
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};
}
// 计算每个顶点的法向量
vector<vec3> calc_vertex_normal(Model* model) {
vector<vec3> vertex_normals(model->nverts(), vec3{0, 0, 0});
// 遍历三角形累加分量
for (int i = 0; i < model->nfaces(); i++) {
// 提取三角形顶点坐标
vec3 v[3];
for (int j = 0; j < 3; j++) {
v[j] = model->vert(i, j);
}
// 计算三角形法向量和权重
vec3 n = cross(v[2] - v[0], v[1] - v[0]);
float area = norm(n) / 2.;
n = normalized(n);
// 给三个顶点法向量加分量
for (int j = 0; j < 3; j++) {
int vertex_idx = model->index(i, j);
vertex_normals[vertex_idx] = vertex_normals[vertex_idx] + n * area;
}
}
// 顶点法向量标准化
for (auto& n : vertex_normals) {
n = normalized(n);
}
return vertex_normals;
}
// 画三角形
void triangle(vec3* pts,
IShader& shader,
TGAImage& image,
float* zbuffer,
int width) {
// 确定包围盒大小
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));
}
// 遍历包围盒中的点
#pragma omp parallel for
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.};
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];
}
if (z < zbuffer[static_cast<int>(x + y * width)]) {
zbuffer[static_cast<int>(x + y * width)] = z;
TGAColor color = {255, 255, 255};
if (shader.fragment(bc, color)) {
continue;
}
image.set(x, y, color);
}
}
}
}