删除了Polygon类并添加Triangle类,改为从最基本的面片开始实现代码,后续再添加其他内容。

master
ternaryop8479 2026-01-31 22:55:29 +08:00
parent 9869136865
commit d99cb1cd83
5 changed files with 180 additions and 690 deletions

View File

@ -2,13 +2,13 @@
#define ARE_INCLUDE_OBJECT_OBJECT_SET_H
#include <object/object.h>
#include <object/polygon.h>
#include <object/triangle.h>
namespace are {
// Unified container for all objects.
struct ObjectSet {
std::vector<Polygon *> object_set;
std::vector<Triangle *> triangles;
};
}

View File

@ -1,45 +0,0 @@
#ifndef ARE_INCLUDE_OBJECT_POLYGON_H
#define ARE_INCLUDE_OBJECT_POLYGON_H
#include <basic/plane.h>
#include <basic/ray.h>
#include <basic/vec3.h>
#include <material/material.h>
#include <object/object.h>
#include <texture.h>
#include <vector>
namespace are {
// Polygon object class, derived from class Object.
class Polygon : public Object {
public:
// Constructor to initialize a Polygon object.
Polygon() = delete;
Polygon(const std::vector<Point3> &vertices, Material *material, Texture *texture);
// Destructors
~Polygon() override = default;
// Function to check if a point is inside the polygon.
bool point_in(const Point3 &point) const override;
// Function to check ray-polygon intersection.
bool intersect_ray(const Ray &ray, Point3 &hit_point) const override;
// Function to trace texture for polygon objects.
Texture trace_texture(const ObjectSet &object_set, const Point3 &viewport_origin_point) const override;
// Object properties
std::vector<Point3> vertices_; // Vertices of the polygon
Plane plane_; // The plane that based on
private:
// Graphic properties
Material *material_;
Texture *texture_;
};
}
#endif

50
include/object/triangle.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef ARE_INCLUDE_OBJECT_TRIANGLE_H
#define ARE_INCLUDE_OBJECT_TRIANGLE_H
#include <basic/plane.h>
#include <basic/ray.h>
#include <basic/vec3.h>
#include <material/material.h>
#include <object/object.h>
#include <texture.h>
#include <vector>
namespace are {
// Triangle object class, derived from class Object.
class Triangle : public Object {
public:
// Constructor to initialize a Triangle object.
Triangle() = delete;
Triangle(const Point3 &Q, const Vec3 &u, const Vec3 &v, Material *material, Texture *texture);
// Destructors
~Triangle() override = default;
// Function to check if a point is inside the triangle.
bool point_in(const Point3 &point) const override;
// Function to check ray-triangle intersection.
bool intersect_ray(const Ray &ray, Point3 &hit_point) const override;
// Function to trace texture for triangle objects.
Texture trace_texture(const ObjectSet &object_set, const Point3 &viewport_origin_point) const override;
// Function to get the vertices of the triangle.
const std::vector<Point3> &get_vertices() const;
private:
// Graphic properties
Material *material_;
Texture *texture_;
// Object properties
Point3 Q; // One vertex of the triangle.
Vec3 u, v; // Edge vectors from vertex Q.
std::vector<Point3> vertices_; // Vertices of the triangle.
};
}
#endif

View File

@ -1,643 +0,0 @@
#include <algorithm>
#include <basic/math.h>
#include <iostream>
#include <object/object_set.h>
#include <object/polygon.h>
namespace are {
Polygon::Polygon(const std::vector<Point3> &vertices, Material *material, Texture *texture) : material_(material), texture_(texture), vertices_(vertices) {
// Initialize plane based on vertices.
// Assumes the polygon is planar and defined by at least 3 non-collinear vertices.
if (vertices_.size() < 3) {
// Degenerate polygon: assign default plane to avoid undefined behavior.
plane_.normal = Vec3(0, 1, 0);
plane_.d = 0;
return;
}
// Compute normal using the first three vertices.
Vec3 v0 = vertices_[0];
Vec3 v1 = vertices_[1];
Vec3 v2 = vertices_[2];
Vec3 u = v1 - v0;
Vec3 v = v2 - v0;
Vec3 n = u.cross(v);
double len = n.length();
if (len > GEOMETRY_EPSILON) {
plane_.normal = n / len;
} else {
// Degenerate case (collinear points), try to find a valid normal or default
plane_.normal = Vec3(0, 0, 0);
}
// Calculate plane constant d: N . P + d = 0 => d = -N . P
plane_.d = -plane_.normal.dot(v0);
}
bool Polygon::point_in(const Point3 &point) const {
if (vertices_.size() < 3) {
return false;
}
// Step 1: Check if the point lies on the polygon's plane.
double dist = plane_.normal.dot(point) + plane_.d;
if (std::fabs(dist) > GEOMETRY_EPSILON) {
return false;
}
// Step 2: Check if the point is within the polygon boundaries.
// Using the same-side method for convex polygons.
// The point must be on the same side of all edges defined by the polygon vertices.
Vec3 n = plane_.normal;
int n_verts = vertices_.size();
// Determine the expected sign of the cross product relative to the normal.
// We check the first edge to establish the reference side (or check all against the normal).
// Robust approach: Check that all cross products have the same sign (positive or negative) relative to N.
bool sign_determined = false;
bool positive_side = false;
for (int i = 0; i < n_verts; ++i) {
const Point3 &curr = vertices_[i];
const Point3 &next = vertices_[(i + 1) % n_verts];
// Edge vector
Vec3 edge = next - curr;
// Vector from vertex to point
Vec3 vec_to_p = point - curr;
// Cross product: edge x vec_to_p.
// The direction of this vector indicates which side of the edge the point is on.
Vec3 cross_prod = edge.cross(vec_to_p);
// Dot product with polygon normal to get signed distance (scaled)
double dot_val = cross_prod.dot(n);
// If the point is exactly on the edge (within epsilon), the dot_val is close to 0.
// Otherwise, we check consistency of the sign.
if (std::fabs(dot_val) > GEOMETRY_EPSILON) {
if (!sign_determined) {
sign_determined = true;
positive_side = (dot_val > 0);
} else {
// If the current sign differs from the reference sign, point is outside.
if ((dot_val > 0) != positive_side) {
return false;
}
}
}
}
return true;
}
bool Polygon::intersect_ray(const Ray &ray, Point3 &hit_point) const {
// Step 1: Intersect ray with the infinite plane.
// Plane equation: N . P + d = 0
// Ray equation: P(t) = Q + t * D
// Substitute: N . (Q + t * D) + d = 0
// Solve for t: t = -(N . Q + d) / (N . D)
double denom = plane_.normal.dot(ray.D);
// Check if ray is parallel to the plane.
if (std::fabs(denom) < GEOMETRY_EPSILON) {
return false;
}
double t = -(plane_.normal.dot(ray.Q) + plane_.d) / denom;
// Check if intersection is behind the ray origin.
if (t < GEOMETRY_EPSILON) {
return false;
}
// Calculate the candidate intersection point.
Point3 candidate_point = ray.Q + t * ray.D;
// Step 2: Check if the intersection point lies inside the polygon boundaries.
if (point_in(candidate_point)) {
hit_point = candidate_point;
return true;
}
return false;
}
static int diguicishu = 0;
Texture Polygon::trace_texture(const ObjectSet &object_set, const Point3 &viewport_origin_point) const {
// 1. 拷贝当前纹理的副本作为返回值
Texture result(texture_->width_, texture_->height_, Color3(0, 0, 0));
for (int y = 0; y < texture_->height_; ++y) {
for (int x = 0; x < texture_->width_; ++x) {
result.pixel(x, y) = texture_->pixel(x, y);
}
}
// 2. 检测当前纹理是否需要处理反射
Point3 dummy_new_origin;
if (!material_->reflect(plane_, viewport_origin_point, dummy_new_origin) || ++diguicishu >= 10) {
// 不需要处理反射,直接返回拷贝的副本
return result;
}
// 3. 建立当前Polygon平面的局部2D坐标系
Vec3 normal = plane_.normal.normalized();
// 选择两个正交的基向量 u_axis 和 v_axis
Vec3 u_axis, v_axis;
if (std::abs(normal.x()) < 0.9) {
u_axis = normal.cross(Vec3(1, 0, 0)).normalized();
} else {
u_axis = normal.cross(Vec3(0, 1, 0)).normalized();
}
v_axis = normal.cross(u_axis).normalized();
// 计算当前polygon顶点在平面坐标系中的UV边界最小包围盒
Point3 plane_origin = vertices_[0];
double min_u_self = std::numeric_limits<double>::max();
double max_u_self = std::numeric_limits<double>::lowest();
double min_v_self = std::numeric_limits<double>::max();
double max_v_self = std::numeric_limits<double>::lowest();
std::vector<std::pair<double, double>> self_uvs;
for (const auto &vertex : vertices_) {
Vec3 rel = vertex - plane_origin;
double u = rel.dot(u_axis);
double v = rel.dot(v_axis);
self_uvs.push_back({ u, v });
min_u_self = std::min(min_u_self, u);
max_u_self = std::max(max_u_self, u);
min_v_self = std::min(min_v_self, v);
max_v_self = std::max(max_v_self, v);
}
double width_self = max_u_self - min_u_self;
double height_self = max_v_self - min_v_self;
// 4. 构建视锥体边界平面由viewport_origin_point和当前polygon的边组成
std::vector<Plane> frustum_planes;
int n_verts = static_cast<int>(vertices_.size());
// 计算当前polygon的中心点
Point3 self_centroid(0, 0, 0);
for (const auto &v : vertices_) {
self_centroid += v;
}
self_centroid = self_centroid / static_cast<double>(n_verts);
for (int i = 0; i < n_verts; ++i) {
const Point3 &v1 = vertices_[i];
const Point3 &v2 = vertices_[(i + 1) % n_verts];
// 构建由viewport_origin_point, v1, v2三点确定的平面
Vec3 edge = v2 - v1;
Vec3 to_v1 = v1 - viewport_origin_point;
Vec3 frustum_normal = edge.cross(to_v1);
if (frustum_normal.near_zero()) {
continue; // 退化情况,跳过
}
frustum_normal.normalize();
// 确保法向量指向视锥体内部朝向polygon中心
Vec3 to_centroid = self_centroid - viewport_origin_point;
if (frustum_normal.dot(to_centroid) < 0) {
frustum_normal = -frustum_normal;
}
frustum_planes.push_back(Plane(viewport_origin_point, frustum_normal));
}
// 添加viewport平面本身确保物体在viewport的正面
Vec3 view_dir = (self_centroid - viewport_origin_point).normalized();
Plane near_plane(viewport_origin_point, view_dir);
// 5. 遍历object_set找出与视锥体相交的polygon
struct PolygonDistance {
Polygon *polygon;
double distance;
Point3 centroid;
};
std::vector<PolygonDistance> intersected_polygons;
for (Polygon *obj : object_set.object_set) {
// 跳过自身
if (obj == this) {
continue;
}
// 计算目标polygon的重心
Point3 target_centroid(0, 0, 0);
int target_n = static_cast<int>(obj->vertices_.size());
for (const auto &v : obj->vertices_) {
target_centroid += v;
}
target_centroid = target_centroid / static_cast<double>(target_n);
// 检测目标polygon是否与视锥体相交
bool intersects = false;
for (const Point3 &vertex : vertices_) { // 检查原点到当前polygon顶点的射线是否与目标polygon相交
Vec3 direction = vertex - viewport_origin_point;
if(direction.near_zero()) {
continue;
}
Ray ray(viewport_origin_point, direction);
Point3 hit;
// std::cout << "Checking hit: " << vertices_.size() << std::endl;
// 视锥体原点到viewport当前端点的连线与目标polugon相交
if(obj->intersect_ray(ray, hit)) {
// std::cout << "HIT!" << std::endl;
// 检测hit_point是否在viewport前面
double dist_to_hit = (hit - viewport_origin_point).length();
double dist_to_vertex = (vertex - viewport_origin_point).length();
if(dist_to_hit > dist_to_vertex - GEOMETRY_EPSILON) {
intersects = true;
break;
}
}
}
if (!intersects) { // 如果顶点都不在内部检查从viewport_origin_point到目标顶点的射线是否与当前polygon相交
for (const Point3 &vertex : obj->vertices_) {
Vec3 dir = vertex - viewport_origin_point;
if (dir.near_zero())
continue;
dir.normalize();
Ray ray(viewport_origin_point, dir);
Point3 hit;
if (intersect_ray(ray, hit)) {
// 确保目标顶点在hit点之后相对于viewport_origin_point
double dist_to_hit = (hit - viewport_origin_point).length();
double dist_to_vertex = (vertex - viewport_origin_point).length();
if (dist_to_vertex > dist_to_hit - GEOMETRY_EPSILON) {
intersects = true;
break;
}
}
}
}
if (!intersects) { // 检查目标polygon的边是否穿过视锥体
for (int i = 0; i < target_n && !intersects; ++i) {
const Point3 &edge_start = obj->vertices_[i];
const Point3 &edge_end = obj->vertices_[(i + 1) % target_n];
Vec3 edge_dir = edge_end - edge_start;
double edge_len = edge_dir.length();
if (edge_len < GEOMETRY_EPSILON)
continue;
edge_dir = edge_dir / edge_len;
Ray edge_ray(edge_start, edge_dir);
Point3 plane_hit;
if (plane_.intersect_ray(edge_ray, plane_hit)) {
double t = (plane_hit - edge_start).dot(edge_dir);
if (t > GEOMETRY_EPSILON && t < edge_len - GEOMETRY_EPSILON) {
// 交点在边上检查是否在当前polygon内
if (point_in(plane_hit)) {
intersects = true;
}
}
}
}
}
if (intersects) {
double dist = (target_centroid - viewport_origin_point).length();
intersected_polygons.push_back({ obj, dist, target_centroid });
}
}
std::cout << "Hitted" << intersected_polygons.size() << " polygons." << std::endl;
// 6. 按距离降序排序(远的先绘制,近的后绘制覆盖)
std::sort(intersected_polygons.begin(), intersected_polygons.end(),
[](const PolygonDistance &a, const PolygonDistance &b) {
return a.distance > b.distance;
});
int i = 0;
// 7. 遍历排序后的polygon数组进行纹理映射
for (const auto &pd : intersected_polygons) {
Polygon *target_polygon = pd.polygon;
// 计算目标polygon每个顶点投影到当前平面的UV坐标
std::vector<std::pair<double, double>> projected_uvs;
bool projection_valid = true;
for (const Point3 &vertex : target_polygon->vertices_) {
Vec3 dir = vertex - viewport_origin_point;
if (dir.near_zero()) {
projection_valid = false;
break;
}
dir.normalize();
Ray ray(viewport_origin_point, dir);
Point3 intersection;
if (!plane_.intersect_ray(ray, intersection)) {
projection_valid = false;
break;
}
// 检查交点是否在viewport_origin_point和顶点之间的正确方向
Vec3 to_intersection = intersection - viewport_origin_point;
Vec3 to_vertex = vertex - viewport_origin_point;
if (to_intersection.dot(to_vertex) < GEOMETRY_EPSILON) {
projection_valid = false;
break;
}
// 计算交点的2D UV坐标
Vec3 rel = intersection - plane_origin;
double u = rel.dot(u_axis);
double v = rel.dot(v_axis);
projected_uvs.push_back({ u, v });
}
if (!projection_valid || projected_uvs.size() != target_polygon->vertices_.size()) {
continue;
}
// 获取反射后的viewport origin用于递归调用
Point3 reflected_origin;
if (!material_->reflect(target_polygon->plane_, viewport_origin_point, reflected_origin)) {
continue;
}
// 递归获取目标polygon的纹理
Texture target_texture = target_polygon->trace_texture(object_set, reflected_origin);
// target_texture.save_texture(std::to_string(diguicishu) + "_" + std::to_string(i++) + ".ppm");
// 计算目标polygon顶点在其自身纹理中的UV坐标最小包围盒原则
Vec3 target_normal = target_polygon->plane_.normal.normalized();
Vec3 target_u_axis, target_v_axis;
if (std::abs(target_normal.x()) < 0.9) {
target_u_axis = target_normal.cross(Vec3(1, 0, 0)).normalized();
} else {
target_u_axis = target_normal.cross(Vec3(0, 1, 0)).normalized();
}
target_v_axis = target_normal.cross(target_u_axis).normalized();
Point3 target_plane_origin = target_polygon->vertices_[0];
double target_min_u = std::numeric_limits<double>::max();
double target_max_u = std::numeric_limits<double>::lowest();
double target_min_v = std::numeric_limits<double>::max();
double target_max_v = std::numeric_limits<double>::lowest();
std::vector<std::pair<double, double>> target_local_uvs;
for (const auto &vertex : target_polygon->vertices_) {
Vec3 rel = vertex - target_plane_origin;
double u = rel.dot(target_u_axis);
double v = rel.dot(target_v_axis);
target_local_uvs.push_back({ u, v });
target_min_u = std::min(target_min_u, u);
target_max_u = std::max(target_max_u, u);
target_min_v = std::min(target_min_v, v);
target_max_v = std::max(target_max_v, v);
}
double target_width = target_max_u - target_min_u;
double target_height = target_max_v - target_min_v;
if (target_width < GEOMETRY_EPSILON || target_height < GEOMETRY_EPSILON) {
continue;
}
// 将目标polygon的局部UV转换为纹理坐标0到1范围然后映射到像素
std::vector<std::pair<double, double>> target_tex_coords;
for (const auto &uv : target_local_uvs) {
double tex_u = (uv.first - target_min_u) / target_width;
double tex_v = (uv.second - target_min_v) / target_height;
target_tex_coords.push_back({ tex_u, tex_v });
}
// 计算投影多边形的边界框
double proj_min_u = std::numeric_limits<double>::max();
double proj_max_u = std::numeric_limits<double>::lowest();
double proj_min_v = std::numeric_limits<double>::max();
double proj_max_v = std::numeric_limits<double>::lowest();
for (const auto &uv : projected_uvs) {
proj_min_u = std::min(proj_min_u, uv.first);
proj_max_u = std::max(proj_max_u, uv.first);
proj_min_v = std::min(proj_min_v, uv.second);
proj_max_v = std::max(proj_max_v, uv.second);
}
// 裁剪到当前polygon的UV范围
proj_min_u = std::max(proj_min_u, min_u_self);
proj_max_u = std::min(proj_max_u, max_u_self);
proj_min_v = std::max(proj_min_v, min_v_self);
proj_max_v = std::min(proj_max_v, max_v_self);
if (proj_min_u >= proj_max_u || proj_min_v >= proj_max_v) {
continue;
}
// 遍历结果纹理的每个像素,进行逆映射
for (int py = 0; py < result.height_; ++py) {
for (int px = 0; px < result.width_; ++px) {
// 将像素坐标转换为当前polygon平面的UV坐标
double uv_u = min_u_self + (static_cast<double>(px) + 0.5) / result.width_ * width_self;
double uv_v = min_v_self + (static_cast<double>(py) + 0.5) / result.height_ * height_self;
// 检查该点是否在投影多边形内(使用重心坐标或射线法)
// 使用射线法检测点是否在多边形内
bool inside_projected = false;
int num_projected = static_cast<int>(projected_uvs.size());
int crossings = 0;
for (int i = 0; i < num_projected; ++i) {
const auto &p1 = projected_uvs[i];
const auto &p2 = projected_uvs[(i + 1) % num_projected];
if ((p1.second <= uv_v && p2.second > uv_v) || (p2.second <= uv_v && p1.second > uv_v)) {
double t = (uv_v - p1.second) / (p2.second - p1.second);
double x_intersect = p1.first + t * (p2.first - p1.first);
if (uv_u < x_intersect) {
crossings++;
}
}
}
inside_projected = (crossings % 2) == 1;
if (!inside_projected) {
continue;
}
// 检查是否在当前polygon内
Point3 world_point = plane_origin + uv_u * u_axis + uv_v * v_axis;
if (!point_in(world_point)) {
continue;
}
// 使用重心坐标进行纹理映射(适用于任意凸多边形)
// 对于三角形,使用重心坐标;对于其他多边形,使用广义重心坐标
// 计算广义重心坐标Mean Value Coordinates
std::vector<double> weights(num_projected, 0.0);
double weight_sum = 0.0;
bool on_vertex = false;
int vertex_idx = -1;
bool on_edge = false;
int edge_idx = -1;
double edge_t = 0.0;
for (int i = 0; i < num_projected; ++i) {
double dx = projected_uvs[i].first - uv_u;
double dy = projected_uvs[i].second - uv_v;
double dist = std::sqrt(dx * dx + dy * dy);
if (dist < GEOMETRY_EPSILON) {
on_vertex = true;
vertex_idx = i;
break;
}
}
if (on_vertex) {
// 点在顶点上
double tex_x = target_tex_coords[vertex_idx].first * (target_texture.width_ - 1);
double tex_y = target_tex_coords[vertex_idx].second * (target_texture.height_ - 1);
int src_x = std::clamp(static_cast<int>(tex_x + 0.5), 0, target_texture.width_ - 1);
int src_y = std::clamp(static_cast<int>(tex_y + 0.5), 0, target_texture.height_ - 1);
result.pixel(px, py) = target_texture.pixel(src_x, src_y);
continue;
}
// 检查是否在边上
for (int i = 0; i < num_projected && !on_edge; ++i) {
const auto &p1 = projected_uvs[i];
const auto &p2 = projected_uvs[(i + 1) % num_projected];
double edge_dx = p2.first - p1.first;
double edge_dy = p2.second - p1.second;
double edge_len_sq = edge_dx * edge_dx + edge_dy * edge_dy;
if (edge_len_sq < GEOMETRY_EPSILON * GEOMETRY_EPSILON)
continue;
double t = ((uv_u - p1.first) * edge_dx + (uv_v - p1.second) * edge_dy) / edge_len_sq;
if (t < 0.0 || t > 1.0)
continue;
double closest_x = p1.first + t * edge_dx;
double closest_y = p1.second + t * edge_dy;
double dist_to_edge = std::sqrt((uv_u - closest_x) * (uv_u - closest_x) + (uv_v - closest_y) * (uv_v - closest_y));
if (dist_to_edge < GEOMETRY_EPSILON) {
on_edge = true;
edge_idx = i;
edge_t = t;
}
}
if (on_edge) {
// 点在边上,线性插值
int i1 = edge_idx;
int i2 = (edge_idx + 1) % num_projected;
double tex_u_interp = target_tex_coords[i1].first * (1.0 - edge_t) + target_tex_coords[i2].first * edge_t;
double tex_v_interp = target_tex_coords[i1].second * (1.0 - edge_t) + target_tex_coords[i2].second * edge_t;
double tex_x = tex_u_interp * (target_texture.width_ - 1);
double tex_y = tex_v_interp * (target_texture.height_ - 1);
int src_x = std::clamp(static_cast<int>(tex_x + 0.5), 0, target_texture.width_ - 1);
int src_y = std::clamp(static_cast<int>(tex_y + 0.5), 0, target_texture.height_ - 1);
result.pixel(px, py) = target_texture.pixel(src_x, src_y);
continue;
}
// 使用Mean Value Coordinates计算广义重心坐标
for (int i = 0; i < num_projected; ++i) {
int prev = (i - 1 + num_projected) % num_projected;
int next = (i + 1) % num_projected;
double dx_i = projected_uvs[i].first - uv_u;
double dy_i = projected_uvs[i].second - uv_v;
double r_i = std::sqrt(dx_i * dx_i + dy_i * dy_i);
double dx_prev = projected_uvs[prev].first - uv_u;
double dy_prev = projected_uvs[prev].second - uv_v;
double r_prev = std::sqrt(dx_prev * dx_prev + dy_prev * dy_prev);
double dx_next = projected_uvs[next].first - uv_u;
double dy_next = projected_uvs[next].second - uv_v;
double r_next = std::sqrt(dx_next * dx_next + dy_next * dy_next);
// 计算角度
auto compute_tan_half_angle = [](double dx1, double dy1, double r1,
double dx2, double dy2, double r2) -> double {
double dot = dx1 * dx2 + dy1 * dy2;
double cross = dx1 * dy2 - dy1 * dx2;
double cos_angle = dot / (r1 * r2);
cos_angle = std::clamp(cos_angle, -1.0, 1.0);
double sin_angle = cross / (r1 * r2);
// tan(angle/2) = sin(angle) / (1 + cos(angle))
double denom = 1.0 + cos_angle;
if (std::abs(denom) < GEOMETRY_EPSILON) {
return (sin_angle >= 0) ? 1e10 : -1e10;
}
return sin_angle / denom;
};
double tan_alpha_prev = compute_tan_half_angle(dx_prev, dy_prev, r_prev, dx_i, dy_i, r_i);
double tan_alpha_next = compute_tan_half_angle(dx_i, dy_i, r_i, dx_next, dy_next, r_next);
weights[i] = (tan_alpha_prev + tan_alpha_next) / r_i;
weight_sum += weights[i];
}
if (std::abs(weight_sum) < GEOMETRY_EPSILON) {
continue;
}
// 归一化权重并计算插值纹理坐标
double interp_tex_u = 0.0;
double interp_tex_v = 0.0;
for (int i = 0; i < num_projected; ++i) {
double normalized_weight = weights[i] / weight_sum;
interp_tex_u += normalized_weight * target_tex_coords[i].first;
interp_tex_v += normalized_weight * target_tex_coords[i].second;
}
// 将归一化纹理坐标转换为像素坐标
double tex_x = interp_tex_u * (target_texture.width_ - 1);
double tex_y = interp_tex_v * (target_texture.height_ - 1);
// 双线性插值采样
int x0 = static_cast<int>(std::floor(tex_x));
int y0 = static_cast<int>(std::floor(tex_y));
int x1 = x0 + 1;
int y1 = y0 + 1;
x0 = std::clamp(x0, 0, target_texture.width_ - 1);
x1 = std::clamp(x1, 0, target_texture.width_ - 1);
y0 = std::clamp(y0, 0, target_texture.height_ - 1);
y1 = std::clamp(y1, 0, target_texture.height_ - 1);
double fx = tex_x - std::floor(tex_x);
double fy = tex_y - std::floor(tex_y);
Color3 c00 = target_texture.pixel(x0, y0);
Color3 c10 = target_texture.pixel(x1, y0);
Color3 c01 = target_texture.pixel(x0, y1);
Color3 c11 = target_texture.pixel(x1, y1);
Color3 sampled_color = c00 * (1.0 - fx) * (1.0 - fy) + c10 * fx * (1.0 - fy) + c01 * (1.0 - fx) * fy + c11 * fx * fy;
// 将采样的颜色写入结果纹理(金属反射直接覆盖)
result.pixel(px, py) = sampled_color;
}
}
}
return result;
}
}

128
src/object/triangle.cpp Normal file
View File

@ -0,0 +1,128 @@
#include <object/triangle.h>
#include <basic/math.h>
#include <stdexcept>
#include <cmath>
namespace are {
// 构造函数实现
Triangle::Triangle(const Point3 &Q, const Vec3 &u, const Vec3 &v, Material *material, Texture *texture)
: material_(material), texture_(texture), Q(Q), u(u), v(v) {
// 检查 material 指针的合法性
if (material_ == nullptr) {
throw std::invalid_argument("Material pointer cannot be null");
}
// 检查 texture 指针的合法性
if (texture_ == nullptr) {
throw std::invalid_argument("Texture pointer cannot be null");
}
// 检查向量 u 是否为零向量
if (u.near_zero()) {
throw std::invalid_argument("Edge vector u cannot be zero vector");
}
// 检查向量 v 是否为零向量
if (v.near_zero()) {
throw std::invalid_argument("Edge vector v cannot be zero vector");
}
// 检查 u 和 v 是否共线(叉积为零向量则共线,无法构成三角形)
Vec3 cross_product = u.cross(v);
if (cross_product.near_zero()) {
throw std::invalid_argument("Edge vectors u and v cannot be collinear");
}
// 生成三角形的三个顶点
// 顶点1: Q
// 顶点2: Q + u
// 顶点3: Q + v
vertices_.reserve(3);
vertices_.push_back(Q);
vertices_.push_back(Q + u);
vertices_.push_back(Q + v);
}
// 检查点是否在三角形内部
bool Triangle::point_in(const Point3 &point) const {
// 使用重心坐标法判断点是否在三角形内
// 计算向量
Vec3 v0 = v;
Vec3 v1 = u;
Vec3 v2 = point - Q;
// 计算点积
double dot00 = v0.dot(v0);
double dot01 = v0.dot(v1);
double dot02 = v0.dot(v2);
double dot11 = v1.dot(v1);
double dot12 = v1.dot(v2);
// 计算重心坐标
double inv_denom = dot00 * dot11 - dot01 * dot01;
// 检查分母是否为零(退化三角形)
if (std::abs(inv_denom) < GEOMETRY_EPSILON) {
return false;
}
inv_denom = 1.0 / inv_denom;
double alpha = (dot11 * dot02 - dot01 * dot12) * inv_denom;
double beta = (dot00 * dot12 - dot01 * dot02) * inv_denom;
// 检查点是否在三角形内(包括边界)
return (alpha >= -GEOMETRY_EPSILON) &&
(beta >= -GEOMETRY_EPSILON) &&
(alpha + beta <= 1.0 + GEOMETRY_EPSILON);
}
// 光线与三角形相交检测
bool Triangle::intersect_ray(const Ray &ray, Point3 &hit_point) const {
// 使用 MöllerTrumbore 算法
Vec3 edge1 = u;
Vec3 edge2 = v;
Vec3 h = ray.D.cross(edge2);
double a = edge1.dot(h);
// 如果 a 接近 0光线与三角形平行
if (std::abs(a) < GEOMETRY_EPSILON) {
return false;
}
double f = 1.0 / a;
Vec3 s = ray.Q - Q;
double alpha = f * s.dot(h);
// 检查 alpha 是否在有效范围内
if (alpha < -GEOMETRY_EPSILON || alpha > 1.0 + GEOMETRY_EPSILON) {
return false;
}
Vec3 q = s.cross(edge1);
double beta = f * ray.D.dot(q);
// 检查 beta 和 alpha + beta 是否在有效范围内
if (beta < -GEOMETRY_EPSILON || alpha + beta > 1.0 + GEOMETRY_EPSILON) {
return false;
}
// 计算 t 值(光线参数)
double t = f * edge2.dot(q);
// 检查交点是否在光线正方向上
if (t > GEOMETRY_EPSILON) {
hit_point = ray.Q + t * ray.D;
return true;
}
return false;
}
// 获取三角形顶点
const std::vector<Point3> &Triangle::get_vertices() const {
return vertices_;
}
}