Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

sphere.cpp

Go to the documentation of this file.
00001 /*
00002     toxic - A Global Illumination Renderer
00003     Copyright (C) 2003-2004 Francois Beaune
00004     Contact: http://toxicengine.sourceforge.net/
00005 
00006     This file is part of toxic.
00007 
00008     toxic is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     toxic is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with toxic; if not, write to the Free Software
00020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021 */
00022 
00023 #include "sphere.h" // include first
00024 #include "common/math/real.h"
00025 #include "common/math/sampling.h"
00026 #include "context.h"
00027 #include "hit.h"
00028 #include "iedf.h"
00029 #include "isurfaceshader.h"
00030 #include "ray.h"
00031 #include "scene.h"
00032 #include "settings.h"
00033 #include "statistics.h"
00034 #include "surfacebasis.h"
00035 #include "utilities.h"
00036 
00037 #include <cassert>
00038 #include <cmath>
00039 
00040 using namespace sheep;
00041 using namespace std;
00042 using namespace toxic;
00043 
00044 Sphere::Sphere(const Matrix4 &m,
00045                const ISurfaceShader *surface_shader,
00046                IntersectionMask intersection_mask /*= INTERSECT_ALL_RAYS*/) :
00047     IAreaLight(m, surface_shader, intersection_mask),
00048     m_center(TransformToWorld(Point3(0.0))),
00049     m_ex(TransformToWorld(Vector3(1.0, 0.0, 0.0))),
00050     m_ey(TransformToWorld(Vector3(0.0, 1.0, 0.0))),
00051     m_ez(TransformToWorld(Vector3(0.0, 0.0, 1.0))),
00052     m_bs(m_center)
00053 {
00054     m_aabb = AABB3(Point3(-1.0), Point3(1.0)).Transform(m);
00055 
00056     // Enlarge the object bounding box to avoid numerical instabilities.
00057     m_aabb.Extend(OBJECT_AABB_EXTENSION);
00058 
00059     m_bs.Include(TransformToWorld(Point3(1.0, 0.0, 0.0)));
00060     m_bs.Include(TransformToWorld(Point3(-1.0, 0.0, 0.0)));
00061     m_bs.Include(TransformToWorld(Point3(0.0, 1.0, 0.0)));
00062     m_bs.Include(TransformToWorld(Point3(0.0, -1.0, 0.0)));
00063     m_bs.Include(TransformToWorld(Point3(0.0, 0.0, 1.0)));
00064     m_bs.Include(TransformToWorld(Point3(0.0, 0.0, -1.0)));
00065 
00066     // Compute the square of the radius of the bounding sphere
00067     // (required by the ComputeIrradiance() method).
00068     m_bs.ComputeSquareRadius();
00069 }
00070 
00071 Real Sphere::ComputeSurfaceArea() const {
00072     const Real r = m_ex.Norm();
00073 
00074     if(!(r == m_ey.Norm() && r == m_ez.Norm())) {
00075         assert(!"Ellipsoid surface area computation not implemented yet.");
00076         //!\todo Implement.
00077         //! Ellipsoid Surface Area: http://documents.wolfram.com/v4/MainBook/G.1.7.html
00078         //! Source Code: http://www.tcs.auckland.ac.nz/dgt/Source_Code.php?id=2
00079     }
00080 
00081     return 4.0 * PI * r * r;
00082 }
00083 
00084 void Sphere::EvaluateSurface(const Point2 &input,
00085                              Point3 *point,
00086                              Vector3 *geometric_normal) const
00087 {
00088     assert(point);
00089     assert(geometric_normal);
00090 
00091     const Vector3 direction = UniformSphereSampling(input.m_x, input.m_y);
00092 
00093     *point =
00094         m_center +
00095         m_ex * direction.m_x +
00096         m_ey * direction.m_y +
00097         m_ez * direction.m_z;
00098 
00099     *geometric_normal = TransformNormalToWorld(direction);
00100 }
00101 
00102 bool Sphere::Intersect(const Context &context,
00103                        const Ray &ray,  //!< 'ray' is expressed in world space.
00104                        Hit *hit /*= 0*/) const
00105 {
00106     // Test whether this object intersects with this type of ray.
00107     if(!(m_intersection_mask & ray.GetType()))
00108         return false;
00109 
00110     ++context.m_statistics->m_tested_intersections;
00111 
00112     const Ray r = TransformToLocal(ray);
00113     const Real inv_scale2 = 1.0 / r.m_direction.SquareNorm();
00114 
00115     const Real a = r.m_origin * r.m_direction;
00116     const Real b = r.m_origin * r.m_origin - 1.0;
00117 
00118     const Real delta = a * a * inv_scale2 - b;
00119 
00120     if(delta < 0.0)
00121         return false;   // the ray does not intersect the sphere
00122 
00123     const Real inv_scale = sqrt(inv_scale2);
00124     const Real c = a * inv_scale;
00125 
00126     const Real sqrt_delta = sqrt(delta);
00127 
00128     Real t = -c - sqrt_delta;
00129 
00130     if(t < 0.0) {
00131         t = -c + sqrt_delta;
00132 
00133         if(t < 0.0)
00134             return false;   // the intersection point is not on the ray
00135     }
00136 
00137     // The ray does intersect the sphere.
00138     ++context.m_statistics->m_intersections_found;
00139 
00140     if(hit) {
00141         t *= inv_scale;
00142 
00143         hit->m_object = this;
00144         hit->m_abscissa = t;    // world space
00145         //!\todo Not sure: should t in r.GetPointAt(t) be in object space or in world space?
00146         hit->m_geometric_normal = r.GetPointAt(t);          // object space
00147         hit->m_shading_normal = hit->m_geometric_normal;    // object space
00148 
00149         // Normals will be made unit-length later, when absolutely necessary.
00150     }
00151 
00152     return true;
00153 }
00154 
00155 void Sphere::ComputeIrradiance(const Context &context,
00156                                const Scene *scene,
00157                                const Point3 &point,
00158                                const Vector3 &geometric_normal,
00159                                const Vector3 &shading_normal,
00160                                const ISurfaceSampler::SampleVector &input,
00161                                IrradianceSampleVector *output) const
00162 {
00163     assert(scene);
00164     assert(geometric_normal.IsUnitLength());
00165     assert(shading_normal.IsUnitLength());
00166     assert(output);
00167 
00168     const Point3 shifted_point = point + 1.0e-8 * geometric_normal;
00169 
00170     const Vector3 psi(m_bs.m_center - point);
00171     const Real psi_inv_norm2 = 1.0 / psi.SquareNorm();
00172     const Real cos_theta_max = sqrt(1.0 - m_bs.m_radius2 * psi_inv_norm2);
00173     const Real k = 1.0 - cos_theta_max;
00174     const Vector3 unit_psi(psi * sqrt(psi_inv_norm2));
00175     const Real sphere_solid_angle = 2.0 * PI * k;
00176 
00177     // Build an orthonormal basis around the Psi vector.
00178     const SurfaceBasis surfacebasis(unit_psi);
00179 
00180     for(ISurfaceSampler::SampleVector::const_iterator i = input.begin(), e = input.end(); i != e; ++i) {
00181         //!\todo Optimize (inline and simplify expressions).
00182         const Real phi = 2.0 * PI * i->m_x;
00183         const Real theta = acos(1.0 - i->m_y * k);
00184         const Vector3 direction = SphericalCoordsToVector(phi, theta);
00185 
00186         const Vector3 incoming = surfacebasis.TransformToWorld(direction);
00187         assert(incoming.IsUnitLength());
00188 
00189         Hit hit;
00190 
00191         // Make sure that this sample really is on the emitter. It is always the
00192         // case for perfectly spherical light sources, but it might not be the case
00193         // for ellipsoid light sources, because we sample the cone sustained by
00194         // the bounding sphere of the light source.
00195         if(!Intersect(context, Ray(Ray::VISIBILITY_RAY, point, incoming), &hit))
00196             continue;   // this sample is not on the emitter: next sample
00197 
00198         const Real distance_to_emitter = hit.m_abscissa;
00199 
00200         if(m_cast_shadows) {
00201             ++context.m_statistics->m_shadow_rays;
00202 
00203             const Ray ray(Ray::SHADOW_RAY, shifted_point, incoming);
00204 
00205             if(scene->Trace(context, ray, &hit)) {
00206                 const bool light_source_visible =
00207                     hit.m_object == this ||
00208                     hit.m_abscissa > distance_to_emitter;
00209 
00210                 if(!light_source_visible) {
00211                     output->push_back(IrradianceSample(Color3(0.0), incoming));
00212                     continue;   // this light is occluded from this direction: next sample
00213                 }
00214             }
00215         }
00216 
00217         //!\todo Flip the normal? Call Hit::ExtractIntersection()?
00218         const Vector3 sample_geometric_normal =
00219             hit.m_object->TransformNormalToWorld(hit.m_geometric_normal);
00220         assert(sample_geometric_normal.IsUnitLength());
00221 
00222         //!\todo Not sure which normal to use.
00223         const Real cos_theta = shading_normal * incoming;
00224 
00225         if(cos_theta <= 0.0) {
00226             // The surface is not directed toward the light: next sample.
00227             output->push_back(IrradianceSample(Color3(0.0), incoming));
00228             continue;
00229         }
00230 
00231         const Real emitted_radiance = GetSurfaceShader()->GetEDF()->GetEmittedRadiance(
00232             context,
00233             sample_geometric_normal,
00234             -incoming);
00235 
00236         assert(emitted_radiance >= 0.0);
00237 
00238         if(emitted_radiance <= 0.0) {
00239             // The light surface element is not directed toward the point: next sample.
00240             output->push_back(IrradianceSample(Color3(0.0), incoming));
00241             continue;
00242         }
00243 
00244         const Color3 irradiance = (sphere_solid_angle * cos_theta * emitted_radiance) *
00245             m_surface_shader->GetRadiantExitance();
00246 
00247         output->push_back(IrradianceSample(irradiance, incoming));
00248     }
00249 }

Generated on Tue May 11 01:31:52 2004 for toxic by doxygen 1.3.6