Nori  23
mesh.cpp
1 /*
2  This file is part of Nori, a simple educational ray tracer
3 
4  Copyright (c) 2015 by Wenzel Jakob, Romain Prévost
5 
6  Nori is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License Version 3
8  as published by the Free Software Foundation.
9 
10  Nori is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <nori/mesh.h>
20 #include <nori/bbox.h>
21 #include <nori/bsdf.h>
22 #include <nori/emitter.h>
23 #include <nori/warp.h>
24 #include <Eigen/Geometry>
25 
26 NORI_NAMESPACE_BEGIN
27 
29 
32 
33  m_pdf.reserve(getPrimitiveCount());
34  for(uint32_t i = 0 ; i < getPrimitiveCount() ; ++i) {
35  m_pdf.append(surfaceArea(i));
36  }
37  m_pdf.normalize();
38 }
39 
40 void Mesh::sampleSurface(ShapeQueryRecord & sRec, const Point2f & sample) const {
41  Point2f s = sample;
42  size_t idT = m_pdf.sampleReuse(s.x());
43 
44  Vector3f bc = Warp::squareToUniformTriangle(s);
45 
46  sRec.p = getInterpolatedVertex(idT,bc);
47  if (m_N.size() > 0) {
48  sRec.n = getInterpolatedNormal(idT, bc);
49  }
50  else {
51  Point3f p0 = m_V.col(m_F(0, idT));
52  Point3f p1 = m_V.col(m_F(1, idT));
53  Point3f p2 = m_V.col(m_F(2, idT));
54  Normal3f n = (p1-p0).cross(p2-p0).normalized();
55  sRec.n = n;
56  }
57  sRec.pdf = m_pdf.getNormalization();
58 }
59 float Mesh::pdfSurface(const ShapeQueryRecord & sRec) const {
60  return m_pdf.getNormalization();
61 }
62 
63 Point3f Mesh::getInterpolatedVertex(uint32_t index, const Vector3f &bc) const {
64  return (bc.x() * m_V.col(m_F(0, index)) +
65  bc.y() * m_V.col(m_F(1, index)) +
66  bc.z() * m_V.col(m_F(2, index)));
67 }
68 
69 Normal3f Mesh::getInterpolatedNormal(uint32_t index, const Vector3f &bc) const {
70  return (bc.x() * m_N.col(m_F(0, index)) +
71  bc.y() * m_N.col(m_F(1, index)) +
72  bc.z() * m_N.col(m_F(2, index))).normalized();
73 }
74 
75 float Mesh::surfaceArea(uint32_t index) const {
76  uint32_t i0 = m_F(0, index), i1 = m_F(1, index), i2 = m_F(2, index);
77 
78  const Point3f p0 = m_V.col(i0), p1 = m_V.col(i1), p2 = m_V.col(i2);
79 
80  return 0.5f * Vector3f((p1 - p0).cross(p2 - p0)).norm();
81 }
82 
83 bool Mesh::rayIntersect(uint32_t index, const Ray3f &ray, float &u, float &v, float &t) const {
84  uint32_t i0 = m_F(0, index), i1 = m_F(1, index), i2 = m_F(2, index);
85  const Point3f p0 = m_V.col(i0), p1 = m_V.col(i1), p2 = m_V.col(i2);
86 
87  /* Find vectors for two edges sharing v[0] */
88  Vector3f edge1 = p1 - p0, edge2 = p2 - p0;
89 
90  /* Begin calculating determinant - also used to calculate U parameter */
91  Vector3f pvec = ray.d.cross(edge2);
92 
93  /* If determinant is near zero, ray lies in plane of triangle */
94  float det = edge1.dot(pvec);
95 
96  if (det > -1e-8f && det < 1e-8f)
97  return false;
98  float inv_det = 1.0f / det;
99 
100  /* Calculate distance from v[0] to ray origin */
101  Vector3f tvec = ray.o - p0;
102 
103  /* Calculate U parameter and test bounds */
104  u = tvec.dot(pvec) * inv_det;
105  if (u < 0.0 || u > 1.0)
106  return false;
107 
108  /* Prepare to test V parameter */
109  Vector3f qvec = tvec.cross(edge1);
110 
111  /* Calculate V parameter and test bounds */
112  v = ray.d.dot(qvec) * inv_det;
113  if (v < 0.0 || u + v > 1.0)
114  return false;
115 
116  /* Ray intersects triangle -> compute t */
117  t = edge2.dot(qvec) * inv_det;
118 
119  return t >= ray.mint && t <= ray.maxt;
120 }
121 
122 void Mesh::setHitInformation(uint32_t index, const Ray3f &ray, Intersection & its) const {
123  /* Find the barycentric coordinates */
124  Vector3f bary;
125  bary << 1-its.uv.sum(), its.uv;
126 
127  /* Vertex indices of the triangle */
128  uint32_t idx0 = m_F(0, index), idx1 = m_F(1, index), idx2 = m_F(2, index);
129 
130  Point3f p0 = m_V.col(idx0), p1 = m_V.col(idx1), p2 = m_V.col(idx2);
131 
132  /* Compute the intersection positon accurately
133  using barycentric coordinates */
134  its.p = bary.x() * p0 + bary.y() * p1 + bary.z() * p2;
135 
136  /* Compute proper texture coordinates if provided by the mesh */
137  if (m_UV.size() > 0)
138  its.uv = bary.x() * m_UV.col(idx0) +
139  bary.y() * m_UV.col(idx1) +
140  bary.z() * m_UV.col(idx2);
141 
142  /* Compute the geometry frame */
143  its.geoFrame = Frame((p1-p0).cross(p2-p0).normalized());
144 
145  if (m_N.size() > 0) {
146  /* Compute the shading frame. Note that for simplicity,
147  the current implementation doesn't attempt to provide
148  tangents that are continuous across the surface. That
149  means that this code will need to be modified to be able
150  use anisotropic BRDFs, which need tangent continuity */
151 
152  its.shFrame = Frame(
153  (bary.x() * m_N.col(idx0) +
154  bary.y() * m_N.col(idx1) +
155  bary.z() * m_N.col(idx2)).normalized());
156  } else {
157  its.shFrame = its.geoFrame;
158  }
159 }
160 
161 BoundingBox3f Mesh::getBoundingBox(uint32_t index) const {
162  BoundingBox3f result(m_V.col(m_F(0, index)));
163  result.expandBy(m_V.col(m_F(1, index)));
164  result.expandBy(m_V.col(m_F(2, index)));
165  return result;
166 }
167 
168 Point3f Mesh::getCentroid(uint32_t index) const {
169  return (1.0f / 3.0f) *
170  (m_V.col(m_F(0, index)) +
171  m_V.col(m_F(1, index)) +
172  m_V.col(m_F(2, index)));
173 }
174 
175 
176 std::string Mesh::toString() const {
177  return tfm::format(
178  "Mesh[\n"
179  " name = \"%s\",\n"
180  " vertexCount = %i,\n"
181  " triangleCount = %i,\n"
182  " bsdf = %s,\n"
183  " emitter = %s\n"
184  "]",
185  m_name,
186  m_V.cols(),
187  m_F.cols(),
188  m_bsdf ? indent(m_bsdf->toString()) : std::string("null"),
189  m_emitter ? indent(m_emitter->toString()) : std::string("null")
190  );
191 }
192 
193 NORI_NAMESPACE_END
MatrixXf m_UV
Vertex texture coordinates.
Definition: mesh.h:122
virtual void setHitInformation(uint32_t index, const Ray3f &ray, Intersection &its) const override
Set intersection information: hit point, shading frame, UVs.
Definition: mesh.cpp:122
virtual void sampleSurface(ShapeQueryRecord &sRec, const Point2f &sample) const override
Uniformly sample a position on the mesh with respect to surface area.
Definition: mesh.cpp:40
MatrixXf m_N
Vertex normals.
Definition: mesh.h:121
virtual std::string toString() const override
Return a human-readable summary of this instance.
Definition: mesh.cpp:176
std::string m_name
Identifying name.
Definition: mesh.h:119
Mesh()
Create an empty mesh.
Definition: mesh.cpp:28
virtual uint32_t getPrimitiveCount() const override
Return the total number of triangles in this shape.
Definition: mesh.h:41
virtual void activate() override
Initialize internal data structures (called once by the XML parser)
Definition: mesh.cpp:30
MatrixXf m_V
Vertex positions.
Definition: mesh.h:120
virtual bool rayIntersect(uint32_t index, const Ray3f &ray, float &u, float &v, float &t) const override
Ray-triangle intersection test.
Definition: mesh.cpp:83
virtual float pdfSurface(const ShapeQueryRecord &sRec) const override
Return the probability of sampling a point sRec.p by the sampleSurface() method (sRec....
Definition: mesh.cpp:59
MatrixXu m_F
Faces.
Definition: mesh.h:123
float surfaceArea(uint32_t index) const
Return the surface area of the given triangle.
Definition: mesh.cpp:75
virtual std::string toString() const =0
Return a brief string summary of the instance (for debugging purposes)
virtual void activate() override
Initialize internal data structures (called once by the XML parser)
Definition: shape.cpp:32
BSDF * m_bsdf
BSDF of the surface.
Definition: shape.h:157
Emitter * m_emitter
Associated emitter, if any.
Definition: shape.h:158
float getNormalization() const
Return the normalization factor (i.e. the inverse of getSum())
Definition: dpdf.h:88
size_t sampleReuse(float &sampleValue) const
Transform a uniformly distributed sample to the stored distribution
Definition: dpdf.h:152
void append(float pdfValue)
Append an entry with the specified discrete probability.
Definition: dpdf.h:55
void reserve(size_t nEntries)
Reserve memory for a certain number of entries.
Definition: dpdf.h:50
float normalize()
Normalize the distribution.
Definition: dpdf.h:97
Stores a three-dimensional orthonormal coordinate frame.
Definition: frame.h:33
Intersection data structure.
Definition: shape.h:37
Frame shFrame
Shading frame (based on the shading normal)
Definition: shape.h:45
Frame geoFrame
Geometric frame (based on the true geometry)
Definition: shape.h:47
Point3f p
Position of the surface intersection.
Definition: shape.h:39
Point2f uv
UV coordinates, if any.
Definition: shape.h:43
3-dimensional surface normal representation
Definition: vector.h:133
Data record for conveniently querying and sampling the a point on a shape.
Definition: shape.h:74
Normal3f n
Sampled normal.
Definition: shape.h:80
Point3f p
Sampled point.
Definition: shape.h:78
float pdf
Probability of the sample.
Definition: shape.h:82
VectorType d
Ray direction.
Definition: ray.h:44
Scalar mint
Minimum position on the ray segment.
Definition: ray.h:46
Scalar maxt
Maximum position on the ray segment.
Definition: ray.h:47
PointType o
Ray origin.
Definition: ray.h:43