From ce80ddcb6d3fe0a7240f7230d9d148d776292f57 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 5 Nov 2014 21:26:02 +0100 Subject: [PATCH] hslt: more consistent shading frame computation in various shapes --- include/mitsuba/core/util.h | 39 ++++++++++++++++++++++++++++---- include/mitsuba/render/skdtree.h | 19 +++++----------- src/libcore/util.cpp | 38 ++++++++++++++++--------------- src/librender/bsdf.cpp | 25 ++++---------------- src/shapes/cylinder.cpp | 3 +-- src/shapes/deformable.cpp | 15 ++---------- src/shapes/disk.cpp | 5 +--- src/shapes/hair.cpp | 3 +-- src/shapes/heightfield.cpp | 6 +---- src/shapes/instance.cpp | 13 ++--------- src/shapes/rectangle.cpp | 4 ++-- src/shapes/sphere.cpp | 3 +-- 12 files changed, 75 insertions(+), 98 deletions(-) diff --git a/include/mitsuba/core/util.h b/include/mitsuba/core/util.h index d76acd7b..febae9e0 100644 --- a/include/mitsuba/core/util.h +++ b/include/mitsuba/core/util.h @@ -329,13 +329,42 @@ extern MTS_EXPORT_CORE bool solveLinearSystem2x2(const Float a[2][2], const Floa */ extern MTS_EXPORT_CORE void coordinateSystem(const Vector &a, Vector &b, Vector &c); + /** - * \brief Derivatives of a frame formed by coordinateSystem - * \param n Source tangent frame that was created with coordinateSystem - * \param ds derivative of the frame with respect to s. ds.n should already contain normal derivative along s - * \param dt derivative of the frame with respect to t. dt.n should already contain normal derivative along t + * \brief Given a smoothly varying shading normal and a tangent of a shape parameterization, + * compute a smoothly varying orthonormal frame + * + * \param n + * A shading normal at a surface position + * \param dpdu + * Position derivative of the underlying parameterization with respect to the 'u' coordinate + * \param frame + * Used to return the computed frame + * + * Mitsuba uses this function to compute the field \ref Intersection::shFrame */ -extern MTS_EXPORT_CORE void coordinateSystemDerivatives(const Frame &frame, Frame &ds, Frame &dt); +extern MTS_EXPORT_CORE void computeShadingFrame(const Vector &n, const Vector &dpdu, Frame &frame); + +/** + * \brief Compute the spatial derivative of \ref computeShadingFrame + * + * This is used by Manifold Exploration and Half Vector Light Transport + * + * \param n + * A shading normal at a surface position + * \param dpdu + * Position derivative of the underlying parameterization with respect to the 'u' coordinate + * \param dndu + * Derivative of the shading normal along the 'u' coordinate + * \param dndv + * Derivative of the shading normal along the 'v' coordinate + * \param du + * Used to return the 'u' derivative of the frame + * \param dv + * Used to return the 'v' derivative of the frame + */ +extern MTS_EXPORT_CORE void computeShadingFrameDerivative(const Vector &n, const Vector &dpdu, + const Vector &dndu, const Vector &dndv, Frame &du, Frame &dv); /** * \brief Generate (optionally jittered) stratified 1D samples diff --git a/include/mitsuba/render/skdtree.h b/include/mitsuba/render/skdtree.h index d9677046..b3eeb19d 100644 --- a/include/mitsuba/render/skdtree.h +++ b/include/mitsuba/render/skdtree.h @@ -376,6 +376,7 @@ protected: its.dpdu = side1; its.dpdv = side2; } + if (EXPECT_TAKEN(vertexNormals)) { const Normal &n0 = vertexNormals[idx0], @@ -384,23 +385,13 @@ protected: its.shFrame.n = normalize(n0 * b.x + n1 * b.y + n2 * b.z); - if (EXPECT_TAKEN(!vertexTangents)) { - coordinateSystem(its.shFrame.n, its.shFrame.s, its.shFrame.t); - } else { - /* Align shFrame.s with dpdu, use Gram-Schmidt to orthogonalize */ - its.shFrame.s = normalize(its.dpdu - its.shFrame.n - * dot(its.shFrame.n, its.dpdu)); - its.shFrame.t = cross(its.shFrame.n, its.shFrame.s); - } - /* Ensure that the geometric & shading normals face the same direction */ if (dot(faceNormal, its.shFrame.n) < 0) faceNormal = -faceNormal; - - its.geoFrame = Frame(faceNormal); } else { - its.shFrame = its.geoFrame = Frame(faceNormal); + its.shFrame.n = faceNormal; } + its.geoFrame = Frame(faceNormal); if (EXPECT_TAKEN(vertexTexcoords)) { const Point2 &t0 = vertexTexcoords[idx0]; @@ -420,7 +411,6 @@ protected: result[2], Spectrum::EReflectance); } - its.wi = its.toLocal(-ray.d); its.shape = trimesh; its.hasUVPartials = false; its.primIndex = cache->primIndex; @@ -430,6 +420,9 @@ protected: shape->fillIntersectionRecord(ray, reinterpret_cast(temp) + 2*sizeof(IndexType), its); } + + computeShadingFrame(its.shFrame.n, its.dpdu, its.shFrame); + its.wi = its.toLocal(-ray.d); } /// Plain shadow ray query (used by the 'instance' plugin) diff --git a/src/libcore/util.cpp b/src/libcore/util.cpp index 292f44cb..a113bb40 100644 --- a/src/libcore/util.cpp +++ b/src/libcore/util.cpp @@ -600,27 +600,29 @@ void coordinateSystem(const Vector &a, Vector &b, Vector &c) { b = cross(c, a); } -void coordinateSystemDerivatives(const Frame &frame, Frame &ds, Frame &dt) { +void computeShadingFrame(const Vector &n, const Vector &dpdu, Frame &frame) { + frame.n = n; + frame.s = normalize(dpdu - frame.n + * dot(frame.n, dpdu)); + frame.t = cross(frame.n, frame.s); +} - const Vector n = frame.n; - const Vector s = frame.s; +void computeShadingFrameDerivative(const Vector &n, const Vector &dpdu, const Vector &dndu, const Vector &dndv, Frame &du, Frame &dv) { + Vector s = dpdu - n * dot(n, dpdu); + Float invLen_s = 1.0f / s.length(); + s *= invLen_s; - if(std::abs(n.x) > std::abs(n.y)) { - const Float invLen = 1 / std::sqrt(n.x * n.x + n.z * n.z); - ds.s = Vector(ds.n.z * invLen, 0, -ds.n.x * invLen); - ds.s -= s * dot(ds.s, s); - dt.s = Vector(dt.n.z * invLen, 0, -dt.n.x * invLen); - dt.s -= s * dot(dt.s, s); - } else { - const Float invLen = 1 / std::sqrt(n.y * n.y + n.z * n.z); - ds.s = Vector(0, ds.n.z * invLen, -ds.n.y * invLen); - ds.s -= s * dot(ds.s, s); - dt.s = Vector(0, dt.n.z * invLen, -dt.n.y * invLen); - dt.s -= s * dot(dt.s, s); - } + du.s = invLen_s * (-dndu * dot(n, dpdu) - n * dot(dndu, dpdu)); + dv.s = invLen_s * (-dndv * dot(n, dpdu) - n * dot(dndv, dpdu)); - dt.t = cross(s, ds.n) + cross(dt.s, n); - ds.t = cross(s, dt.n) + cross(ds.s, n); + du.s -= s * dot(du.s, s); + dv.s -= s * dot(dv.s, s); + + du.t = cross(dndu, s) + cross(n, du.s); + dv.t = cross(dndv, s) + cross(n, dv.s); + + du.n = dndu; + dv.n = dndv; } Point2 toSphericalCoordinates(const Vector &v) { diff --git a/src/librender/bsdf.cpp b/src/librender/bsdf.cpp index 79f75d6b..528d452b 100644 --- a/src/librender/bsdf.cpp +++ b/src/librender/bsdf.cpp @@ -65,31 +65,14 @@ Float BSDF::getEta() const { Frame BSDF::getFrame(const Intersection &its) const { Frame result; - - result.n = its.shFrame.n; - result.s = normalize(its.dpdu - result.n - * dot(result.n, its.dpdu)); - result.t = cross(result.n, result.s); - + computeShadingFrame(its.shFrame.n, its.dpdu, result); return result; } void BSDF::getFrameDerivative(const Intersection &its, Frame &du, Frame &dv) const { - (its.instance ? its.instance : its.shape)->getNormalDerivative(its, du.n, dv.n, true); - - Vector n = its.shFrame.n; - Vector s = its.dpdu - n * dot(n, its.dpdu); - Float invLen_s = 1.0f / s.length(); - s *= invLen_s; - - du.s = invLen_s * (-du.n * dot(n, its.dpdu) - n * dot(du.n, its.dpdu)); - dv.s = invLen_s * (-dv.n * dot(n, its.dpdu) - n * dot(dv.n, its.dpdu)); - - du.s -= s * dot(du.s, s); - dv.s -= s * dot(dv.s, s); - - du.t = cross(du.n, s) + cross(n, du.s); - dv.t = cross(dv.n, s) + cross(n, dv.s); + Vector dndu, dndv; + (its.instance ? its.instance : its.shape)->getNormalDerivative(its, dndu, dndv, true); + computeShadingFrameDerivative(its.shFrame.n, its.dpdu, dndu, dndv, du, dv); } Float BSDF::getRoughness(const Intersection &its, int component) const { diff --git a/src/shapes/cylinder.cpp b/src/shapes/cylinder.cpp index c33fbc41..1cc60ef7 100644 --- a/src/shapes/cylinder.cpp +++ b/src/shapes/cylinder.cpp @@ -223,8 +223,7 @@ public: if (m_flipNormals) its.geoFrame.n *= -1; - its.shFrame = its.geoFrame; - its.wi = its.toLocal(-ray.d); + its.shFrame.n = its.geoFrame.n; its.hasUVPartials = false; its.instance = NULL; its.time = ray.time; diff --git a/src/shapes/deformable.cpp b/src/shapes/deformable.cpp index 7e00ef51..fffd7bad 100644 --- a/src/shapes/deformable.cpp +++ b/src/shapes/deformable.cpp @@ -477,23 +477,13 @@ public: its.shFrame.n = normalize(n0 * b.x + n1 * b.y + n2 * b.z); - if (EXPECT_TAKEN(!vertexTangents0)) { - coordinateSystem(its.shFrame.n, its.shFrame.s, its.shFrame.t); - } else { - /* Align shFrame.s with dpdu, use Gram-Schmidt to orthogonalize */ - its.shFrame.s = normalize(its.dpdu - its.shFrame.n - * dot(its.shFrame.n, its.dpdu)); - its.shFrame.t = cross(its.shFrame.n, its.shFrame.s); - } - /* Ensure that the geometric & shading normals face the same direction */ if (dot(faceNormal, its.shFrame.n) < 0) faceNormal = -faceNormal; - - its.geoFrame = Frame(faceNormal); } else { - its.shFrame = its.geoFrame = Frame(faceNormal); + its.shFrame.n = faceNormal; } + its.geoFrame = Frame(faceNormal); if (EXPECT_TAKEN(vertexTexcoords0)) { Point2 @@ -515,7 +505,6 @@ public: result[2], Spectrum::EReflectance); } - its.wi = its.toLocal(-ray.d); its.shape = m_kdtree->getMesh(0, cache->shapeIndex); its.hasUVPartials = false; its.primIndex = cache->primIndex; diff --git a/src/shapes/disk.cpp b/src/shapes/disk.cpp index a9bca923..232ad6c0 100644 --- a/src/shapes/disk.cpp +++ b/src/shapes/disk.cpp @@ -189,12 +189,9 @@ public: its.dpdv = trafo(Vector(0, 1, 0)); } - its.shFrame = its.geoFrame = Frame( - normalize(its.dpdu), normalize(its.dpdv), - normalize(trafo(Normal(0, 0, 1)))); + its.shFrame.n = normalize(trafo(Normal(0, 0, 1))); its.uv = Point2(r, phi * INV_TWOPI); its.p = ray(its.t); - its.wi = its.toLocal(-ray.d); its.hasUVPartials = false; its.instance = NULL; its.time = ray.time; diff --git a/src/shapes/hair.cpp b/src/shapes/hair.cpp index 0d5ec181..959998ea 100644 --- a/src/shapes/hair.cpp +++ b/src/shapes/hair.cpp @@ -845,8 +845,7 @@ void HairShape::fillIntersectionRecord(const Ray &ray, const Vector local = its.geoFrame.toLocal(relHitPoint); its.p += its.geoFrame.n * (m_kdtree->getRadius() - std::sqrt(local.y*local.y+local.z*local.z)); - its.shFrame = its.geoFrame; - its.wi = its.toLocal(-ray.d); + its.shFrame.n = its.geoFrame.n; its.hasUVPartials = false; its.instance = this; its.time = ray.time; diff --git a/src/shapes/heightfield.cpp b/src/shapes/heightfield.cpp index 72bb86e4..d1d5b5ca 100644 --- a/src/shapes/heightfield.cpp +++ b/src/shapes/heightfield.cpp @@ -402,11 +402,8 @@ public: its.shFrame.n = normalize(m_objectToWorld(Normal( (1 - temp.p.x) * ((1-temp.p.y) * n00 + temp.p.y * n01) + temp.p.x * ((1-temp.p.y) * n10 + temp.p.y * n11)))); - - its.shFrame.s = normalize(its.geoFrame.s - dot(its.geoFrame.s, its.shFrame.n) * its.shFrame.n); - its.shFrame.t = cross(its.shFrame.n, its.shFrame.s); } else { - its.shFrame = its.geoFrame; + its.shFrame.n = its.geoFrame.n; } if (m_flipNormals) { @@ -415,7 +412,6 @@ public: } its.shape = this; - its.wi = its.toLocal(-ray.d); its.hasUVPartials = false; its.instance = NULL; its.time = ray.time; diff --git a/src/shapes/instance.cpp b/src/shapes/instance.cpp index dff80e26..8017fb57 100644 --- a/src/shapes/instance.cpp +++ b/src/shapes/instance.cpp @@ -133,15 +133,11 @@ void Instance::adjustTime(Intersection &its, Float time) const { Transform trafo = m_transform->eval(its.time).inverse(); trafo = m_transform->eval(time) * trafo; - Vector s = trafo(its.shFrame.s); - its.shFrame.n = normalize(trafo(its.shFrame.n)); - its.shFrame.s = normalize(s - its.shFrame.n - * dot(its.shFrame.n, s)); - its.shFrame.t = cross(its.shFrame.n, its.shFrame.s); - its.geoFrame = Frame(normalize(trafo(its.geoFrame.n))); its.dpdu = trafo(its.dpdu); its.dpdv = trafo(its.dpdv); + its.geoFrame = Frame(normalize(trafo(its.geoFrame.n))); its.p = trafo(its.p); + computeShadingFrame(normalize(trafo(its.shFrame.n)), its.dpdu, its.shFrame); its.wi = normalize(trafo(its.wi)); its.instance = this; its.time = time; @@ -155,16 +151,11 @@ void Instance::fillIntersectionRecord(const Ray &_ray, trafo.inverse()(_ray, ray); kdtree->fillIntersectionRecord(ray, temp, its); - Vector s = trafo(its.shFrame.s); its.shFrame.n = normalize(trafo(its.shFrame.n)); - its.shFrame.s = normalize(s - its.shFrame.n - * dot(its.shFrame.n, s)); - its.shFrame.t = cross(its.shFrame.n, its.shFrame.s); its.geoFrame = Frame(normalize(trafo(its.geoFrame.n))); its.dpdu = trafo(its.dpdu); its.dpdv = trafo(its.dpdv); its.p = trafo(its.p); - its.wi = normalize(its.shFrame.toLocal(-_ray.d)); its.instance = this; } diff --git a/src/shapes/rectangle.cpp b/src/shapes/rectangle.cpp index 3e556268..5a886368 100644 --- a/src/shapes/rectangle.cpp +++ b/src/shapes/rectangle.cpp @@ -155,13 +155,13 @@ public: void fillIntersectionRecord(const Ray &ray, const void *temp, Intersection &its) const { const Float *data = static_cast(temp); - its.shFrame = its.geoFrame = m_frame; + its.geoFrame = m_frame; + its.shFrame = its.geoFrame.n; its.shape = this; its.dpdu = m_dpdu; its.dpdv = m_dpdv; its.uv = Point2(0.5f * (data[0]+1), 0.5f * (data[1]+1)); its.p = ray(its.t); - its.wi = its.toLocal(-ray.d); its.hasUVPartials = false; its.instance = NULL; its.time = ray.time; diff --git a/src/shapes/sphere.cpp b/src/shapes/sphere.cpp index 52bcd695..f3a34d6e 100644 --- a/src/shapes/sphere.cpp +++ b/src/shapes/sphere.cpp @@ -248,8 +248,7 @@ public: if (m_flipNormals) its.geoFrame.n *= -1; - its.shFrame = its.geoFrame; - its.wi = its.toLocal(-ray.d); + its.shFrame.n = its.geoFrame.n; its.hasUVPartials = false; its.instance = NULL; its.time = ray.time;