00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "sphere.h"
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 ) :
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
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
00067
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
00077
00078
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,
00104 Hit *hit ) const
00105 {
00106
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;
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;
00135 }
00136
00137
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;
00145
00146 hit->m_geometric_normal = r.GetPointAt(t);
00147 hit->m_shading_normal = hit->m_geometric_normal;
00148
00149
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
00178 const SurfaceBasis surfacebasis(unit_psi);
00179
00180 for(ISurfaceSampler::SampleVector::const_iterator i = input.begin(), e = input.end(); i != e; ++i) {
00181
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
00192
00193
00194
00195 if(!Intersect(context, Ray(Ray::VISIBILITY_RAY, point, incoming), &hit))
00196 continue;
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;
00213 }
00214 }
00215 }
00216
00217
00218 const Vector3 sample_geometric_normal =
00219 hit.m_object->TransformNormalToWorld(hit.m_geometric_normal);
00220 assert(sample_geometric_normal.IsUnitLength());
00221
00222
00223 const Real cos_theta = shading_normal * incoming;
00224
00225 if(cos_theta <= 0.0) {
00226
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
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 }