int main() { Imath::Rand48 rng; float min = std::numeric_limits<float>::max(); float max = -std::numeric_limits<float>::max(); PerlinNoise::Ptr perlin(new PerlinNoise); for (int i = 0; i < 1000000; i++) { Vector p; p.x = rng.nextf() * 100; p.y = rng.nextf() * 100; p.z = rng.nextf() * 100; float noise = perlin->eval(p.x, p.y, p.z); min = std::min(min, noise); max = std::max(max, noise); } cout << "min: " << min << endl << "max: " << max << endl; }
void testTranslationRotationMatrix (const Imath::M44d& mat) { std::cout << "Testing known translate/rotate matrix:\n " << mat; typedef Imath::Vec3<T> Vec; static Imath::Rand48 rand (2047); size_t numPoints = 7; std::vector<Vec> from; from.reserve (numPoints); std::vector<Vec> to; to.reserve (numPoints); for (size_t i = 0; i < numPoints; ++i) { Imath::V3d a (rand.nextf(), rand.nextf(), rand.nextf()); Imath::V3d b = a * mat; from.push_back (Vec(a)); to.push_back (Vec(b)); } std::vector<T> weights (numPoints, T(1)); const Imath::M44d m1 = procrustesRotationAndTranslation (&from[0], &to[0], &weights[0], numPoints); const Imath::M44d m2 = procrustesRotationAndTranslation (&from[0], &to[0], numPoints); const T eps = sizeof(T) == 8 ? 1e-8 : 1e-4; for (size_t i = 0; i < numPoints; ++i) { const Imath::V3d a = from[i]; const Imath::V3d b = to[i]; const Imath::V3d b1 = a * m1; const Imath::V3d b2 = a * m2; assert ((b - b1).length() < eps); assert ((b - b2).length() < eps); } std::cout << " OK\n"; }
void testProcrustesWithMatrix (const Imath::M44d& m) { std::cout << "Testing Procrustes algorithm with arbitrary matrix: \n" << m; std::vector<Imath::Vec3<T> > fromPoints; std::vector<Imath::Vec3<T> > toPoints; Imath::Rand48 random (1209); std::cout << " numPoints: "; for (size_t numPoints = 1; numPoints < 10; ++numPoints) { std::cout << numPoints << " " << std::flush; fromPoints.clear(); toPoints.clear(); for (size_t i = 0; i < numPoints; ++i) { const Imath::V3d fromPt (random.nextf(), random.nextf(), random.nextf()); const Imath::V3d toPt = fromPt * m; fromPoints.push_back (Imath::Vec3<T>(fromPt)); toPoints.push_back (Imath::Vec3<T>(toPt)); } verifyProcrustes (fromPoints, toPoints); } std::cout << "OK\n"; }
Imath::C3f radiance(Scene& scene, Ray& ray, Imath::Rand48& rnd, const int depth) { Imath::V3d intersection_point, intersection_normal; int obj_id; if (!scene.intersect(ray, intersection_point, intersection_normal, obj_id)) { return scene.bgColor; } Geometry geom = scene.geometries[obj_id]; Imath::V3d orienting_normal = intersection_normal; if ((intersection_normal ^ ray.dir) > 0.0) { orienting_normal *= -1.0; } if (depth > LIMIT_DEPTH) { return geom.emission; } float reflect_ratio = (5.0f - static_cast<float>(depth)) / 5.0f; Imath::C3f inc_rad(0,0,0), weight(1,1,1); switch (geom.reflection) { case Reflection::Diffuse: { Imath::V3d w, u, v; w = orienting_normal; if (fabs(w.x) > 0.0000009) { u = (Imath::V3d(0.0, 1.0, 0.0) % w).normalized(); } else { u = (Imath::V3d(1.0, 0.0, 0.0) % w).normalized(); } v = w % u; double r1 = 2.0 * M_PI * rnd.nextf(); double r2 = rnd.nextf(); double rr2 = sqrt(r2); ray.pos = intersection_point; ray.dir = (u * cos(r1) * rr2 + v * sin(r1) * rr2 + w * sqrt(1.0 - r2)).normalized(); inc_rad = radiance(scene, ray, rnd, depth + 1); weight = geom.color * reflect_ratio; } break; case Reflection::Specular: ray.pos = intersection_point; ray.dir -= intersection_normal * 2.0 * (intersection_normal ^ ray.dir); inc_rad = radiance(scene, ray, rnd, depth + 1); weight = geom.color * reflect_ratio; break; case Reflection::Refraction: bool into = (orienting_normal ^ intersection_normal) > 0.0; double default_refraction = 1.0; double object_refraction = 1.5; double ray_refraction; if (into) { ray_refraction = default_refraction / object_refraction; } else { ray_refraction = object_refraction / default_refraction; } double incident_dot = ray.dir ^ orienting_normal; double critical_factor = 1.0 - pow(ray_refraction, 2) * (1.0 - pow(incident_dot,2)); Ray reflection_ray(intersection_point, ray.dir - intersection_normal * 2.0 * (intersection_normal ^ ray.dir)); Ray refraction_ray(intersection_point, (ray.dir * ray_refraction - intersection_normal * (into ? 1.0 : -1.0) * (incident_dot * ray_refraction + sqrt(critical_factor))).normalized()); // total reflection if (critical_factor < 0.0) { inc_rad = radiance(scene, reflection_ray, rnd, depth + 1); weight = geom.color * reflect_ratio; break; } double a = object_refraction - default_refraction; double b = object_refraction + default_refraction; double vertical_incidence_factor = pow(a, 2) / pow(b, 2); double c = 1.0 - (into ? -1.0 * incident_dot : (refraction_ray.dir ^ -1.0 * orienting_normal)); double fresnel_incidence_factor = vertical_incidence_factor + (1.0 - vertical_incidence_factor) * pow(c,5); double radiance_scale = pow(ray_refraction, 2.0); double refraction_factor = (1.0 - fresnel_incidence_factor) * radiance_scale; double probability = 0.75 + fresnel_incidence_factor; if (depth > 2) { if (rnd.nextf() < probability) { inc_rad = radiance(scene, reflection_ray, rnd, depth + 1) * fresnel_incidence_factor; weight = geom.color * reflect_ratio; } else { inc_rad = radiance(scene, refraction_ray, rnd, depth + 1) * refraction_factor; weight = geom.color * reflect_ratio; } } else { inc_rad = radiance(scene, reflection_ray, rnd, depth + 1) * fresnel_incidence_factor + radiance(scene, refraction_ray, rnd, depth + 1) * refraction_factor; weight = geom.color * reflect_ratio; } break; } return geom.emission + weight * inc_rad; }
void testProcrustesImp () { // Test the empty case: Imath::M44d id = procrustesRotationAndTranslation ((Imath::Vec3<T>*) 0, (Imath::Vec3<T>*) 0, (T*) 0, 0); assert (id == Imath::M44d()); id = procrustesRotationAndTranslation ((Imath::Vec3<T>*) 0, (Imath::Vec3<T>*) 0, 0); assert (id == Imath::M44d()); // First we'll test with a bunch of known translation/rotation matrices // to make sure we get back exactly the same points: Imath::M44d m; m.makeIdentity(); testTranslationRotationMatrix<T> (m); m.translate (Imath::V3d(3.0, 5.0, -0.2)); testTranslationRotationMatrix<T> (m); m.rotate (Imath::V3d(M_PI, 0, 0)); testTranslationRotationMatrix<T> (m); m.rotate (Imath::V3d(0, M_PI/4.0, 0)); testTranslationRotationMatrix<T> (m); m.rotate (Imath::V3d(0, 0, -3.0/4.0 * M_PI)); testTranslationRotationMatrix<T> (m); m.makeIdentity(); testWithTranslateRotateAndScale<T> (m); m.translate (Imath::V3d(0.4, 6.0, 10.0)); testWithTranslateRotateAndScale<T> (m); m.rotate (Imath::V3d(M_PI, 0, 0)); testWithTranslateRotateAndScale<T> (m); m.rotate (Imath::V3d(0, M_PI/4.0, 0)); testWithTranslateRotateAndScale<T> (m); m.rotate (Imath::V3d(0, 0, -3.0/4.0 * M_PI)); testWithTranslateRotateAndScale<T> (m); m.scale (Imath::V3d(2.0, 2.0, 2.0)); testWithTranslateRotateAndScale<T> (m); m.scale (Imath::V3d(0.01, 0.01, 0.01)); testWithTranslateRotateAndScale<T> (m); // Now we'll test with some random point sets and verify // the various Procrustes properties: std::vector<Imath::Vec3<T> > fromPoints; std::vector<Imath::Vec3<T> > toPoints; fromPoints.clear(); toPoints.clear(); for (size_t i = 0; i < 4; ++i) { const T theta = T(2*i) / T(M_PI); fromPoints.push_back (Imath::Vec3<T>(cos(theta), sin(theta), 0)); toPoints.push_back (Imath::Vec3<T>(cos(theta + M_PI/3.0), sin(theta + M_PI/3.0), 0)); } verifyProcrustes (fromPoints, toPoints); Imath::Rand48 random (1209); for (size_t numPoints = 1; numPoints < 10; ++numPoints) { fromPoints.clear(); toPoints.clear(); for (size_t i = 0; i < numPoints; ++i) { fromPoints.push_back (Imath::Vec3<T>(random.nextf(), random.nextf(), random.nextf())); toPoints.push_back (Imath::Vec3<T>(random.nextf(), random.nextf(), random.nextf())); } } verifyProcrustes (fromPoints, toPoints); // Test with some known matrices of varying degrees of quality: testProcrustesWithMatrix<T> (m); m.translate (Imath::Vec3<T>(3, 4, 1)); testProcrustesWithMatrix<T> (m); m.translate (Imath::Vec3<T>(-10, 2, 1)); testProcrustesWithMatrix<T> (m); Imath::Eulerd rot (M_PI/3.0, 3.0*M_PI/4.0, 0); m = m * rot.toMatrix44(); testProcrustesWithMatrix<T> (m); m.scale (Imath::Vec3<T>(1.5, 6.4, 2.0)); testProcrustesWithMatrix<T> (m); Imath::Eulerd rot2 (1.0, M_PI, M_PI/3.0); m = m * rot.toMatrix44(); m.scale (Imath::Vec3<T>(-1, 1, 1)); testProcrustesWithMatrix<T> (m); m.scale (Imath::Vec3<T>(1, 0.001, 1)); testProcrustesWithMatrix<T> (m); m.scale (Imath::Vec3<T>(1, 1, 0)); testProcrustesWithMatrix<T> (m); }
void verifyProcrustes (const std::vector<Imath::Vec3<T> >& from, const std::vector<Imath::Vec3<T> >& to) { typedef Imath::Vec3<T> V3; const T eps = std::sqrt(std::numeric_limits<T>::epsilon()); const size_t n = from.size(); // Validate that passing in uniform weights gives the same answer as // passing in no weights: std::vector<T> weights (from.size()); for (size_t i = 0; i < weights.size(); ++i) weights[i] = 1; Imath::M44d m1 = procrustesRotationAndTranslation (&from[0], &to[0], n); Imath::M44d m2 = procrustesRotationAndTranslation (&from[0], &to[0], &weights[0], n); for (int i = 0; i < 4; ++i) for (int j = 0; j < 4; ++j) assert (std::abs(m1[i][j] - m2[i][j]) < eps); // Now try the weighted version: for (size_t i = 0; i < weights.size(); ++i) weights[i] = i+1; Imath::M44d m = procrustesRotationAndTranslation (&from[0], &to[0], &weights[0], n); // with scale: Imath::M44d ms = procrustesRotationAndTranslation (&from[0], &to[0], &weights[0], n, true); // Verify that it's orthonormal w/ positive determinant. const T det = m.determinant(); assert (std::abs(det - T(1)) < eps); // Verify orthonormal: Imath::M33d upperLeft; for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) upperLeft[i][j] = m[i][j]; Imath::M33d product = upperLeft * upperLeft.transposed(); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { const double expected = (i == j ? 1.0 : 0.0); assert (std::abs(product[i][j] - expected) < eps); } } // Verify that nearby transforms are worse: const size_t numTries = 10; Imath::Rand48 rand (1056); const double delta = 1e-3; for (size_t i = 0; i < numTries; ++i) { // Construct an orthogonal rotation matrix using Euler angles: Imath::Eulerd diffRot (delta * rand.nextf(), delta * rand.nextf(), delta * rand.nextf()); assert (procrustesError (&from[0], &to[0], &weights[0], n, m * diffRot.toMatrix44()) > procrustesError (&from[0], &to[0], &weights[0], n, m)); // Try a small translation: Imath::V3d diffTrans (delta * rand.nextf(), delta * rand.nextf(), delta * rand.nextf()); Imath::M44d translateMatrix; translateMatrix.translate (diffTrans); assert (procrustesError (&from[0], &to[0], &weights[0], n, m * translateMatrix) > procrustesError (&from[0], &to[0], &weights[0], n, m)); } // Try a small scale: Imath::M44d newMat = ms; const double scaleDiff = delta; for (size_t i = 0; i < 3; ++i) for (size_t j = 0; j < 3; ++j) newMat[i][j] = ms[i][j] * (1.0 + scaleDiff); assert (procrustesError (&from[0], &to[0], &weights[0], n, newMat) > procrustesError (&from[0], &to[0], &weights[0], n, ms)); for (size_t i = 0; i < 3; ++i) for (size_t j = 0; j < 3; ++j) newMat[i][j] = ms[i][j] * (1.0 - scaleDiff); assert (procrustesError (&from[0], &to[0], &weights[0], n, newMat) > procrustesError (&from[0], &to[0], &weights[0], n, ms)); // // Verify the magical property that makes shape springs work: // when the displacements Q*A-B, times the weights, // are applied as forces at B, // there is zero net force and zero net torque. // { Imath::V3d center (0, 0, 0); Imath::V3d netForce(0); Imath::V3d netTorque(0); for (int iPoint = 0; iPoint < n; ++iPoint) { const Imath::V3d force = weights[iPoint] * (from[iPoint]*m - to[iPoint]); netForce += force; netTorque += to[iPoint].cross (force); } assert (netForce.length2() < eps); assert (netTorque.length2() < eps); } }