aurora-rendering-engine/experiments/rt9.cpp

1552 lines
46 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 <cmath>
#include <fstream>
#include <iostream>
#include <limits>
#include <memory>
#include <queue>
#include <vector>
// ============================================================================
// 基础数学结构
// ============================================================================
struct Vec3 {
double x, y, z;
Vec3(double x = 0, double y = 0, double z = 0) : 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/(double t) const {
return Vec3(x / t, y / t, z / t);
}
Vec3 operator-() const {
return Vec3(-x, -y, -z);
}
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 normalize() const {
double l = length();
return l > 1e-10 ? *this / l : Vec3(0, 0, 0);
}
// 关于平面反射点平面由点planePoint和法向量planeNormal定义
// 在 Vec3 类中,确保 reflect 函数正确
Vec3 reflect(const Vec3 &planePoint, const Vec3 &planeNormal) const {
Vec3 n = planeNormal.normalize();
double d = (*this - planePoint).dot(n);
return *this - n * (2.0 * d);
}
};
inline Vec3 operator*(double t, const Vec3 &v) {
return v * t;
}
// ============================================================================
// 颜色类
// ============================================================================
struct Color {
double r, g, b;
Color(double r = 0, double g = 0, double b = 0) : 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-(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/(double t) const {
return Color(r / t, g / t, b / t);
}
Color &operator+=(const Color &c) {
r += c.r;
g += c.g;
b += c.b;
return *this;
}
Color clamp() 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)));
}
// Gamma校正
Color gammaCorrect(double gamma = 2.2) const {
double invGamma = 1.0 / gamma;
return Color(std::pow(r, invGamma), std::pow(g, invGamma), std::pow(b, invGamma));
}
};
// ============================================================================
// 纹理类
// ============================================================================
// ============================================================================
// 三角形光栅化辅助函数
// ============================================================================
// 2D点结构
struct Point2D {
double x, y;
Point2D(double x = 0, double y = 0) : x(x), y(y) {
}
};
// 计算2D三角形的有符号面积的两倍
double signedArea2D(const Point2D &a, const Point2D &b, const Point2D &c) {
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}
// 计算2D重心坐标
bool barycentric2D(const Point2D &p, const Point2D &a, const Point2D &b, const Point2D &c, double &u, double &v, double &w) {
double area = signedArea2D(a, b, c);
if (std::abs(area) < 1e-10)
return false;
u = signedArea2D(p, b, c) / area;
v = signedArea2D(a, p, c) / area;
w = signedArea2D(a, b, p) / area;
return true;
}
class Texture {
public:
int width, height;
std::vector<Color> pixels;
std::vector<double> depths; // 深度缓冲
Texture(int w = 1, int h = 1, const Color &c = Color(0, 0, 0))
: width(w), height(h), pixels(w * h, c), depths(w * h, 1e30) {
}
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)
pixels[y * width + x] = c;
}
double getDepth(int x, int y) const {
if (x < 0 || x >= width || y < 0 || y >= height)
return 1e30;
return depths[y * width + x];
}
void setDepth(int x, int y, double d) {
if (x >= 0 && x < width && y >= 0 && y < height)
depths[y * width + x] = d;
}
void setPixelWithDepth(int x, int y, const Color &c, double depth) {
if (x >= 0 && x < width && y >= 0 && y < height) {
int idx = y * width + x;
if (depth < depths[idx]) {
depths[idx] = depth;
pixels[idx] = c;
}
}
}
// 双线性插值采样 (u, v 在 [0, 1] 范围内)
Color sample(double u, double v) const {
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, y0 = (int)fy;
int x1 = std::min(x0 + 1, width - 1);
int y1 = std::min(y0 + 1, height - 1);
double tx = fx - x0, 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 = c00 * (1 - tx) + c10 * tx;
Color c1 = c01 * (1 - tx) + c11 * tx;
return c0 * (1 - ty) + c1 * ty;
}
void clear(const Color &c = Color(0, 0, 0)) {
std::fill(pixels.begin(), pixels.end(), c);
std::fill(depths.begin(), depths.end(), 1e30);
}
// 写入PPM文件
void writePPM(const std::string &filename) const {
std::ofstream ofs(filename);
ofs << "P3\n"
<< width << " " << height << "\n255\n";
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Color c = getPixel(x, y).clamp().gammaCorrect();
int ir = static_cast<int>(255.99 * c.r);
int ig = static_cast<int>(255.99 * c.g);
int ib = static_cast<int>(255.99 * c.b);
ofs << ir << " " << ig << " " << ib << "\n";
}
}
ofs.close();
}
// 新增:将源纹理拉伸到目标三角形区域并混合粘贴
// srcTri: 源纹理的三角形UV坐标 (通常是 (0,0), (1,0), (0,1))
// dstTri: 目标纹理上的三角形像素坐标
// tint: 颜色染色(用于金属反射)
// alpha: 混合透明度
void blendTriangleTexture(
const Texture &src,
const Point2D &dst0, const Point2D &dst1, const Point2D &dst2,
const Color &tint = Color(1, 1, 1),
double alpha = 1.0) {
// 计算包围盒
double minX = std::min({ dst0.x, dst1.x, dst2.x });
double maxX = std::max({ dst0.x, dst1.x, dst2.x });
double minY = std::min({ dst0.y, dst1.y, dst2.y });
double maxY = std::max({ dst0.y, dst1.y, dst2.y });
int startX = std::max(0, (int)std::floor(minX));
int endX = std::min(width - 1, (int)std::ceil(maxX));
int startY = std::max(0, (int)std::floor(minY));
int endY = std::min(height - 1, (int)std::ceil(maxY));
// 检查三角形面积,避免退化三角形
double area = signedArea2D(dst0, dst1, dst2);
if (std::abs(area) < 1e-6)
return;
for (int py = startY; py <= endY; py++) {
for (int px = startX; px <= endX; px++) {
Point2D p(px + 0.5, py + 0.5);
double u, v, w;
if (!barycentric2D(p, dst0, dst1, dst2, u, v, w))
continue;
// 放宽边界检查
const double eps = -0.01;
if (u < eps || v < eps || w < eps)
continue;
if (u > 1.01 || v > 1.01 || w > 1.01)
continue;
// 限制重心坐标到有效范围
u = std::max(0.0, std::min(1.0, u));
v = std::max(0.0, std::min(1.0, v));
w = std::max(0.0, std::min(1.0, w));
// 源纹理采样 - 使用整个纹理范围
// 重心坐标直接映射到纹理UV
double srcU = v; // 对应dst1方向
double srcV = w; // 对应dst2方向
// 确保在[0,1]范围内
srcU = std::max(0.0, std::min(1.0, srcU));
srcV = std::max(0.0, std::min(1.0, srcV));
Color srcColor = src.sample(srcU, srcV);
Color finalColor = srcColor * tint;
if (alpha >= 1.0) {
setPixel(px, py, finalColor);
} else {
Color dstColor = getPixel(px, py);
setPixel(px, py, finalColor * alpha + dstColor * (1.0 - alpha));
}
}
}
}
// 新增使用UV坐标版本UV范围0-1
void blendTriangleTextureUV(
const Texture &src,
const Point2D &uv0, const Point2D &uv1, const Point2D &uv2,
const Color &tint = Color(1, 1, 1),
double alpha = 1.0) {
// 将UV坐标转换为像素坐标允许负值和超出范围的值
Point2D dst0(uv0.x * width, uv0.y * height);
Point2D dst1(uv1.x * width, uv1.y * height);
Point2D dst2(uv2.x * width, uv2.y * height);
// 计算包围盒并裁剪到纹理范围
double minX = std::min({ dst0.x, dst1.x, dst2.x });
double maxX = std::max({ dst0.x, dst1.x, dst2.x });
double minY = std::min({ dst0.y, dst1.y, dst2.y });
double maxY = std::max({ dst0.y, dst1.y, dst2.y });
// 裁剪到有效范围
int startX = std::max(0, (int)std::floor(minX));
int endX = std::min(width - 1, (int)std::ceil(maxX));
int startY = std::max(0, (int)std::floor(minY));
int endY = std::min(height - 1, (int)std::ceil(maxY));
// 如果完全在范围外,直接返回
if (startX > endX || startY > endY)
return;
double area = signedArea2D(dst0, dst1, dst2);
if (std::abs(area) < 0.5)
return; // 三角形太小
for (int py = startY; py <= endY; py++) {
for (int px = startX; px <= endX; px++) {
Point2D p(px + 0.5, py + 0.5);
double u, v, w;
if (!barycentric2D(p, dst0, dst1, dst2, u, v, w))
continue;
const double eps = -0.001;
if (u < eps || v < eps || w < eps)
continue;
double srcU = std::max(0.0, std::min(1.0, v));
double srcV = std::max(0.0, std::min(1.0, w));
Color srcColor = src.sample(srcU, srcV);
Color finalColor = srcColor * tint;
setPixel(px, py, finalColor);
}
}
}
};
// ============================================================================
// 材质类型枚举
// ============================================================================
enum MaterialType {
DIFFUSE,
REFLECTIVE,
EMISSIVE // 光源
};
// ============================================================================
// 材质类
// ============================================================================
struct Material {
MaterialType type;
Color baseColor;
Color emissionColor;
double reflectivity; // 反射率(0-1)
Material(MaterialType t = DIFFUSE, const Color &color = Color(0.8, 0.8, 0.8))
: type(t), baseColor(color), emissionColor(0, 0, 0), reflectivity(0.9) {
}
static Material Diffuse(const Color &color) {
return Material(DIFFUSE, color);
}
static Material Reflective(const Color &color, double refl = 0.95) {
Material m(REFLECTIVE, color);
m.reflectivity = refl;
return m;
}
static Material Emissive(const Color &color, const Color &emission) {
Material m(EMISSIVE, color);
m.emissionColor = emission;
return m;
}
};
// ============================================================================
// 三角形类
// ============================================================================
class Triangle {
public:
Vec3 v0, v1, v2; // 三个顶点
Vec3 normal;
Material material;
int id; // 唯一标识符,用于避免自相交
Triangle() : id(-1) {
}
Triangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2,
const Material &mat = Material(), int id = -1)
: v0(v0), v1(v1), v2(v2), material(mat), id(id) {
computeNormal();
}
void computeNormal() {
normal = (v1 - v0).cross(v2 - v0).normalize();
}
// 获取三角形中心
Vec3 center() const {
return (v0 + v1 + v2) / 3.0;
}
// 获取三角形面积
double area() const {
return (v1 - v0).cross(v2 - v0).length() / 2.0;
}
// 计算重心坐标
bool getBarycentricCoords(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;
}
// 检查点是否在三角形内部(使用重心坐标)
bool containsPoint(const Vec3 &p, double epsilon = 1e-6) const {
double u, v, w;
if (!getBarycentricCoords(p, u, v, w))
return false;
return u >= -epsilon && v >= -epsilon && w >= -epsilon;
}
// 射线与三角形相交 (MöllerTrumbore算法)
bool intersectRay(const Vec3 &origin, const Vec3 &dir, double &t, double &u, double &v) const {
const double EPSILON = 1e-10;
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;
}
// 获取平面方程 (ax + by + cz + d = 0)
void getPlaneEquation(double &a, double &b, double &c, double &d) const {
a = normal.x;
b = normal.y;
c = normal.z;
d = -normal.dot(v0);
}
// 射线与三角形所在平面的交点
bool intersectPlane(const Vec3 &origin, const Vec3 &dir, Vec3 &intersection) const {
double denom = normal.dot(dir);
if (std::abs(denom) < 1e-10)
return false;
double t = normal.dot(v0 - origin) / denom;
if (t < 0)
return false;
intersection = origin + dir * t;
return true;
}
// 将世界坐标点转换为三角形的UV坐标
// 在 Triangle类中修改 worldToUV 函数
bool worldToUV(const Vec3 &worldPoint, double &u, double &v) const {
// 首先将点投影到三角形平面上
Vec3 pointOnPlane = worldPoint - normal * (worldPoint - v0).dot(normal);
// 计算重心坐标
Vec3 v0v1 = v1 - v0;
Vec3 v0v2 = v2 - v0;
Vec3 v0p = pointOnPlane - 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;
double bv = (d11 * d20 - d01 * d21) / denom; // v1的权重
double bw = (d00 * d21 - d01 * d20) / denom; // v2的权重
// bu = 1 - bv - bw // v0的权重
// UV坐标v0=(0,0), v1=(1,0), v2=(0,1)
u = bv;
v = bw;
return true;
}
// 将UV坐标转换为世界坐标
Vec3 uvToWorld(double u, double v) const {
return v0 * (1.0 - u - v) + v1 * u + v2 * v;
}
// 获取AABB包围盒
void getAABB(Vec3 &minP, Vec3 &maxP) const {
minP.x = std::min({ v0.x, v1.x, v2.x });
minP.y = std::min({ v0.y, v1.y, v2.y });
minP.z = std::min({ v0.z, v1.z, v2.z });
maxP.x = std::max({ v0.x, v1.x, v2.x });
maxP.y = std::max({ v0.y, v1.y, v2.y });
maxP.z = std::max({ v0.z, v1.z, v2.z });
}
};
// ============================================================================
// 视锥体类由apex和底面三角形定义的四面体
// ============================================================================
class Frustum {
public:
Vec3 apex;
Triangle base; // 底面三角形
Vec3 planes[4]; // 4个侧面的法向量指向内部
double planeDs[4]; // 平面方程的d值
Frustum(const Vec3 &apex, const Triangle &base) : apex(apex), base(base) {
computePlanes();
}
void computePlanes() {
// 计算4个平面3个侧面 + 1个底面
// 侧面由apex和底面的边构成
Vec3 edges[3][2] = {
{ base.v0, base.v1 },
{ base.v1, base.v2 },
{ base.v2, base.v0 }
};
Vec3 baseCenter = base.center();
for (int i = 0; i < 3; i++) {
Vec3 edge = edges[i][1] - edges[i][0];
Vec3 toApex = apex - edges[i][0];
planes[i] = edge.cross(toApex).normalize();
// 确保法向量指向视锥体内部
Vec3 toCenter = baseCenter - edges[i][0];
if (planes[i].dot(toCenter) < 0) {
planes[i] = -planes[i];
}
planeDs[i] = -planes[i].dot(edges[i][0]);
}
// 底面(远平面)
planes[3] = base.normal;
Vec3 apexToBase = baseCenter - apex;
if (planes[3].dot(apexToBase) < 0) {
planes[3] = -planes[3];
}
planeDs[3] = -planes[3].dot(base.v0);
}
// 检查点是否在视锥体内
bool containsPoint(const Vec3 &p) const {
// 检查点是否在apex的前方相对于底面
Vec3 apexToP = p - apex;
Vec3 apexToBase = base.center() - apex;
if (apexToP.dot(apexToBase) < 0)
return false;
// 检查是否在所有平面的正确一侧
for (int i = 0; i < 4; i++) {
double dist = planes[i].dot(p) + planeDs[i];
if (dist < -1e-6)
return false;
}
return true;
}
// 检查三角形是否与视锥体相交或在其内部
bool intersectsOrContainsTriangle(const Triangle &tri) const {
// 快速检测:如果三角形的任意顶点在视锥体内
if (containsPoint(tri.v0) || containsPoint(tri.v1) || containsPoint(tri.v2))
return true;
// 检查视锥体的边是否与三角形相交
Vec3 frustumVerts[4] = { apex, base.v0, base.v1, base.v2 };
int edges[6][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 2 }, { 2, 3 }, { 3, 1 } };
for (int i = 0; i < 6; i++) {
Vec3 p1 = frustumVerts[edges[i][0]];
Vec3 p2 = frustumVerts[edges[i][1]];
Vec3 dir = (p2 - p1).normalize();
double maxT = (p2 - p1).length();
double t, u, v;
if (tri.intersectRay(p1, dir, t, u, v) && t < maxT) {
return true;
}
}
// 检查三角形的边是否与视锥体的面相交
Vec3 triVerts[3] = { tri.v0, tri.v1, tri.v2 };
Triangle frustumFaces[4] = {
Triangle(apex, base.v0, base.v1),
Triangle(apex, base.v1, base.v2),
Triangle(apex, base.v2, base.v0),
base
};
for (int i = 0; i < 3; i++) {
Vec3 p1 = triVerts[i];
Vec3 p2 = triVerts[(i + 1) % 3];
Vec3 dir = (p2 - p1).normalize();
double maxT = (p2 - p1).length();
for (int j = 0; j < 4; j++) {
double t, u, v;
if (frustumFaces[j].intersectRay(p1, dir, t, u, v) && t < maxT) {
return true;
}
}
}
return false;
}
};
//============================================================================
// 简化的可见性检测类
// ============================================================================
class VisibilityChecker {
public:
Vec3 cameraOrigin;
Triangle viewport;
Vec3 viewDir; // 视线方向
VisibilityChecker(const Vec3 &origin, const Triangle &vp)
: cameraOrigin(origin), viewport(vp) {
viewDir = (viewport.center() - cameraOrigin).normalize();
}
// 检查三角形是否可能在视野内
// 简化方法检查三角形是否在相机前方且投影到viewport平面上有交集
bool isTriangleVisible(const Triangle &tri) const {
// 检查三角形中心是否在相机前方
Vec3 toTri = tri.center() - cameraOrigin;
if (toTri.dot(viewDir) <= 0.01)
return false;
// 检查三角形的任意顶点投影是否可能落在viewport附近
Vec3 verts[3] = { tri.v0, tri.v1, tri.v2 };
int validCount = 0;
for (int i = 0; i < 3; i++) {
Vec3 dir = (verts[i] - cameraOrigin).normalize();
// 检查方向是否大致朝向viewport
if (dir.dot(viewDir) > 0.01) {
validCount++;
}
}
return validCount > 0;
}
// 计算三角形顶点在viewport平面上的投影
// 返回是否所有顶点都能成功投影
bool projectTriangle(const Triangle &tri, Vec3 projected[3]) const {
Vec3 verts[3] = { tri.v0, tri.v1, tri.v2 };
for (int i = 0; i < 3; i++) {
Vec3 dir = (verts[i] - cameraOrigin);
double dirLen = dir.length();
if (dirLen < 1e-10)
return false;
dir = dir / dirLen;
// 与viewport平面求交
double denom = viewport.normal.dot(dir);
if (std::abs(denom) < 1e-10)
return false;
double t = viewport.normal.dot(viewport.v0 - cameraOrigin) / denom;
if (t <= 0)
return false; // 在相机后方
projected[i] = cameraOrigin + dir * t;
}
return true;
}
};
// ============================================================================
// 前向声明
// ============================================================================
class Scene;
// ============================================================================
// 渲染函数声明
// ============================================================================
// 新增一个辅助结构来传递viewport信息
struct ViewportInfo {
bool isRectViewport; // 是否是矩形viewport相机viewport
Vec3 topLeft;
Vec3 right, down;
double width, height;
ViewportInfo() : isRectViewport(false) {
}
ViewportInfo(const Vec3 &tl, const Vec3 &r, const Vec3 &d, double w, double h)
: isRectViewport(true), topLeft(tl), right(r), down(d), width(w), height(h) {
}
// 将世界坐标转换为UV
bool worldToUV(const Vec3 &worldPoint, double &u, double &v) const {
Vec3 toPoint = worldPoint - topLeft;
u = toPoint.dot(right) / width;
v = toPoint.dot(down) / height;
return true;
}
};
Texture renderTriangleWithTriangle(
const std::vector<Triangle *> &triangles,
const Vec3 &cameraOrigin,
const Triangle &viewport,
int estimatedWidth,
int estimatedHeight,
int maxDepth,
int currentDepth,
int excludeId,
const ViewportInfo *vpInfo = nullptr // 新增参数
);
// ============================================================================
// 场景类
// ============================================================================
class Scene {
public:
std::vector<Triangle> triangles;
int nextId = 0;
void addTriangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2, const Material &mat) {
triangles.emplace_back(v0, v1, v2, mat, nextId++);
}
// 添加一个四边形(由两个三角形组成)
void addQuad(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, const Material &mat) {
addTriangle(v0, v1, v2, mat);
addTriangle(v0, v2, v3, mat);
}
// 添加一个盒子
void addBox(const Vec3 &minP, const Vec3 &maxP, const Material &mat) {
Vec3 v[8] = {
Vec3(minP.x, minP.y, minP.z),
Vec3(maxP.x, minP.y, minP.z),
Vec3(maxP.x, maxP.y, minP.z),
Vec3(minP.x, maxP.y, minP.z),
Vec3(minP.x, minP.y, maxP.z),
Vec3(maxP.x, minP.y, maxP.z),
Vec3(maxP.x, maxP.y, maxP.z),
Vec3(minP.x, maxP.y, maxP.z)
};
// 前面 (z = maxP.z)
addQuad(v[4], v[5], v[6], v[7], mat);
// 后面 (z = minP.z)
addQuad(v[1], v[0], v[3], v[2], mat);
// 左面 (x = minP.x)
addQuad(v[0], v[4], v[7], v[3], mat);
// 右面 (x = maxP.x)
addQuad(v[5], v[1], v[2], v[6], mat);
// 顶面 (y = maxP.y)
addQuad(v[7], v[6], v[2], v[3], mat);
// 底面 (y = minP.y)
addQuad(v[0], v[1], v[5], v[4], mat);
}
std::vector<Triangle *> getTrianglePtrs() {
std::vector<Triangle *> ptrs;
for (auto &tri : triangles) {
ptrs.push_back(&tri);
}
return ptrs;
}
};
Texture renderTriangleWithTriangle(
const std::vector<Triangle *> &triangles,
const Vec3 &cameraOrigin,
const Triangle &viewport,
int estimatedWidth,
int estimatedHeight,
int maxDepth,
int currentDepth,
int excludeId,
const ViewportInfo *vpInfo) {
Texture result(estimatedWidth, estimatedHeight);
// 终止条件
if (currentDepth >= maxDepth || estimatedWidth < 4 || estimatedHeight < 4) {
if (viewport.material.type == EMISSIVE) {
result.clear(viewport.material.emissionColor);
} else {
result.clear(viewport.material.baseColor * 0.3);
}
return result;
}
// 漫反射材质:直接返回着色后的基础颜色
if (viewport.material.type == DIFFUSE) {
Vec3 lightDir = Vec3(0, 1, 0.5).normalize();
double intensity = std::max(0.3, std::abs(viewport.normal.dot(lightDir)));
result.clear(viewport.material.baseColor * intensity);
return result;
}
// 发光材质
if (viewport.material.type == EMISSIVE) {
result.clear(viewport.material.emissionColor);
return result;
}
// ========== 反射材质处理 ==========
// 初始化为暗色(无反射时的基础色)
result.clear(viewport.material.baseColor * 0.1);
// 创建可见性检测器
VisibilityChecker checker(cameraOrigin, viewport);
// 收集可见三角形并按距离排序
struct TriangleDist {
Triangle *tri;
double distance;
};
std::vector<TriangleDist> visibleTriangles;
for (Triangle *tri : triangles) {
if (tri->id == viewport.id || tri->id == excludeId)
continue;
if (checker.isTriangleVisible(*tri)) {
double dist = (tri->center() - cameraOrigin).length();
visibleTriangles.push_back({ tri, dist });
}
}
// 按距离排序(由远到近)
std::sort(visibleTriangles.begin(), visibleTriangles.end(), [](const TriangleDist &a, const TriangleDist &b) {
return a.distance > b.distance;
});
std::cout << "Depth " << currentDepth << ": Found " << visibleTriangles.size()
<< " visible triangles" << std::endl;
for (const auto &td : visibleTriangles) {
Triangle *tri = td.tri;
Vec3 projected[3];
if (!checker.projectTriangle(*tri, projected))
continue;
// ===== 修改UV计算部分 =====
Point2D uvCoords[3];
bool uvValid = true;
for (int i = 0; i < 3; i++) {
double u, v;
if (vpInfo && vpInfo->isRectViewport) {
// 使用矩形viewport的UV坐标系
vpInfo->worldToUV(projected[i], u, v);
} else {
// 使用三角形的UV坐标系
if (!viewport.worldToUV(projected[i], u, v)) {
uvValid = false;
break;
}
}
uvCoords[i] = Point2D(u, v);
}
if (!uvValid)
continue;
// ===== 添加调试输出 =====
if (currentDepth == 0 && tri->material.type == REFLECTIVE) {
std::cout << "Metal tri " << tri->id << " UV: ("
<< uvCoords[0].x << "," << uvCoords[0].y << ") ("
<< uvCoords[1].x << "," << uvCoords[1].y << ") ("
<< uvCoords[2].x << "," << uvCoords[2].y << ")" << std::endl;
}
// 检查投影是否在viewport范围内
double minU = std::min({ uvCoords[0].x, uvCoords[1].x, uvCoords[2].x });
double maxU = std::max({ uvCoords[0].x, uvCoords[1].x, uvCoords[2].x });
double minV = std::min({ uvCoords[0].y, uvCoords[1].y, uvCoords[2].y });
double maxV = std::max({ uvCoords[0].y, uvCoords[1].y, uvCoords[2].y });
// 如果完全在viewport外跳过
if (maxU < 0 || minU > 1 || maxV < 0 || minV > 1)
continue;
// 获取目标三角形的纹理
Texture subTexture;
if (tri->material.type == REFLECTIVE) {
// 对于反射材质,递归计算
// 将相机关于目标三角形对称
Vec3 newOrigin = cameraOrigin.reflect(tri->center(), tri->normal);
double projArea = std::abs(signedArea2D(uvCoords[0], uvCoords[1], uvCoords[2]));
int subW = std::max(8, (int)(estimatedWidth * std::sqrt(projArea) * 0.8));
int subH = std::max(8, (int)(estimatedHeight * std::sqrt(projArea) * 0.8));
subW = std::min(subW, estimatedWidth);
subH = std::min(subH, estimatedHeight);
subTexture = renderTriangleWithTriangle(
triangles, newOrigin, *tri,
subW, subH, maxDepth, currentDepth + 1, viewport.id);
// 在处理反射材质时,添加测试
// if (viewport.material.type == REFLECTIVE && currentDepth > 0) {
// // 测试:直接返回一个明显的颜色,验证反射路径是否被执行
// result.clear(Color(1, 0, 1)); // 洋红色
// return result;
// }
} else if (tri->material.type == EMISSIVE) {
// 发光材质
subTexture = Texture(16, 16, tri->material.emissionColor);
} else {
// 漫反射材质
Vec3 lightDir = Vec3(0, 1, 0.5).normalize();
double intensity = std::max(0.3, std::abs(tri->normal.dot(lightDir)));
subTexture = Texture(16, 16, tri->material.baseColor * intensity);
}
// 计算染色(金属会给反射染色)
Color tint = viewport.material.baseColor * viewport.material.reflectivity;
// 粘贴纹理
result.blendTriangleTextureUV(subTexture, uvCoords[0], uvCoords[1], uvCoords[2], tint, 1.0);
}
return result;
}
// // ============================================================================
// // 核心渲染函数
// // ============================================================================
// Texture renderTriangleWithTriangle(
// const std::vector<Triangle*>& triangles,
// const Vec3& cameraOrigin,
// const Triangle& viewport,
// int estimatedWidth,
// int estimatedHeight,
// int maxDepth,
// int currentDepth,
// int excludeId)
// {
// // 创建当前视口的纹理
// Texture result(estimatedWidth, estimatedHeight, viewport.material.baseColor);
//
// // 终止条件:达到最大深度
// if (currentDepth >= maxDepth) {
// if (viewport.material.type == EMISSIVE) {
// result.clear(viewport.material.emissionColor);
// }
// return result;
// }
//
// // 漫反射材质:直接返回基础颜色
// if (viewport.material.type == DIFFUSE) {
// Vec3 lightDir = Vec3(0.5, 1, 0.3).normalize();
// double intensity = std::max(0.2, std::abs(viewport.normal.dot(lightDir)));
// Color shadedColor = viewport.material.baseColor * intensity;
// result.clear(shadedColor);
// return result;
// }
//
// // 发光材质
// if (viewport.material.type == EMISSIVE) {
// result.clear(viewport.material.emissionColor);
// return result;
// }
//
// //========== 反射材质处理 ==========
//
// // 用较暗的基础色初始化(环境光贡献)
// Color ambientColor = viewport.material.baseColor * 0.05;
// result.clear(ambientColor);
//
// // 构建视锥体
// Frustum frustum(cameraOrigin, viewport);
//
// // 收集与视锥体相交的三角形,按距离排序(由远到近)
// struct TriangleDist {
// Triangle* tri;
// double distance;
// bool operator>(const TriangleDist& other) const {
// return distance > other.distance;
// }
// };
//
// std::vector<TriangleDist> visibleTriangles;
//
// for (Triangle* tri : triangles) {
// if (tri->id == viewport.id || tri->id == excludeId) continue;
//
// Vec3 triCenter = tri->center();
// Vec3 toTri = triCenter - cameraOrigin;
// Vec3 viewDir = viewport.center() - cameraOrigin;
// if (toTri.dot(viewDir) <= 0) continue;
//
// if (frustum.intersectsOrContainsTriangle(*tri)) {
// double dist = toTri.length();
// visibleTriangles.push_back({tri, dist});
// }
// }
//
// // 按距离排序(由远到近)
// std::sort(visibleTriangles.begin(), visibleTriangles.end(),[](const TriangleDist& a, const TriangleDist& b) {
// return a.distance > b.distance;
// });
//
// // ========== 遍历并粘贴纹理 ==========
//
// for (const auto& td : visibleTriangles) {
// Triangle* tri = td.tri;
//
// // 计算目标三角形三个顶点投影到当前viewport平面上的交点
// Vec3 triVerts[3] = {tri->v0, tri->v1, tri->v2};
// Vec3 projectedVerts[3];
// bool allValid = true;
//
// for (int i = 0; i < 3; i++) {
// Vec3 dir = (triVerts[i] - cameraOrigin).normalize();
// Vec3 intersection;
//
// if (viewport.intersectPlane(cameraOrigin, dir, intersection)) {
// projectedVerts[i] = intersection;
// } else {
// allValid = false;
// break;
// }
// }
//
// if (!allValid) continue;
//
// // 将投影点转换为viewport的UV坐标
// Point2D uvCoords[3];
// bool uvValid = true;
// for (int i = 0; i < 3; i++) {
// double u, v;
// if (viewport.worldToUV(projectedVerts[i], u, v)) {
// uvCoords[i] = Point2D(u, v);
// } else {
// uvValid = false;
// break;
// }
// }
//
// if (!uvValid) continue;
//
// // 递归获取目标三角形的纹理
// Vec3 newCameraOrigin = cameraOrigin.reflect(tri->center(), tri->normal);
// // 估算子纹理大小
// double projArea = std::abs(signedArea2D(uvCoords[0], uvCoords[1], uvCoords[2]));
// int subWidth = std::max(8, (int)(estimatedWidth * std::sqrt(projArea)));
// int subHeight = std::max(8, (int)(estimatedHeight * std::sqrt(projArea)));
// subWidth = std::min(subWidth, estimatedWidth);
// subHeight = std::min(subHeight, estimatedHeight);
// Texture subTexture = renderTriangleWithTriangle(
// triangles, newCameraOrigin, *tri,
// subWidth, subHeight,
// maxDepth, currentDepth + 1, viewport.id
// );
//
// // 计算金属染色和反射率
// Color metalTint = viewport.material.baseColor * viewport.material.reflectivity;
// // 直接将子纹理拉伸粘贴到当前纹理
// result.blendTriangleTextureUV(subTexture, uvCoords[0], uvCoords[1], uvCoords[2], metalTint, 1.0);
// }
//
// return result;
// }
//
// Texture renderTriangleWithTriangle(
// const std::vector<Triangle *> &triangles,
// const Vec3 &cameraOrigin,
// const Triangle &viewport,
// int estimatedWidth,
// int estimatedHeight,
// int maxDepth,
// int currentDepth,
// int excludeId) {
// // 创建当前视口的纹理
// Texture result(estimatedWidth, estimatedHeight, viewport.material.baseColor);
//
// // 终止条件:达到最大深度或面积太小
// if (currentDepth >= maxDepth) {
// // 对于发光材质,返回发光颜色
// if (viewport.material.type == EMISSIVE) {
// result.clear(viewport.material.emissionColor);
// }
// return result;
// }
//
// // 如果是漫反射材质,直接返回基础颜色
// if (viewport.material.type == DIFFUSE) {
// // 添加简单的着色(基于法线方向)
// Vec3 lightDir = Vec3(0.5, 1, 0.3).normalize();
// double intensity = std::max(0.2, std::abs(viewport.normal.dot(lightDir)));
// Color shadedColor = viewport.material.baseColor * intensity;
// result.clear(shadedColor);
// return result;
// }
//
// // 如果是发光材质
// if (viewport.material.type == EMISSIVE) {
// result.clear(viewport.material.emissionColor);
// return result;
// }
//
// // 对于反射材质,需要递归处理
// // 首先用基础颜色初始化
// result.clear(viewport.material.baseColor * 0.05); // 微弱的基础色
//
// // 构建视锥体
// Frustum frustum(cameraOrigin, viewport);
//
// // 收集与视锥体相交的三角形,并按距离排序
// struct TriangleDist {
// Triangle *tri;
// double distance;
// bool operator>(const TriangleDist &other) const {
// return distance > other.distance; // 远的优先(用于由远到近渲染)
// }
// };
//
// std::priority_queue<TriangleDist, std::vector<TriangleDist>, std::greater<TriangleDist>> pq;
//
// for (Triangle *tri : triangles) {
// // 跳过自身和被排除的三角形
// if (tri->id == viewport.id || tri->id == excludeId)
// continue;
//
// // 检查三角形是否在相机前方
// Vec3 triCenter = tri->center();
// Vec3 toTri = triCenter - cameraOrigin;
// Vec3 viewDir = viewport.center() - cameraOrigin;
// if (toTri.dot(viewDir) <= 0)
// continue;
//
// // 检查三角形是否与视锥体相交
// if (frustum.intersectsOrContainsTriangle(*tri)) {
// double dist = (triCenter - cameraOrigin).length();
// pq.push({ tri, dist });
// }
// }
//
// // 将优先队列转换为向量(从远到近)
// std::vector<Triangle *> sortedTriangles;
// while (!pq.empty()) {
// sortedTriangles.push_back(pq.top().tri);
// pq.pop();
// }
// std::reverse(sortedTriangles.begin(), sortedTriangles.end()); // 变成由远到近
//
// // 遍历每个三角形,将其投影到当前视口上
// for (Triangle *tri : sortedTriangles) {
// // 计算目标三角形顶点在当前视口上的投影位置
// Vec3 projectedVerts[3];
// bool allValid = true;
//
// Vec3 triVerts[3] = { tri->v0, tri->v1, tri->v2 };
//
// for (int i = 0; i < 3; i++) {
// Vec3 dir = (triVerts[i] - cameraOrigin).normalize();
// Vec3 intersection;
//
// if (viewport.intersectPlane(cameraOrigin, dir, intersection)) {
// // 检查交点是否在视口三角形内(或附近)
// projectedVerts[i] = intersection;
// } else {
// allValid = false;
// break;
// }
// }
//
// if (!allValid)
// continue;
//
// // 将投影的3D坐标转换为视口的2D UV坐标
// Point2D uvCoords[3];
// for (int i = 0; i < 3; i++) {
// double u, v;
// viewport.worldToUV(projectedVerts[i], u, v);
// uvCoords[i] = Point2D(u, v);
// }
//
// // 递归获取目标三角形应显示的纹理
// // 对于反射,将相机原点关于目标三角形平面对称
// Vec3 newCameraOrigin = cameraOrigin.reflect(tri->center(), tri->normal);
//
// // 估算子纹理大小(基于投影面积)
// double projArea = std::abs(signedArea2D(uvCoords[0], uvCoords[1], uvCoords[2]));
// int subWidth = std::max(4, (int)(estimatedWidth * std::sqrt(projArea) * 0.5));
// int subHeight = std::max(4, (int)(estimatedHeight * std::sqrt(projArea) * 0.5));
//
// // 限制子纹理大小
// subWidth = std::min(subWidth, estimatedWidth);
// subHeight = std::min(subHeight, estimatedHeight);
// Texture subTexture = renderTriangleWithTriangle(
// triangles, newCameraOrigin, *tri,
// subWidth, subHeight,
// maxDepth, currentDepth + 1, viewport.id);
//
// // 将子纹理变换并绘制到当前纹理上
// // 使用扫描线算法进行三角形光栅化
//
// // 计算包围盒
// double minU = std::min({ uvCoords[0].x, uvCoords[1].x, uvCoords[2].x });
// double maxU = std::max({ uvCoords[0].x, uvCoords[1].x, uvCoords[2].x });
// double minV = std::min({ uvCoords[0].y, uvCoords[1].y, uvCoords[2].y });
// double maxV = std::max({ uvCoords[0].y, uvCoords[1].y, uvCoords[2].y });
//
// // 转换为像素坐标
// int startX = std::max(0, (int)(minU * estimatedWidth) - 1);
// int endX = std::min(estimatedWidth - 1, (int)(maxU * estimatedWidth) + 1);
// int startY = std::max(0, (int)(minV * estimatedHeight) - 1);
// int endY = std::min(estimatedHeight - 1, (int)(maxV * estimatedHeight) + 1);
//
// // 计算到目标三角形的距离(用于深度测试)
// double triDist = (tri->center() - cameraOrigin).length();
//
// // 光栅化三角形
// for (int py = startY; py <= endY; py++) {
// for (int px = startX; px <= endX; px++) {
// // 当前像素的UV坐标
// double u = (px + 0.5) / estimatedWidth;
// double v = (py + 0.5) / estimatedHeight;
// Point2D p(u, v);
//
// // 计算相对于投影三角形的重心坐标
// double bu, bv, bw;
// if (!barycentric2D(p, uvCoords[0], uvCoords[1], uvCoords[2], bu, bv, bw))
// continue;
//
// // 检查是否在三角形内
// const double eps = -0.001; // 稍微放宽以避免间隙
// if (bu < eps || bv < eps || bw < eps)
// continue;
//
// // 使用重心坐标在子纹理中采样
// // 子纹理的UV对应于目标三角形的顶点
// // 在源三角形中顶点UV为 (0,0), (1,0), (0,1)
// double srcU = bv; // 对应v1
// double srcV = bw; // 对应v2
//
// Color sampledColor = subTexture.sample(srcU, srcV);
//
// // // 应用反射率衰减
// // sampledColor = sampledColor * viewport.material.reflectivity;
// //
// // // 混合到结果纹理(使用深度测试)
// // result.setPixelWithDepth(px, py, sampledColor, triDist);
//
// // 应用反射率衰减,并与金属基础色混合
// Color metalTint = viewport.material.baseColor; // 金属染色
// sampledColor = sampledColor * metalTint * viewport.material.reflectivity;
//
// // 可选:添加非反射部分的基础色贡献
// Color finalColor = sampledColor + metalTint * (1.0 - viewport.material.reflectivity) * 0.1;
//
// result.setPixelWithDepth(px, py, sampledColor, triDist);
// }
// }
// }
//
// return result;
// }
// ============================================================================
// 相机类
// ============================================================================
class Camera {
public:
Vec3 origin;
Vec3 lookAt;
Vec3 up;
double fov; // 垂直视场角(度)
int width, height;
// 两个视口三角形
Triangle viewportTri1, viewportTri2;
// 新增viewport矩形的四个角和坐标轴
Vec3 vpTopLeft, vpTopRight, vpBottomLeft, vpBottomRight;
Vec3 vpRight, vpDown; // viewport的水平和垂直方向
Vec3 vpNormal;
double vpWidth, vpHeight;
Camera(const Vec3 &origin, const Vec3 &lookAt, const Vec3 &up,
double fov, int width, int height)
: origin(origin), lookAt(lookAt), up(up), fov(fov), width(width), height(height) {
computeViewportTriangles();
}
void computeViewportTriangles() {
Vec3 forward = (lookAt - origin).normalize();
Vec3 right = forward.cross(up).normalize();
Vec3 camUp = right.cross(forward).normalize();
double aspectRatio = (double)width / height;
double fovRad = fov * M_PI / 180.0;
double viewportDist = 1.0;
double viewportHeight = 2.0 * viewportDist * std::tan(fovRad / 2.0);
double viewportWidth = viewportHeight * aspectRatio;
Vec3 viewportCenter = origin + forward * viewportDist;
Vec3 halfRight = right * (viewportWidth / 2.0);
Vec3 halfUp = camUp * (viewportHeight / 2.0);
// 保存viewport矩形信息
vpTopLeft = viewportCenter - halfRight + halfUp;
vpTopRight = viewportCenter + halfRight + halfUp;
vpBottomLeft = viewportCenter - halfRight - halfUp;
vpBottomRight = viewportCenter + halfRight - halfUp;
vpRight = right;
vpDown = -camUp; // 向下为正(图像坐标系)
vpNormal = forward;
vpWidth = viewportWidth;
vpHeight = viewportHeight;
Material viewportMat = Material::Reflective(Color(1, 1, 1), 1.0);
viewportTri1 = Triangle(vpBottomLeft, vpBottomRight, vpTopLeft, viewportMat, -100);
viewportTri2 = Triangle(vpBottomRight, vpTopRight, vpTopLeft, viewportMat, -101);
}
// 新增将世界坐标转换为viewport的UV坐标相对于整个矩形
bool worldToViewportUV(const Vec3 &worldPoint, double &u, double &v) const {
// 将点投影到viewport平面
Vec3 toPoint = worldPoint - vpTopLeft;
// 计算在viewport坐标系中的位置
u = toPoint.dot(vpRight) / vpWidth;
v = toPoint.dot(vpDown) / vpHeight;
return true;
}
Texture render(Scene &scene, int maxDepth = 3) {
Texture finalImage(width, height);
auto trianglePtrs = scene.getTrianglePtrs();
// 创建viewport信息
ViewportInfo vpInfo(vpTopLeft, vpRight, vpDown, vpWidth, vpHeight);
std::cout << "Rendering viewport..." << std::endl;
// 只渲染一次使用矩形viewport
// 创建一个虚拟的viewport三角形实际上覆盖整个矩形
Material viewportMat = Material::Reflective(Color(1, 1, 1), 1.0);
Triangle fullViewport(vpTopLeft, vpBottomRight, vpTopRight, viewportMat, -100);
fullViewport.normal = vpNormal;
Texture tex = renderTriangleWithTriangle(
trianglePtrs, origin, fullViewport,
width, height, maxDepth, 0, -1, &vpInfo);
// 直接复制纹理到最终图像
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;
finalImage.setPixel(x, y, tex.sample(u, v));
}
}
return finalImage;
}
};
// ============================================================================
// 创建康奈尔盒子场景
// ============================================================================
void createCornellBoxScene(Scene &scene) {
// 康奈尔盒子尺寸
double size = 5.0;
double halfSize = size / 2.0;
// 材质定义
Material whiteDiffuse = Material::Diffuse(Color(0.73, 0.73, 0.73));
Material redDiffuse = Material::Diffuse(Color(0.65, 0.05, 0.05));
Material greenDiffuse = Material::Diffuse(Color(0.12, 0.45, 0.15));
Material blueMetal = Material::Reflective(Color(0.1, 0.2, 0.6), 0.85);
Material yellowMetal = Material::Reflective(Color(0.8, 0.7, 0.1), 0.85);
Material lightMat = Material::Emissive(Color(1, 1, 1), Color(15, 15, 15));
// 地板 (白色)
scene.addQuad(
Vec3(-halfSize, -halfSize, -halfSize),
Vec3(halfSize, -halfSize, -halfSize),
Vec3(halfSize, -halfSize, halfSize),
Vec3(-halfSize, -halfSize, halfSize),
whiteDiffuse);
// 天花板 (白色)
scene.addQuad(
Vec3(-halfSize, halfSize, halfSize),
Vec3(halfSize, halfSize, halfSize),
Vec3(halfSize, halfSize, -halfSize),
Vec3(-halfSize, halfSize, -halfSize),
whiteDiffuse);
// 后墙 (白色)
scene.addQuad(
Vec3(-halfSize, -halfSize, -halfSize),
Vec3(-halfSize, halfSize, -halfSize),
Vec3(halfSize, halfSize, -halfSize),
Vec3(halfSize, -halfSize, -halfSize),
whiteDiffuse);
// 左墙 (红色)
scene.addQuad(
Vec3(-halfSize, -halfSize, halfSize),
Vec3(-halfSize, halfSize, halfSize),
Vec3(-halfSize, halfSize, -halfSize),
Vec3(-halfSize, -halfSize, -halfSize),
redDiffuse);
// 右墙 (绿色)
scene.addQuad(
Vec3(halfSize, -halfSize, -halfSize),
Vec3(halfSize, halfSize, -halfSize),
Vec3(halfSize, halfSize, halfSize),
Vec3(halfSize, -halfSize, halfSize),
greenDiffuse);
// 光源(天花板中央的小矩形)
// double lightSize = 1.0;
// scene.addQuad(
// Vec3(-lightSize, halfSize - 0.01, -lightSize),
// Vec3(-lightSize, halfSize - 0.01, lightSize),
// Vec3(lightSize, halfSize - 0.01, lightSize),
// Vec3(lightSize, halfSize - 0.01, -lightSize),
// lightMat);
// 蓝色金属盒子 (左侧)
double box1Size = 1.2;
Vec3 box1Min(-halfSize + 0.8, -halfSize, -0.5);
Vec3 box1Max = box1Min + Vec3(box1Size, box1Size * 2, box1Size);
scene.addBox(box1Min, box1Max, blueMetal);
// 黄色金属盒子 (右侧)
double box2Size = 1.2;
Vec3 box2Min(halfSize - 0.8 - box2Size, -halfSize, 0.3);
Vec3 box2Max = box2Min + Vec3(box2Size, box2Size * 1.5, box2Size);
scene.addBox(box2Min, box2Max, yellowMetal);
}
// ============================================================================
// 主函数
// ============================================================================
int main() {
std::cout << "=== 面片变换渲染器 - 康奈尔盒子 ===" << std::endl;
// 创建场景
Scene scene;
createCornellBoxScene(scene);
std::cout << "场景创建完成,共" << scene.triangles.size() << " 个三角形" << std::endl;
// 设置相机
int width = 1024;
int height = 1024;
Vec3 cameraPos(0, 0, 12);
Vec3 lookAt(0, 0, 0);
Vec3 up(0, 1, 0);
double fov = 45.0;
Camera camera(cameraPos, lookAt, up, fov, width, height);
std::cout << "开始渲染..." << std::endl;
// 渲染
int maxReflectionDepth = 3; // 最大反射深度
Texture image = camera.render(scene, maxReflectionDepth);
// 保存图像
std::string filename = "cornell_box_rt9.ppm";
image.writePPM(filename);
std::cout << "渲染完成!图像已保存到 " << filename << std::endl;
return 0;
}