nori/src/accel.cpp

110 lines
3.8 KiB
C++

/*
This file is part of Nori, a simple educational ray tracer
Copyright (c) 2015 by Wenzel Jakob
Nori is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Nori is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <nori/accel.h>
#include <Eigen/Geometry>
#include <chrono>
NORI_NAMESPACE_BEGIN
void Accel::addMesh(Mesh *mesh) {
if (m_mesh)
throw NoriException("Accel: only a single mesh is supported!");
m_mesh = mesh;
m_bbox = m_mesh->getBoundingBox();
}
void Accel::build() {
auto triangles = new std::vector<uint32_t>(m_mesh->getTriangleCount());
for (uint32_t idx = 0; idx < m_mesh->getTriangleCount(); ++idx) {
(*triangles)[idx] = idx;
}
cout << "Building octree" << endl;
auto start = std::chrono::high_resolution_clock::now();
m_octree = Octree::build(&m_bbox, m_mesh, triangles);
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
std::cout << "Octree build took " << duration.count() << "ms" << std::endl;
}
bool Accel::rayIntersect(const Ray3f &ray_, Intersection &its, bool shadowRay) const {
Ray3f ray(ray_); /// Make a copy of the ray (we will need to update its '.maxt' value)
auto f = m_octree->getIntersectingTriangle(ray, its, shadowRay);
bool found = f != (uint32_t) -1;
if (found && !shadowRay) {
/* At this point, we now know that there is an intersection,
and we know the triangle index of the closest such intersection.
The following computes a number of additional properties which
characterize the intersection (normals, texture coordinates, etc..)
*/
/* Find the barycentric coordinates */
Vector3f bary;
bary << 1-its.uv.sum(), its.uv;
/* References to all relevant mesh buffers */
const Mesh *mesh = its.mesh;
const MatrixXf &V = mesh->getVertexPositions();
const MatrixXf &N = mesh->getVertexNormals();
const MatrixXf &UV = mesh->getVertexTexCoords();
const MatrixXu &F = mesh->getIndices();
/* Vertex indices of the triangle */
uint32_t idx0 = F(0, f), idx1 = F(1, f), idx2 = F(2, f);
Point3f p0 = V.col(idx0), p1 = V.col(idx1), p2 = V.col(idx2);
/* Compute the intersection positon accurately
using barycentric coordinates */
its.p = bary.x() * p0 + bary.y() * p1 + bary.z() * p2;
/* Compute proper texture coordinates if provided by the mesh */
if (UV.size() > 0)
its.uv = bary.x() * UV.col(idx0) +
bary.y() * UV.col(idx1) +
bary.z() * UV.col(idx2);
/* Compute the geometry frame */
its.geoFrame = Frame((p1-p0).cross(p2-p0).normalized());
if (N.size() > 0) {
/* Compute the shading frame. Note that for simplicity,
the current implementation doesn't attempt to provide
tangents that are continuous across the surface. That
means that this code will need to be modified to be able
use anisotropic BRDFs, which need tangent continuity */
its.shFrame = Frame(
(bary.x() * N.col(idx0) +
bary.y() * N.col(idx1) +
bary.z() * N.col(idx2)).normalized());
} else {
its.shFrame = its.geoFrame;
}
}
return found;
}
NORI_NAMESPACE_END