作业6的文件较多,这里只主要说明几个关键代码,分别是BVH.cpp和Triangle.hpp中的部分函数,我在这里贴出这两者的代码
#include <algorithm>
#include <cassert>
#include "BVH.hpp"
BVHAccel::BVHAccel(std::vector<Object*> p, int maxPrimsInNode,
SplitMethod splitMethod)
: maxPrimsInNode(std::min(255, maxPrimsInNode)), splitMethod(splitMethod),
primitives(std::move(p))
{
time_t start, stop;
time(&start); //记录开始构建BVH的时间
if (primitives.empty()) //如果没有物体,直接返回
return;
root = recursiveBuild(primitives); //递归构建BVH
time(&stop); //记录构建BVH结束的时间
double diff = difftime(stop, start); //计算构建BVH所用的时间
int hrs = (int)diff / 3600; //计算小时数
int mins = ((int)diff / 60) - (hrs * 60); //计算分钟数
int secs = (int)diff - (hrs * 3600) - (mins * 60); //计算秒数
printf(
"\rBVH Generation complete: \nTime Taken: %i hrs, %i mins, %i secs\n\n",
hrs, mins, secs); //输出构建BVH完成,以及所用的时间
}
BVHBuildNode* BVHAccel::recursiveBuild(std::vector<Object*> objects)
{
BVHBuildNode* node = new BVHBuildNode();
// Compute bounds of all primitives in BVH node
Bounds3 bounds;
//首先是创建一个节点
//然后遍历每个物体的getBounds方法获取包围它们的AABB的边界
//然后通过Union函数把这些AABB合为一个大的AABB作为节点
for (int i = 0; i < objects.size(); ++i)
bounds = Union(bounds, objects[i]->getBounds());
//根据当前AABB内物体的个数来决定划分的方法
if (objects.size() == 1) {//如果只有一个物体,那么它就作为叶节点,并把它的子节点设为空
// Create leaf _BVHBuildNode_
node->bounds = objects[0]->getBounds();
node->object = objects[0];
node->left = nullptr;
node->right = nullptr;
return node;
}
else if (objects.size() == 2) {//如果是有两个物体,那么就就将这两个物体分别放入两个子节点中进行遍历,这样就保证了每个节点最多只有一个物体了
node->left = recursiveBuild(std::vector{objects[0]});
node->right = recursiveBuild(std::vector{objects[1]});
node->bounds = Union(node->left->bounds, node->right->bounds);
return node;
}
else {//如果大于两个物体,那么就要划分当前的BVH
Bounds3 centroidBounds;
for (int i = 0; i < objects.size(); ++i)//遍历物体
centroidBounds =
Union(centroidBounds, objects[i]->getBounds().Centroid());//通过getBounds()的Centroid()方法找到所有物体的中心,通过Union函数找到它们覆盖的范围
int dim = centroidBounds.maxExtent();//通过maxExtent()方法判断到底是哪个轴覆盖的范围大
//返回0就是x轴范围大,返回1就是y轴范围大,返回2就是z轴范围大
//根据不同的情况,将物体根据中心坐标分别按照x、y、z轴排序,
switch (dim) {
case 0:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().x <
f2->getBounds().Centroid().x;
});
break;
case 1:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().y <
f2->getBounds().Centroid().y;
});
break;
case 2:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().z <
f2->getBounds().Centroid().z;
});
break;
}
//排序完后将物体对半划分,分别放入两个子节点中进行递归,直到只剩一个物体为止
//最后将当前节点的AABB赋值为两个子节点的Union
auto beginning = objects.begin();
auto middling = objects.begin() + (objects.size() / 2);
auto ending = objects.end();
auto leftshapes = std::vector<Object*>(beginning, middling);
auto rightshapes = std::vector<Object*>(middling, ending);
assert(objects.size() == (leftshapes.size() + rightshapes.size()));
node->left = recursiveBuild(leftshapes);
node->right = recursiveBuild(rightshapes);
node->bounds = Union(node->left->bounds, node->right->bounds);
}
return node;
}
Intersection BVHAccel::Intersect(const Ray& ray) const
{
Intersection isect;
if (!root)
return isect;
isect = BVHAccel::getIntersection(root, ray);
return isect;
}
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
// 定义三个交点结构体
Intersection intersect, intersectl, intersectr;
// 检查光线是否与 BVH 节点的包围盒相交
if (!node->bounds.IntersectP(ray, ray.direction_inv))
return intersect;
// 如果当前 BVH 节点是叶子节点,则计算光线与物体的交点
if (node->left == nullptr && node->right == nullptr) {
intersect = node->object->getIntersection(ray);
return intersect;
}
// 如果当前 BVH 节点不是叶子节点,则递归地计算光线与左右子节点的交点
intersectl = getIntersection(node->left, ray);
intersectr = getIntersection(node->right, ray);
// 比较两个交点的距离,返回距离较小的那个交点
return intersectl.distance < intersectr.distance ? intersectl : intersectr;
}
#pragma once
#include "BVH.hpp"
#include "Intersection.hpp"
#include "Material.hpp"
#include "OBJ_Loader.hpp"
#include "Object.hpp"
#include "Triangle.hpp"
#include <cassert>
#include <array>
bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1,
const Vector3f& v2, const Vector3f& orig,
const Vector3f& dir, float& tnear, float& u, float& v)
{
Vector3f edge1 = v1 - v0;
Vector3f edge2 = v2 - v0;
Vector3f pvec = crossProduct(dir, edge2);
float det = dotProduct(edge1, pvec);
if (det == 0 || det < 0)
return false;
Vector3f tvec = orig - v0;
u = dotProduct(tvec, pvec);
if (u < 0 || u > det)
return false;
Vector3f qvec = crossProduct(tvec, edge1);
v = dotProduct(dir, qvec);
if (v < 0 || u + v > det)
return false;
float invDet = 1 / det;
tnear = dotProduct(edge2, qvec) * invDet;
u *= invDet;
v *= invDet;
return true;
}
class Triangle : public Object
{
public:
Vector3f v0, v1, v2; // vertices A, B ,C , counter-clockwise order
Vector3f e1, e2; // 2 edges v1-v0, v2-v0;
Vector3f t0, t1, t2; // texture coords
Vector3f normal;
Material* m;
Triangle(Vector3f _v0, Vector3f _v1, Vector3f _v2, Material* _m = nullptr)
: v0(_v0), v1(_v1), v2(_v2), m(_m)
{
e1 = v1 - v0;
e2 = v2 - v0;
normal = normalize(crossProduct(e1, e2));
}
bool intersect(const Ray& ray) override;
bool intersect(const Ray& ray, float& tnear,
uint32_t& index) const override;
Intersection getIntersection(Ray ray) override;
void getSurfaceProperties(const Vector3f& P, const Vector3f& I,
const uint32_t& index, const Vector2f& uv,
Vector3f& N, Vector2f& st) const override
{
N = normal;
// throw std::runtime_error("triangle::getSurfaceProperties not
// implemented.");
}
Vector3f evalDiffuseColor(const Vector2f&) const override;
Bounds3 getBounds() override;
};
class MeshTriangle : public Object
{
public:
MeshTriangle(const std::string& filename)
{
objl::Loader loader;
loader.LoadFile(filename);
assert(loader.LoadedMeshes.size() == 1);
auto mesh = loader.LoadedMeshes[0];
Vector3f min_vert = Vector3f{std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::infinity()};
Vector3f max_vert = Vector3f{-std::numeric_limits<float>::infinity(),
-std::numeric_limits<float>::infinity(),
-std::numeric_limits<float>::infinity()};
for (int i = 0; i < mesh.Vertices.size(); i += 3) {
std::array<Vector3f, 3> face_vertices;
for (int j = 0; j < 3; j++) {
auto vert = Vector3f(mesh.Vertices[i + j].Position.X,
mesh.Vertices[i + j].Position.Y,
mesh.Vertices[i + j].Position.Z) *
60.f;
face_vertices[j] = vert;
min_vert = Vector3f(std::min(min_vert.x, vert.x),
std::min(min_vert.y, vert.y),
std::min(min_vert.z, vert.z));
max_vert = Vector3f(std::max(max_vert.x, vert.x),
std::max(max_vert.y, vert.y),
std::max(max_vert.z, vert.z));
}
auto new_mat =
new Material(MaterialType::DIFFUSE_AND_GLOSSY,
Vector3f(0.5, 0.5, 0.5), Vector3f(0, 0, 0));
new_mat->Kd = 0.6;
new_mat->Ks = 0.0;
new_mat->specularExponent = 0;
triangles.emplace_back(face_vertices[0], face_vertices[1],
face_vertices[2], new_mat);
}
bounding_box = Bounds3(min_vert, max_vert);
std::vector<Object*> ptrs;
for (auto& tri : triangles)
ptrs.push_back(&tri);
bvh = new BVHAccel(ptrs);
}
bool intersect(const Ray& ray) { return true; }
bool intersect(const Ray& ray, float& tnear, uint32_t& index) const
{
bool intersect = false;
for (uint32_t k = 0; k < numTriangles; ++k) {
const Vector3f& v0 = vertices[vertexIndex[k * 3]];
const Vector3f& v1 = vertices[vertexIndex[k * 3 + 1]];
const Vector3f& v2 = vertices[vertexIndex[k * 3 + 2]];
float t, u, v;
if (rayTriangleIntersect(v0, v1, v2, ray.origin, ray.direction, t,
u, v) &&
t < tnear) {
tnear = t;
index = k;
intersect |= true;
}
}
return intersect;
}
Bounds3 getBounds() { return bounding_box; }
void getSurfaceProperties(const Vector3f& P, const Vector3f& I,
const uint32_t& index, const Vector2f& uv,
Vector3f& N, Vector2f& st) const
{
const Vector3f& v0 = vertices[vertexIndex[index * 3]];
const Vector3f& v1 = vertices[vertexIndex[index * 3 + 1]];
const Vector3f& v2 = vertices[vertexIndex[index * 3 + 2]];
Vector3f e0 = normalize(v1 - v0);
Vector3f e1 = normalize(v2 - v1);
N = normalize(crossProduct(e0, e1));
const Vector2f& st0 = stCoordinates[vertexIndex[index * 3]];
const Vector2f& st1 = stCoordinates[vertexIndex[index * 3 + 1]];
const Vector2f& st2 = stCoordinates[vertexIndex[index * 3 + 2]];
st = st0 * (1 - uv.x - uv.y) + st1 * uv.x + st2 * uv.y;
}
Vector3f evalDiffuseColor(const Vector2f& st) const
{
float scale = 5;
float pattern =
(fmodf(st.x * scale, 1) > 0.5) ^ (fmodf(st.y * scale, 1) > 0.5);
return lerp(Vector3f(0.815, 0.235, 0.031),
Vector3f(0.937, 0.937, 0.231), pattern);
}
Intersection getIntersection(Ray ray)
{
Intersection intersec;
if (bvh) {
intersec = bvh->Intersect(ray);
}
return intersec;
}
Bounds3 bounding_box;
std::unique_ptr<Vector3f[]> vertices;
uint32_t numTriangles;
std::unique_ptr<uint32_t[]> vertexIndex;
std::unique_ptr<Vector2f[]> stCoordinates;
std::vector<Triangle> triangles;
BVHAccel* bvh;
Material* m;
};
inline bool Triangle::intersect(const Ray& ray) { return true; }
inline bool Triangle::intersect(const Ray& ray, float& tnear,
uint32_t& index) const
{
return false;
}
inline Bounds3 Triangle::getBounds() { return Union(Bounds3(v0, v1), v2); }
inline Intersection Triangle::getIntersection(Ray ray)
{
Intersection inter;
// 检查光线方向与三角形法向量的点积是否大于零
if (dotProduct(ray.direction, normal) > 0)
return inter;
//用 Möller–Trumbore 算法计算光线与三角形的交点
double u, v, t_tmp = 0;
Vector3f pvec = crossProduct(ray.direction, e2);
double det = dotProduct(e1, pvec);
// 如果 det 的绝对值小于 EPSILON,则返回空的交点结构体
if (fabs(det) < EPSILON)
return inter;
double det_inv = 1. / det;
Vector3f tvec = ray.origin - v0;
u = dotProduct(tvec, pvec) * det_inv;
// 如果 u 小于零或大于一,则返回空的交点结构体
if (u < 0 || u > 1)
return inter;
Vector3f qvec = crossProduct(tvec, e1);
v = dotProduct(ray.direction, qvec) * det_inv;
// 如果 v 小于零或者 u + v 大于一,则返回空的交点结构体
if (v < 0 || u + v > 1)
return inter;
// t_tmp表示光线与三角形相交时光线上的距离
t_tmp = dotProduct(e2, qvec) * det_inv;
// 计算并更新交点结构体
inter.happened = true;
inter.obj = this;
inter.distance = t_tmp;
inter.normal = normal;
inter.coords = ray(t_tmp);
inter.m = this->m;
return inter;
}
inline Vector3f Triangle::evalDiffuseColor(const Vector2f&) const
{
return Vector3f(0.5, 0.5, 0.5);
}
凭借一些个人的理解对大多数代码加上了注释,有些地方肯定会有理解不到位或者不对的情况,欢迎大佬留言指正。
完成内容:完成了Whittd-style的光线追踪渲染,使用BVH结构进行加速求交
实验结果:
完整代码git自取:Yuchenpoet/CG (github.com)