#include #include #include #include #include namespace are { Polygon::Polygon(const std::vector &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::max(); double max_u_self = std::numeric_limits::lowest(); double min_v_self = std::numeric_limits::max(); double max_v_self = std::numeric_limits::lowest(); std::vector> 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 frustum_planes; int n_verts = static_cast(vertices_.size()); // 计算当前polygon的中心点 Point3 self_centroid(0, 0, 0); for (const auto &v : vertices_) { self_centroid += v; } self_centroid = self_centroid / static_cast(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 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(obj->vertices_.size()); for (const auto &v : obj->vertices_) { target_centroid += v; } target_centroid = target_centroid / static_cast(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> 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::max(); double target_max_u = std::numeric_limits::lowest(); double target_min_v = std::numeric_limits::max(); double target_max_v = std::numeric_limits::lowest(); std::vector> 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> 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::max(); double proj_max_u = std::numeric_limits::lowest(); double proj_min_v = std::numeric_limits::max(); double proj_max_v = std::numeric_limits::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(px) + 0.5) / result.width_ * width_self; double uv_v = min_v_self + (static_cast(py) + 0.5) / result.height_ * height_self; // 检查该点是否在投影多边形内(使用重心坐标或射线法) // 使用射线法检测点是否在多边形内 bool inside_projected = false; int num_projected = static_cast(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 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(tex_x + 0.5), 0, target_texture.width_ - 1); int src_y = std::clamp(static_cast(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(tex_x + 0.5), 0, target_texture.width_ - 1); int src_y = std::clamp(static_cast(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(std::floor(tex_x)); int y0 = static_cast(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; } }