#include #include #include #include #include #include #include #include // ==================== 基础数学结构 ==================== struct Vec3 { double x, y, z; Vec3() : x(0), y(0), z(0) {} Vec3(double x, double y, double z) : x(x), y(y), z(z) {} Vec3 operator+(const Vec3& v) const { return Vec3(x + v.x, y + v.y, z + v.z); } Vec3 operator-(const Vec3& v) const { return Vec3(x - v.x, y - v.y, z - v.z); } Vec3 operator*(double t) const { return Vec3(x * t, y * t, z * t); } Vec3 operator*(const Vec3& v) const { return Vec3(x * v.x, y * v.y, z * v.z); } Vec3 operator/(double t) const { return Vec3(x / t, y / t, z / t); } Vec3& operator+=(const Vec3& v) { x += v.x; y += v.y; z += v.z; return *this; } Vec3& operator*=(double t) { x *= t; y *= t; z *= t; return *this; } double dot(const Vec3& v) const { return x * v.x + y * v.y + z * v.z; } Vec3 cross(const Vec3& v) const { return Vec3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); } double length() const { return std::sqrt(x * x + y * y + z * z); } double lengthSquared() const { return x * x + y * y + z * z; } Vec3 normalized() const { double len = length(); if (len < 1e-10) return Vec3(0, 0, 0); return *this / len; } // 关于平面对称点 static Vec3 reflect(const Vec3& point, const Vec3& planePoint, const Vec3& planeNormal) { Vec3 n = planeNormal.normalized(); double d = (point - planePoint).dot(n); return point - n * (2 * d); } }; Vec3 operator*(double t, const Vec3& v) { return v * t; } // 颜色结构(RGB,0-1范围) struct Color { double r, g, b; Color() : r(0), g(0), b(0) {} Color(double r, double g, double b) : r(r), g(g), b(b) {} Color operator+(const Color& c) const { return Color(r + c.r, g + c.g, b + c.b); } Color operator*(double t) const { return Color(r * t, g * t, b * t); } Color operator*(const Color& c) const { return Color(r * c.r, g * c.g, b * c.b); } Color& operator+=(const Color& c) { r += c.r; g += c.g; b += c.b; return *this; } Color clamped() const { return Color( std::max(0.0, std::min(1.0, r)), std::max(0.0, std::min(1.0, g)), std::max(0.0, std::min(1.0, b)) ); } static Color lerp(const Color& a, const Color& b, double t) { return Color(a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t, a.b + (b.b - a.b) * t); } }; // ==================== 纹理类 ==================== class Texture { public: int width, height; std::vector pixels; Texture() : width(0), height(0) {} Texture(int w, int h) : width(w), height(h), pixels(w * h) {} Texture(int w, int h, const Color& fillColor) : width(w), height(h), pixels(w * h, fillColor) {} Color getPixel(int x, int y) const { if (x < 0 || x >= width || y < 0 || y >= height) return Color(0, 0, 0); return pixels[y * width + x]; } void setPixel(int x, int y, const Color& c) { if (x < 0 || x >= width || y < 0 || y >= height) return; pixels[y * width + x] = c; } // 双线性插值采样(UV坐标0-1) Color sample(double u, double v) const { if (width == 0 || height == 0) return Color(0, 0, 0); u = std::max(0.0, std::min(1.0, u)); v = std::max(0.0, std::min(1.0, v)); double fx = u * (width - 1); double fy = v * (height - 1); int x0 = (int)fx; int y0 = (int)fy; int x1 = std::min(x0 + 1, width - 1); int y1 = std::min(y0 + 1, height - 1); double tx = fx - x0; double ty = fy - y0; Color c00 = getPixel(x0, y0); Color c10 = getPixel(x1, y0); Color c01 = getPixel(x0, y1); Color c11 = getPixel(x1, y1); Color c0 = Color::lerp(c00, c10, tx); Color c1 = Color::lerp(c01, c11, tx); return Color::lerp(c0, c1, ty); } // 在指定像素位置混合颜色(用于叠加) void blendPixel(int x, int y, const Color& c, double alpha = 1.0) { if (x < 0 || x >= width || y < 0 || y >= height) return; Color existing = getPixel(x, y); Color blended = Color( existing.r * (1 - alpha) + c.r * alpha, existing.g * (1 - alpha) + c.g * alpha, existing.b * (1 - alpha) + c.b * alpha ); setPixel(x, y, blended); } }; // ==================== 材质类型 ==================== enum class MaterialType { Diffuse, // 漫反射 Reflective, // 镜面反射 Emissive // 发光体 }; struct Material { MaterialType type; Color baseColor; double reflectivity; // 0-1,反射率 Material() : type(MaterialType::Diffuse), baseColor(0.8, 0.8, 0.8), reflectivity(0) {} Material(MaterialType t, const Color& c, double refl = 0.9) : type(t), baseColor(c), reflectivity(refl) {} }; // ==================== 三角形类 ==================== class Triangle { public: Vec3 v0, v1, v2; // 三个顶点 Vec3 normal; // 法向量 Material material; Texture* texture; // 可选的纹理 // UV坐标 Vec3 uv0, uv1, uv2; Triangle() : texture(nullptr) { uv0 = Vec3(0, 0, 0); uv1 = Vec3(1, 0, 0); uv2 = Vec3(0, 1, 0); } Triangle(const Vec3& a, const Vec3& b, const Vec3& c, const Material& mat = Material()) : v0(a), v1(b), v2(c), material(mat), texture(nullptr) { normal = (v1 - v0).cross(v2 - v0).normalized(); uv0 = Vec3(0, 0, 0); uv1 = Vec3(1, 0, 0); uv2 = Vec3(0, 1, 0); } void setUV(const Vec3& u0, const Vec3& u1, const Vec3& u2) { uv0 = u0; uv1 = u1; uv2 = u2; } // 获取三角形中心 Vec3 center() const { return (v0 + v1 + v2) / 3.0; } // 获取三角形面积 double area() const { return (v1 - v0).cross(v2 - v0).length() * 0.5; } // 计算重心坐标 bool barycentricCoords(const Vec3& p, double& u, double& v, double& w) const { Vec3 v0v1 = v1 - v0; Vec3 v0v2 = v2 - v0; Vec3 v0p = p - v0; double d00 = v0v1.dot(v0v1); double d01 = v0v1.dot(v0v2); double d11 = v0v2.dot(v0v2); double d20 = v0p.dot(v0v1); double d21 = v0p.dot(v0v2); double denom = d00 * d11 - d01 * d01; if (std::abs(denom) < 1e-10) return false; v = (d11 * d20 - d01 * d21) / denom; w = (d00 * d21 - d01 * d20) / denom; u = 1.0 - v - w; return true; } // 从重心坐标获取UV Vec3 getUVFromBarycentric(double u, double v, double w) const { return Vec3( u * uv0.x + v * uv1.x + w * uv2.x, u * uv0.y + v * uv1.y + w * uv2.y, 0 ); } // 射线与三角形相交测试(Möller–Trumbore算法) bool rayIntersect(const Vec3& origin, const Vec3& dir, double& t, double& u, double& v) const { const double EPSILON = 1e-8; Vec3 edge1 = v1 - v0; Vec3 edge2 = v2 - v0; Vec3 h = dir.cross(edge2); double a = edge1.dot(h); if (std::abs(a) < EPSILON) return false; double f = 1.0 / a; Vec3 s = origin - v0; u = f * s.dot(h); if (u < 0.0 || u > 1.0) return false; Vec3 q = s.cross(edge1); v = f * dir.dot(q); if (v < 0.0 || u + v > 1.0) return false; t = f * edge2.dot(q); return t > EPSILON; } // 获取指定UV处的颜色 Color getColorAtUV(double u, double v) const { if (texture != nullptr) { return texture->sample(u, v); } return material.baseColor; } }; // ==================== 视锥体类(由原点和三角形viewport组成) ==================== class Frustum { public: Vec3 apex; // 视锥体顶点(相机原点) Triangle viewport; // 底面三角形 // 四个平面(侧面3个 + 底面1个) // 每个平面由法向量和平面上的一点定义 std::array planeNormals; std::array planePoints; Frustum() {} Frustum(const Vec3& origin, const Triangle& vp) : apex(origin), viewport(vp) { buildPlanes(); } void buildPlanes() { // 三个侧面 Vec3 edge0 = viewport.v1 - viewport.v0; Vec3 edge1 = viewport.v2 - viewport.v1; Vec3 edge2 = viewport.v0 - viewport.v2; Vec3 toApex0 = apex - viewport.v0; Vec3 toApex1 = apex - viewport.v1; Vec3 toApex2 = apex - viewport.v2; // 侧面0: v0-v1-apex planeNormals[0] = edge0.cross(toApex0).normalized(); planePoints[0] = viewport.v0; // 侧面1: v1-v2-apex planeNormals[1] = edge1.cross(toApex1).normalized(); planePoints[1] = viewport.v1; // 侧面2: v2-v0-apex planeNormals[2] = edge2.cross(toApex2).normalized(); planePoints[2] = viewport.v2; // 底面(viewport平面) planeNormals[3] = viewport.normal; planePoints[3] = viewport.v0; // 确保法向量朝向视锥体内部 Vec3 center = (viewport.v0 + viewport.v1 + viewport.v2) / 3.0; Vec3 frustumCenter = (center + apex) / 2.0; for (int i = 0; i < 3; ++i) { Vec3 toCenter = frustumCenter - planePoints[i]; if (planeNormals[i].dot(toCenter) < 0) { planeNormals[i] = planeNormals[i] * (-1); } } // 底面法向量应该指向apex方向 Vec3 toApex = apex - planePoints[3]; if (planeNormals[3].dot(toApex) < 0) { planeNormals[3] = planeNormals[3] * (-1); } } // 检测点是否在视锥体内 bool containsPoint(const Vec3& p) const { for (int i = 0; i < 4; ++i) { Vec3 toPoint = p - planePoints[i]; if (planeNormals[i].dot(toPoint) < -1e-6) { return false; } } return true; } // 检测三角形是否与视锥体相交或被包含 bool intersectsTriangle(const Triangle& tri) const { // 简化检测:检查三角形的任意顶点是否在视锥体内, // 或者视锥体的任意边是否与三角形相交 // 检查三角形顶点 if (containsPoint(tri.v0) || containsPoint(tri.v1) || containsPoint(tri.v2)) { return true; } // 检查视锥体边与三角形的相交 std::vector> frustumEdges = { {apex, viewport.v0}, {apex, viewport.v1}, {apex, viewport.v2}, {viewport.v0, viewport.v1}, {viewport.v1, viewport.v2}, {viewport.v2, viewport.v0} }; for (const auto& edge : frustumEdges) { Vec3 dir = edge.second - edge.first; double t, u, v; if (tri.rayIntersect(edge.first, dir.normalized(), t, u, v)) { if (t >= 0 && t <= dir.length()) { return true; } } } // 检查三角形边与视锥体平面的相交 std::vector> triEdges = { {tri.v0, tri.v1}, {tri.v1, tri.v2}, {tri.v2, tri.v0} }; for (const auto& edge : triEdges) { Vec3 dir = edge.second - edge.first; double t, u, v; if (viewport.rayIntersect(edge.first, dir.normalized(), t, u, v)) { if (t >= 0 && t <= dir.length()) { return true; } } } return false; } }; // ==================== 前向声明 ==================== class Scene; Texture renderTriangleWithTriangle( const std::vector& triangles, const Vec3& cameraOrigin, const Triangle& currentTriangle, int estimatedWidth, int estimatedHeight, int depth, int maxDepth, const Scene& scene ); // ==================== 场景类 ==================== class Scene { public: std::vector triangles; Color ambientLight; Vec3 lightPosition; Color lightColor; Scene() : ambientLight(0.1, 0.1, 0.1), lightPosition(0, 1.9, 0), lightColor(1, 1, 1) {} void addTriangle(const Triangle& tri) { triangles.push_back(tri); } // 添加四边形(分解为两个三角形) void addQuad(const Vec3& v0, const Vec3& v1, const Vec3& v2, const Vec3& v3, const Material& mat) { Triangle t1(v0, v1, v2, mat); Triangle t2(v0, v2, v3, mat); // 设置UV坐标 t1.setUV(Vec3(0, 0, 0), Vec3(1, 0, 0), Vec3(1, 1, 0)); t2.setUV(Vec3(0, 0, 0), Vec3(1, 1, 0), Vec3(0, 1, 0)); triangles.push_back(t1); triangles.push_back(t2); } // 添加盒子 void addBox(const Vec3& minCorner, const Vec3& maxCorner, const Material& mat) { Vec3 v000 = minCorner; Vec3 v111 = maxCorner; Vec3 v100 = Vec3(maxCorner.x, minCorner.y, minCorner.z); Vec3 v010 = Vec3(minCorner.x, maxCorner.y, minCorner.z); Vec3 v001 = Vec3(minCorner.x, minCorner.y, maxCorner.z); Vec3 v110 = Vec3(maxCorner.x, maxCorner.y, minCorner.z); Vec3 v101 = Vec3(maxCorner.x, minCorner.y, maxCorner.z); Vec3 v011 = Vec3(minCorner.x, maxCorner.y, maxCorner.z); // 前面 (z = max) addQuad(v001, v101, v111, v011, mat); // 后面 (z = min) addQuad(v100, v000, v010, v110, mat); // 左面 (x = min) addQuad(v000, v001, v011, v010, mat); // 右面 (x = max) addQuad(v101, v100, v110, v111, mat); // 顶面 (y = max) addQuad(v010, v011, v111, v110, mat); // 底面 (y = min) addQuad(v000, v100, v101, v001, mat); } std::vector getTrianglePointers() { std::vector ptrs; for (auto& tri : triangles) { ptrs.push_back(&tri); } return ptrs; } }; // ==================== 工具函数 ==================== // 将点从相机原点投影到目标平面上 Vec3 projectPointToPlane(const Vec3& origin, const Vec3& point, const Vec3& planePoint, const Vec3& planeNormal) { Vec3 dir = (point - origin).normalized(); double denom = dir.dot(planeNormal); if (std::abs(denom) < 1e-10) return point; // 平行 double t = (planePoint - origin).dot(planeNormal) / denom; return origin + dir * t; } // 将三角形的纹理变换并绘制到目标纹理上 void drawTransformedTriangle( Texture& destTexture, const Triangle& destTriangle, // 目标三角形(viewport) const Triangle& srcTriangle, // 源三角形 const Texture& srcTexture, // 源纹理 const Vec3& cameraOrigin // 相机原点 ) { // 计算源三角形三个顶点投影到目标三角形平面上的位置 Vec3 proj0 = projectPointToPlane(cameraOrigin, srcTriangle.v0, destTriangle.v0, destTriangle.normal); Vec3 proj1 = projectPointToPlane(cameraOrigin, srcTriangle.v1, destTriangle.v0, destTriangle.normal); Vec3 proj2 = projectPointToPlane(cameraOrigin, srcTriangle.v2, destTriangle.v0, destTriangle.normal); // 将投影点转换为目标三角形的重心坐标 double u0, v0, w0, u1, v1, w1, u2, v2, w2; destTriangle.barycentricCoords(proj0, u0, v0, w0); destTriangle.barycentricCoords(proj1, u1, v1, w1); destTriangle.barycentricCoords(proj2, u2, v2, w2); // 转换为纹理坐标 Vec3 texCoord0 = destTriangle.getUVFromBarycentric(u0, v0, w0); Vec3 texCoord1 = destTriangle.getUVFromBarycentric(u1, v1, w1); Vec3 texCoord2 = destTriangle.getUVFromBarycentric(u2, v2, w2); // 在目标纹理上栅格化源三角形 // 计算边界框 double minU = std::min({texCoord0.x, texCoord1.x, texCoord2.x}); double maxU = std::max({texCoord0.x, texCoord1.x, texCoord2.x}); double minV = std::min({texCoord0.y, texCoord1.y, texCoord2.y}); double maxV = std::max({texCoord0.y, texCoord1.y, texCoord2.y}); int startX = std::max(0, (int)(minU * destTexture.width) - 1); int endX = std::min(destTexture.width - 1, (int)(maxU * destTexture.width) + 1); int startY = std::max(0, (int)(minV * destTexture.height) - 1); int endY = std::min(destTexture.height - 1, (int)(maxV * destTexture.height) + 1); // 使用重心坐标进行纹理映射 for (int py = startY; py <= endY; ++py) { for (int px = startX; px <= endX; ++px) { double u = (px + 0.5) / destTexture.width; double v = (py + 0.5) / destTexture.height; // 检查点是否在投影三角形内(使用2D重心坐标) Vec3 p(u, v, 0); Vec3 a(texCoord0.x, texCoord0.y, 0); Vec3 b(texCoord1.x, texCoord1.y, 0); Vec3 c(texCoord2.x, texCoord2.y, 0); Vec3 v0v = b - a; Vec3 v1v = c - a; Vec3 v2v = p - a; double d00 = v0v.x * v0v.x + v0v.y * v0v.y; double d01 = v0v.x * v1v.x + v0v.y * v1v.y; double d11 = v1v.x * v1v.x + v1v.y * v1v.y; double d20 = v2v.x * v0v.x + v2v.y * v0v.y; double d21 = v2v.x * v1v.x + v2v.y * v1v.y; double denom = d00 * d11 - d01 * d01; if (std::abs(denom) < 1e-10) continue; double baryV = (d11 * d20 - d01 * d21) / denom; double baryW = (d00 * d21 - d01 * d20) / denom; double baryU = 1.0 - baryV - baryW; // 检查是否在三角形内 if (baryU >= -0.001 && baryV >= -0.001 && baryW >= -0.001) { // 计算源纹理的UV坐标 double srcU = baryU * srcTriangle.uv0.x + baryV * srcTriangle.uv1.x + baryW * srcTriangle.uv2.x; double srcV = baryU * srcTriangle.uv0.y + baryV * srcTriangle.uv1.y + baryW * srcTriangle.uv2.y; Color srcColor = srcTexture.sample(srcU, srcV); destTexture.setPixel(px, py, srcColor); } } } } // 计算三角形投影到viewport上的近似像素面积 double estimateProjectedArea(const Triangle& tri, const Vec3& cameraOrigin, const Triangle& viewport, int viewportWidth, int viewportHeight) { Vec3 proj0 = projectPointToPlane(cameraOrigin, tri.v0, viewport.v0, viewport.normal); Vec3 proj1 = projectPointToPlane(cameraOrigin, tri.v1, viewport.v0, viewport.normal); Vec3 proj2 = projectPointToPlane(cameraOrigin, tri.v2, viewport.v0, viewport.normal); double u0, v0, w0, u1, v1, w1, u2, v2, w2; viewport.barycentricCoords(proj0, u0, v0, w0); viewport.barycentricCoords(proj1, u1, v1, w1); viewport.barycentricCoords(proj2, u2, v2, w2); Vec3 tc0 = viewport.getUVFromBarycentric(u0, v0, w0); Vec3 tc1 = viewport.getUVFromBarycentric(u1, v1, w1); Vec3 tc2 = viewport.getUVFromBarycentric(u2, v2, w2); // 计算2D三角形面积 double area = 0.5 * std::abs( (tc1.x - tc0.x) * (tc2.y - tc0.y) - (tc2.x - tc0.x) * (tc1.y - tc0.y) ); return area * viewportWidth * viewportHeight; } // ==================== 核心渲染函数 ==================== Texture renderTriangleWithTriangle( const std::vector& triangles, const Vec3& cameraOrigin, const Triangle& currentTriangle, int estimatedWidth, int estimatedHeight, int depth, int maxDepth, const Scene& scene ) { // 检查终止条件 if (depth >= maxDepth || estimatedWidth < 2 || estimatedHeight < 2) { // 返回基础颜色纹理 Texture result(std::max(1, estimatedWidth), std::max(1, estimatedHeight), currentTriangle.material.baseColor); return result; } // 如果是漫反射材质,直接返回基础颜色 if (currentTriangle.material.type == MaterialType::Diffuse) { Texture result(estimatedWidth, estimatedHeight, currentTriangle.material.baseColor); // 添加简单的光照计算 Vec3 toLight = (scene.lightPosition - currentTriangle.center()).normalized(); double diffuse = std::max(0.0, currentTriangle.normal.dot(toLight)); Color litColor = currentTriangle.material.baseColor * (0.3 + 0.7 * diffuse); for (int y = 0; y < estimatedHeight; ++y) { for (int x = 0; x < estimatedWidth; ++x) { result.setPixel(x, y, litColor); } } return result; } // 如果是发光材质,返回发光颜色 if (currentTriangle.material.type == MaterialType::Emissive) { Texture result(estimatedWidth, estimatedHeight, currentTriangle.material.baseColor); return result; } // 镜面反射材质 // 初始化当前面片的纹理(使用基础颜色) Texture currentTexture(estimatedWidth, estimatedHeight, currentTriangle.material.baseColor * 0.1); // 计算反射后的相机原点(关于当前面片对称) Vec3 reflectedOrigin = Vec3::reflect(cameraOrigin, currentTriangle.center(), currentTriangle.normal); // 构建视锥体 Frustum frustum(reflectedOrigin, currentTriangle); // 收集与视锥体相交的三角形 struct TriangleDistance { Triangle* tri; double distance; bool operator<(const TriangleDistance& other) const { return distance > other.distance; // 由远到近 } }; std::vector intersectedTriangles; for (Triangle* tri : triangles) { // 跳过当前三角形 if (tri->v0.x == currentTriangle.v0.x && tri->v0.y == currentTriangle.v0.y && tri->v0.z == currentTriangle.v0.z && tri->v1.x == currentTriangle.v1.x && tri->v1.y == currentTriangle.v1.y && tri->v1.z == currentTriangle.v1.z && tri->v2.x == currentTriangle.v2.x && tri->v2.y == currentTriangle.v2.y && tri->v2.z == currentTriangle.v2.z) { continue; } // 检查三角形是否在视锥体的"前方"(从反射原点看去) Vec3 toTriCenter = tri->center() - reflectedOrigin; Vec3 viewDir = (currentTriangle.center() - reflectedOrigin).normalized(); if (toTriCenter.dot(viewDir) <= 0) { continue; // 三角形在视锥体后方 } if (frustum.intersectsTriangle(*tri)) { double dist = (tri->center() - reflectedOrigin).length(); intersectedTriangles.push_back({tri, dist}); } } // 按距离排序(由远到近) std::sort(intersectedTriangles.begin(), intersectedTriangles.end()); // 遍历并渲染每个相交的三角形 for (const auto& td : intersectedTriangles) { Triangle* tri = td.tri; // 估计该三角形投影到当前面片上的像素大小 double projectedArea = estimateProjectedArea(*tri, reflectedOrigin, currentTriangle, estimatedWidth, estimatedHeight); int subWidth = std::max(2, (int)std::sqrt(projectedArea)); int subHeight = subWidth; // 递归获取该三角形的纹理 Texture subTexture = renderTriangleWithTriangle( triangles, reflectedOrigin, *tri, subWidth, subHeight, depth + 1, maxDepth, scene ); // 创建临时三角形用于绘制(带有获取的纹理) Triangle tempTri = *tri; tempTri.texture = &subTexture; // 将该三角形的纹理变换并绘制到当前纹理上 drawTransformedTriangle(currentTexture, currentTriangle, *tri, subTexture, reflectedOrigin); } // 混合基础颜色和反射结果 double refl = currentTriangle.material.reflectivity; for (int y = 0; y < estimatedHeight; ++y) { for (int x = 0; x < estimatedWidth; ++x) { Color reflected = currentTexture.getPixel(x, y); Color base = currentTriangle.material.baseColor * 0.1; Color mixed = Color( base.r * (1 - refl) + reflected.r * refl, base.g * (1 - refl) + reflected.g * refl, base.b * (1 - refl) + reflected.b * refl ); currentTexture.setPixel(x, y, mixed); } } return currentTexture; } // ==================== 相机类 ==================== class Camera { public: Vec3 position; Vec3 lookAt; Vec3 up; double fov; int width, height; // Viewport三角形 Triangle viewportTri1, viewportTri2; Camera(const Vec3& pos, const Vec3& look, const Vec3& upDir, double fieldOfView, int w, int h) : position(pos), lookAt(look), up(upDir), fov(fieldOfView), width(w), height(h) { setupViewport(); } void setupViewport() { Vec3 forward = (lookAt - position).normalized(); Vec3 right = forward.cross(up).normalized(); Vec3 upVec = right.cross(forward).normalized(); double aspectRatio = (double)width / height; double halfHeight = std::tan(fov * 0.5 * M_PI / 180.0); double halfWidth = aspectRatio * halfHeight; // Viewport在相机前方单位距离处 Vec3 center = position + forward; Vec3 bottomLeft = center - right * halfWidth - upVec * halfHeight; Vec3 bottomRight = center + right * halfWidth - upVec * halfHeight; Vec3 topLeft = center - right * halfWidth + upVec * halfHeight; Vec3 topRight = center + right * halfWidth + upVec * halfHeight; // 创建两个三角形组成viewport Material viewportMat(MaterialType::Diffuse, Color(0, 0, 0)); viewportTri1 = Triangle(bottomLeft, bottomRight, topRight, viewportMat); viewportTri2 = Triangle(bottomLeft, topRight, topLeft, viewportMat); // 设置UV坐标(用于纹理映射到最终图像) viewportTri1.setUV(Vec3(0, 1, 0), Vec3(1, 1, 0), Vec3(1, 0, 0)); // 注意Y轴翻转 viewportTri2.setUV(Vec3(0, 1, 0), Vec3(1, 0, 0), Vec3(0, 0, 0)); } void render(Scene& scene, const std::string& filename) { std::vector triPtrs = scene.getTrianglePointers(); int maxDepth = 4; std::cout << "Rendering viewport triangle 1..." << std::endl; Texture tex1 = renderTriangleWithTriangle(triPtrs, position, viewportTri1, width, height, 0, maxDepth, scene); std::cout << "Rendering viewport triangle 2..." << std::endl; Texture tex2 = renderTriangleWithTriangle(triPtrs, position, viewportTri2, width, height, 0, maxDepth, scene); // 合并两个三角形的纹理到最终图像 Texture finalImage(width, height); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { double u = (x + 0.5) / width; double v = (y + 0.5) / height; // 计算在viewport平面上的3D位置 Vec3 forward = (lookAt - position).normalized(); Vec3 right = forward.cross(up).normalized(); Vec3 upVec = right.cross(forward).normalized(); double aspectRatio = (double)width / height; double halfHeight = std::tan(fov * 0.5 * M_PI / 180.0); double halfWidth = aspectRatio * halfHeight; Vec3 center = position + forward; Vec3 point = center + right * (u * 2 - 1) * halfWidth + upVec * (1 - v * 2) * halfHeight; // 检查点在哪个三角形内 double bu, bv, bw; if (viewportTri1.barycentricCoords(point, bu, bv, bw) && bu >= 0 && bv >= 0 && bw >= 0) { Vec3 uv = viewportTri1.getUVFromBarycentric(bu, bv, bw); finalImage.setPixel(x, y, tex1.sample(uv.x, uv.y)); } else if (viewportTri2.barycentricCoords(point, bu, bv, bw) && bu >= 0 && bv >= 0 && bw >= 0) { Vec3 uv = viewportTri2.getUVFromBarycentric(bu, bv, bw); finalImage.setPixel(x, y, tex2.sample(uv.x, uv.y)); } } } // 写入PPM文件 writePPM(filename, finalImage); std::cout << "Image saved to " << filename << std::endl; } void writePPM(const std::string& filename, const Texture& image) { std::ofstream file(filename); file << "P3\n" << image.width << " " << image.height << "\n255\n"; for (int y = 0; y < image.height; ++y) { for (int x = 0; x < image.width; ++x) { Color c = image.getPixel(x, y).clamped(); int r = (int)(c.r * 255); int g = (int)(c.g * 255); int b = (int)(c.b * 255); file << r << " " << g << " " << b << "\n"; } } file.close(); } }; // ==================== 构建Cornell Box场景 ==================== void buildCornellBox(Scene& scene) { // Cornell Box尺寸:大约 [-1, 1] x [0, 2] x [-1, 1] double boxSize = 1.0; // 材质定义 Material whiteDiffuse(MaterialType::Diffuse, Color(0.75, 0.75, 0.75)); Material redDiffuse(MaterialType::Diffuse, Color(0.75, 0.15, 0.15)); Material greenDiffuse(MaterialType::Diffuse, Color(0.15, 0.75, 0.15)); Material blueReflective(MaterialType::Reflective, Color(0.2, 0.4, 0.8), 0.85); Material yellowReflective(MaterialType::Reflective, Color(0.8, 0.7, 0.2), 0.85); Material lightEmissive(MaterialType::Emissive, Color(1.0, 0.95, 0.9)); // 地板(白色) scene.addQuad( Vec3(-boxSize, 0, -boxSize), Vec3(boxSize, 0, -boxSize), Vec3(boxSize, 0, boxSize), Vec3(-boxSize, 0, boxSize), whiteDiffuse ); // 天花板(白色) scene.addQuad( Vec3(-boxSize, 2 * boxSize, boxSize), Vec3(boxSize, 2 * boxSize, boxSize), Vec3(boxSize, 2 * boxSize, -boxSize), Vec3(-boxSize, 2 * boxSize, -boxSize), whiteDiffuse ); // 后墙(白色) scene.addQuad( Vec3(-boxSize, 0, -boxSize), Vec3(-boxSize, 2 * boxSize, -boxSize), Vec3(boxSize, 2 * boxSize, -boxSize), Vec3(boxSize, 0, -boxSize), whiteDiffuse ); // 左墙(红色) scene.addQuad( Vec3(-boxSize, 0, boxSize), Vec3(-boxSize, 2 * boxSize, boxSize), Vec3(-boxSize, 2 * boxSize, -boxSize), Vec3(-boxSize, 0, -boxSize), redDiffuse ); // 右墙(绿色) scene.addQuad( Vec3(boxSize, 0, -boxSize), Vec3(boxSize, 2 * boxSize, -boxSize), Vec3(boxSize, 2 * boxSize, boxSize), Vec3(boxSize, 0, boxSize), greenDiffuse ); // 顶部灯光(发光面板) scene.addQuad( Vec3(-0.25, 1.99, -0.25), Vec3(0.25, 1.99, -0.25), Vec3(0.25, 1.99, 0.25), Vec3(-0.25, 1.99, 0.25), lightEmissive ); // 蓝色金属盒子(左侧,稍微旋转) double boxHeight1 = 0.6; double boxWidth1 = 0.35; Vec3 box1Center(-0.4, boxHeight1 / 2, -0.2); // 简化:使用轴对齐的盒子 scene.addBox( Vec3(box1Center.x - boxWidth1/2, 0, box1Center.z - boxWidth1/2), Vec3(box1Center.x + boxWidth1/2, boxHeight1, box1Center.z + boxWidth1/2), blueReflective ); // 黄色金属盒子(右侧,更高一些) double boxHeight2 = 0.9; double boxWidth2 = 0.35; Vec3 box2Center(0.35, boxHeight2 / 2, 0.15); scene.addBox( Vec3(box2Center.x - boxWidth2/2, 0, box2Center.z - boxWidth2/2), Vec3(box2Center.x + boxWidth2/2, boxHeight2, box2Center.z + boxWidth2/2), yellowReflective ); // 设置光源 scene.lightPosition = Vec3(0, 1.95, 0); scene.lightColor = Color(1, 1, 1); scene.ambientLight = Color(0.05, 0.05, 0.05); } // ==================== 主函数 ==================== int main() { std::cout << "=== Panel-based Cornell Box Renderer ===" << std::endl; std::cout << "Building scene..." << std::endl; Scene scene; buildCornellBox(scene); std::cout << "Scene contains " << scene.triangles.size() << " triangles." << std::endl; // 设置相机 Vec3 cameraPos(0, 1, 2.5); Vec3 lookAt(0, 1, 0); Vec3 up(0, 1, 0); int imageWidth = 400; int imageHeight = 400; double fov = 50.0; Camera camera(cameraPos, lookAt, up, fov, imageWidth, imageHeight); std::cout << "Starting render at " << imageWidth << "x" << imageHeight << "..." << std::endl; camera.render(scene, "cornell_box.ppm"); std::cout << "Render complete!" << std::endl; return 0; }