aurora-rendering-engine/src/object/polygon.cpp

644 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}
}