Material.cpp

Go to the documentation of this file.
00001 /**<!-------------------------------------------------------------------->
00002    @file   Material.cpp
00003    @author Travis Fischer (fisch0920@gmail.com)
00004    @author Matthew Jacobs (jacobs.mh@gmail.com)
00005    @date   Fall 2008
00006    
00007    @brief
00008       Abstract representation of a surface Material, defined without respect 
00009    to the underlying surface (loose coupling between Shapes and Materials 
00010    from the point-of-view of a Material, but all Shapes know about their 
00011    surface Material).  Materials subclass PropertyMap, and it is through this 
00012    interface that Material properties may be set (ex: diffuse color, 
00013    texture/bump/color map(s), index of refraction of interior volume, etc.)
00014    
00015        Reflectivity, emittance, and sensor response (BSDFs, Emitters, and 
00016    Sensors respectively) are three properties of a material that are defined 
00017    at a single point on a surface.  A Material encapsulates BSDF, Emitter, 
00018    and Sensor properties defined over its surface, where specific instances of 
00019    BSDF, Emitter, and Sensor are allowed to have their inputs vary with 
00020    respect to position along the surface.  In this respect, Materials 
00021    represent a mapping from surface position to associated BSDF, Emitter, and 
00022    Sensor functions, where the underlying functions themselves remain constant 
00023    along the surface, and only the inputs vary among the different instances / 
00024    surface points.  For example, a Material may have a DiffuseBSDF over its 
00025    entire surface, but a specific DiffuseBSDF instance obtained by getBSDF or 
00026    implicitly in initSurfacePoint (which fills in the SurfacePoint's BSDF), is 
00027    allowed to have its 'kd' parameter (diffuse albedo) vary with respect to 
00028    the given surface point via lookup in an associated 'kd' texture map 
00029    defined over the UV coordinates of the surface.  
00030    <!-------------------------------------------------------------------->**/
00031 
00032 #include "Material.h"
00033 #include <materials.h>
00034 #include <filters.h>
00035 
00036 #include <ResourceManager.h>
00037 #include <Camera.h>
00038 
00039 #include <GL/gl.h>
00040 #include <QtCore>
00041 
00042 // initialize static members of Material
00043 SurfacePoint Material::s_nullSurfacePoint;
00044 Emitter *Material::s_nullEmitter = new NullEmitter(Material::s_nullSurfacePoint);
00045 Sensor  *Material::s_nullSensor  = new NullSensor (Material::s_nullSurfacePoint);
00046 
00047 Material::~Material() {
00048    safeDelete(m_filter);
00049 }
00050 
00051 void Material::init() {
00052    const std::string &filter = getValue<std::string>("filter", "triangle");
00053    
00054    m_filter  = KernelFilter::create(filter, *this);
00055    
00056    m_bsdf    = getValue<const std::string>("bsdf", m_bsdf);
00057    m_emitter = getValue<const std::string>("emitter", m_emitter);
00058    m_bumpMap = getValue<const std::string>("bumpMap", m_bumpMap);
00059    
00060    m_repeatU       = getValue<real_t>("repeatU", m_repeatU);
00061    m_repeatV       = getValue<real_t>("repeatV", m_repeatV);
00062    m_bumpIntensity = getValue<real_t>("bumpIntensity", m_bumpIntensity); 
00063 }
00064 
00065 BSDF *Material::getBSDF(SurfacePoint &pt) {
00066    BSDF *bsdf = NULL;
00067    
00068    if (m_bsdf == "diffuse") {
00069       bsdf = new DiffuseBSDF(pt, this);
00070    } else if (m_bsdf == "dielectric") {
00071       bsdf = new DielectricBSDF(pt, this);
00072    } else if (m_bsdf == "modifiedPhong" || m_bsdf == "phong") {
00073       bsdf = new ModifiedPhongBSDF(pt, this);
00074    } else if (m_bsdf == "absorbent") {
00075       bsdf = new AbsorbentBSDF(pt, this);
00076    } else if (m_bsdf == "specular") {
00077       (*this)["transparency"] = 0.0;
00078       bsdf = new DielectricBSDF(pt, this);
00079    } else if (m_bsdf == "transmissive") {
00080       (*this)["transparency"] = 1.0;
00081       bsdf = new DielectricBSDF(pt, this);
00082    } else {
00083       cerr << "invalid material (BSDF type): " << m_bsdf << endl;
00084       ASSERT(0 && "Found Invalid Material (BSDF type)");
00085       return NULL;
00086    }
00087    
00088    bsdf->init();
00089    return bsdf;
00090 }
00091 
00092 bool Material::isEmitter() {
00093    return (m_emitter != "null");
00094 }
00095 
00096 Emitter *Material::getEmitter(SurfacePoint &pt) {
00097    Emitter *emitter = NULL;
00098    
00099    // standard area light; emittance distribution restricted by surface normal
00100    if (m_emitter == "oriented") {
00101       emitter = new OrientedEmitter(pt, this);
00102    } else if (m_emitter == "omni" || m_emitter == "point") {
00103       emitter = new OmniEmitter(pt, this);
00104    } else if (m_emitter == "environment") {
00105       emitter = new EnvironmentMap(this);
00106    } else {
00107       ASSERT(m_emitter == "null" && "Found Invalid Material (Emitter type)");
00108       
00109       return Material::s_nullEmitter;
00110    }
00111    
00112    emitter->init();
00113    return emitter;
00114 }
00115 
00116 Emitter *Material::getEmitter() {
00117    return getEmitter(s_nullSurfacePoint);
00118 }
00119 
00120 Sensor *Material::getSensor(SurfacePoint &pt) {
00121    Sensor *sensor = Material::s_nullSensor;
00122    Camera *camera = NULL;
00123    
00124    // TODO: make this not dependent on Camera...
00125    try {
00126       camera = dynamic_cast<Camera*>(pt.shape);
00127    } catch(std::bad_cast&) { }
00128    
00129    if (camera) {
00130       sensor = new Sensor(pt, this);
00131       sensor->init();
00132    }
00133    
00134    return sensor;
00135 }
00136 
00137 void Material::preview(Shape *shape) {
00138    SurfacePoint pt;
00139    pt.shape = shape;
00140    
00141    if (isEmitter()) {
00142       Emitter *emitter = getEmitter(pt);
00143       emitter->preview(shape);
00144       
00145       if (emitter != Material::s_nullEmitter)
00146          safeDelete(emitter);
00147    }
00148    
00149    BSDF *bsdf = getBSDF(pt);
00150    bsdf->preview(shape);
00151    
00152    safeDelete(bsdf);
00153 }
00154 
00155 void Material::initSurfacePoint(SurfacePoint &pt) {
00156    safeDelete(pt.bsdf);
00157    
00158    // TODO: abstract this out into SurfacePoint or Material
00159    if (pt.emitter != Material::s_nullEmitter)
00160       safeDelete(pt.emitter);
00161    if (pt.sensor != Material::s_nullSensor)
00162       safeDelete(pt.sensor);
00163    
00164    _initShadingNormal(pt);
00165    
00166    pt.bsdf    = getBSDF(pt);
00167    pt.emitter = getEmitter(pt);
00168    pt.sensor  = getSensor(pt);
00169    
00170    const SpectralSampleSet &ior = 
00171       getSpectralSampleSet("ior", IndexOfRefraction::AIR, pt);
00172    
00173    const unsigned index = Random::sampleInt(0, ior.getN());
00174    pt.ior2 = ior[index].value;
00175 }
00176 
00177 void Material::_initShadingNormal(SurfacePoint &pt) {
00178    // default to geometric normal if no bumpmap was specified
00179    if (m_bumpMap == "") {
00180       pt.normalS = pt.normalG;
00181       return;
00182    }
00183    
00184    // load or access cached version of bumpmap image
00185    const ImagePtr &image = ResourceManager::getImage(m_bumpMap);
00186    
00187    // if image failed to load successfully, default to geometric normal
00188    if (!image) {
00189       pt.normalS = pt.normalG;
00190       return;
00191    }
00192    
00193    // calculate bumpmap base coordinates
00194    const int width  = image->getWidth();
00195    const int height = image->getHeight();
00196    
00197    const real_t s = pt.uv.u * m_repeatU;
00198    const real_t t = pt.uv.v * m_repeatV;
00199    const int x = (int)(CAP((s - floor(s)) * width,  0, width  - 1));
00200    const int y = (int)(CAP((t - floor(t)) * height, 0, height - 1));
00201    
00202    // compute discrete partial derivatives
00203    const real_t xGrad = 
00204       image->getLuminance(y, x - (x > 0)) - 
00205       image->getLuminance(y, x + (x < width - 1));
00206    const real_t yGrad = 
00207       image->getLuminance(y - (y > 0), x) - 
00208       image->getLuminance(y + (y < height - 1), x);
00209    
00210    if (EQ(xGrad, 0) && EQ(yGrad, 0)) {
00211       pt.normalS = pt.normalG;
00212       return;
00213    }
00214    
00215    // construct orthonormal basis given geometric normal at samplepoint
00216    Vector3 N = pt.normalG;
00217    Vector3 U, V;
00218    
00219    N.getOrthonormalBasis(U, V);
00220    
00221    // scaleFactor modifies the intensity or magnitude of the bumps
00222    pt.normalS = (N + m_bumpIntensity * (xGrad * U + yGrad * V)).getNormalized();
00223 }
00224 
00225 SpectralSampleSet Material::getSpectralSampleSet(const std::string &key, 
00226                                                  const SpectralSampleSet &defaultValue, 
00227                                                  const SurfacePoint &pt)
00228 {
00229    if (!contains(key))
00230       return boost::any_cast<const SpectralSampleSet&>((*this)[key] = defaultValue);
00231    
00232    return getSpectralSampleSet(key, pt);
00233 }
00234 
00235 SpectralSampleSet Material::getSpectralSampleSet(const std::string &key, 
00236                                                  const real_t &defaultValue, 
00237                                                  const SurfacePoint &pt)
00238 {
00239    if (!contains(key)) {
00240       return boost::any_cast<const SpectralSampleSet&>((*this)[key] = 
00241                                                        SpectralSampleSet::fill(defaultValue));
00242    }
00243    
00244    return getSpectralSampleSet(key, pt);
00245 }
00246 
00247 SpectralSampleSet Material::getSpectralSampleSet(const std::string &key, 
00248                                                  const std::string &defaultValue, 
00249                                                  const SurfacePoint &pt)
00250 {
00251    if (!contains(key))
00252       (*this)[key] = defaultValue;
00253    
00254    return getSpectralSampleSet(key, pt);
00255 }
00256 
00257 SpectralSampleSet Material::getSpectralSampleSet(const std::string &key, 
00258                                                  const SurfacePoint &pt)
00259 {
00260    const boost::any &value = (*this)[key];
00261    
00262    if (value.type() == typeid(SpectralSampleSet)) {
00263       return boost::any_cast<SpectralSampleSet>(value);
00264    } else if (value.type() == typeid(std::string)) {
00265       const std::string &fileName = boost::any_cast<std::string>(value);
00266       const ImagePtr    &image    = ResourceManager::getImage(fileName);
00267       
00268       if (image)
00269          return SpectralSampleSet(getSample(image, pt.uv));
00270    } else if (value.type() == typeid(real_t)) {
00271       return SpectralSampleSet::fill(boost::any_cast<real_t>(value));
00272    } else {
00273       ASSERT(0 && "invalid spectrum type");
00274    }
00275    
00276    return SpectralSampleSet::fill(0.7);
00277 }
00278 
00279 RgbaHDR Material::getSample(const ImagePtr &image, const UV &uv)
00280 {
00281    ASSERT(image);
00282    
00283    if (NULL == m_filter) {
00284       init();
00285       
00286       ASSERT(m_filter);
00287    }
00288    
00289    const unsigned width  = image->getWidth();
00290    const unsigned height = image->getHeight();
00291    
00292    const real_t s = uv.u * m_repeatU;
00293    const real_t t = uv.v * m_repeatV;
00294    const real_t x = CAP((s - floor(s)) * width,  0, width  - 1);
00295    const real_t y = CAP((t - floor(t)) * height, 0, height - 1);
00296    
00297    return m_filter->apply(image.get(), x, y);
00298 }
00299 

Generated on 28 Feb 2009 for Milton by doxygen 1.5.6