110 lines
3.8 KiB
C++
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 = 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
|
|
|