00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 #include "stdafx.h"
00027 #include "shapes/disk.h"
00028 #include "paramset.h"
00029 #include "montecarlo.h"
00030 
00031 
00032 Disk::Disk(const Transform *o2w, const Transform *w2o, bool ro,
00033            float ht, float r, float ri, float tmax)
00034     : Shape(o2w, w2o, ro) {
00035     height = ht;
00036     radius = r;
00037     innerRadius = ri;
00038     phiMax = Radians(Clamp(tmax, 0.0f, 360.0f));
00039 }
00040 
00041 
00042 BBox Disk::ObjectBound() const {
00043     return BBox(Point(-radius, -radius, height),
00044                 Point( radius,  radius, height));
00045 }
00046 
00047 
00048 bool Disk::Intersect(const Ray &r, float *tHit, float *rayEpsilon,
00049                      DifferentialGeometry *dg) const {
00050     
00051     Ray ray;
00052     (*WorldToObject)(r, &ray);
00053 
00054     
00055     if (fabsf(ray.d.z) < 1e-7) return false;
00056     float thit = (height - ray.o.z) / ray.d.z;
00057     if (thit < ray.mint || thit > ray.maxt)
00058         return false;
00059 
00060     
00061     Point phit = ray(thit);
00062     float dist2 = phit.x * phit.x + phit.y * phit.y;
00063     if (dist2 > radius * radius || dist2 < innerRadius * innerRadius)
00064         return false;
00065 
00066     
00067     float phi = atan2f(phit.y, phit.x);
00068     if (phi < 0) phi += 2. * M_PI;
00069     if (phi > phiMax)
00070         return false;
00071 
00072     
00073     float u = phi / phiMax;
00074     float v = 1.f - ((sqrtf(dist2)-innerRadius) /
00075                      (radius-innerRadius));
00076     Vector dpdu(-phiMax * phit.y, phiMax * phit.x, 0.);
00077     Vector dpdv(-phit.x / (1-v), -phit.y / (1-v), 0.);
00078     dpdu *= phiMax * INV_TWOPI;
00079     dpdv *= (radius - innerRadius) / radius;
00080     Normal dndu(0,0,0), dndv(0,0,0);
00081 
00082     
00083     const Transform &o2w = *ObjectToWorld;
00084     *dg = DifferentialGeometry(o2w(phit), o2w(dpdu), o2w(dpdv),
00085                                o2w(dndu), o2w(dndv), u, v, this);
00086 
00087     
00088     *tHit = thit;
00089 
00090     
00091     *rayEpsilon = 5e-4f * *tHit;
00092     return true;
00093 }
00094 
00095 
00096 bool Disk::IntersectP(const Ray &r) const {
00097     
00098     Ray ray;
00099     (*WorldToObject)(r, &ray);
00100 
00101     
00102     if (fabsf(ray.d.z) < 1e-7) return false;
00103     float thit = (height - ray.o.z) / ray.d.z;
00104     if (thit < ray.mint || thit > ray.maxt)
00105         return false;
00106 
00107     
00108     Point phit = ray(thit);
00109     float dist2 = phit.x * phit.x + phit.y * phit.y;
00110     if (dist2 > radius * radius || dist2 < innerRadius * innerRadius)
00111         return false;
00112 
00113     
00114     float phi = atan2f(phit.y, phit.x);
00115     if (phi < 0) phi += 2. * M_PI;
00116     if (phi > phiMax)
00117         return false;
00118     return true;
00119 }
00120 
00121 
00122 float Disk::Area() const {
00123     return phiMax * 0.5f *
00124        (radius * radius - innerRadius * innerRadius);
00125 }
00126 
00127 
00128 Disk *CreateDiskShape(const Transform *o2w, const Transform *w2o,
00129         bool reverseOrientation, const ParamSet ¶ms) {
00130     float height = params.FindOneFloat("height", 0.);
00131     float radius = params.FindOneFloat("radius", 1);
00132     float inner_radius = params.FindOneFloat("innerradius", 0);
00133     float phimax = params.FindOneFloat("phimax", 360);
00134     return new Disk(o2w, w2o, reverseOrientation, height, radius, inner_radius, phimax);
00135 }
00136 
00137 
00138 Point Disk::Sample(float u1, float u2, Normal *Ns) const {
00139     Point p;
00140     ConcentricSampleDisk(u1, u2, &p.x, &p.y);
00141     p.x *= radius;
00142     p.y *= radius;
00143     p.z = height;
00144     *Ns = Normalize((*ObjectToWorld)(Normal(0,0,1)));
00145     if (ReverseOrientation) *Ns *= -1.f;
00146     return (*ObjectToWorld)(p);
00147 }
00148 
00149