ModifiedPhongBSDF.cpp

Go to the documentation of this file.
00001 /**<!-------------------------------------------------------------------->
00002    @file   ModifiedPhongBSDF.cpp
00003    @author Travis Fischer (fisch0920@gmail.com)
00004    @author Matthew Jacobs (jacobs.mh@gmail.com)
00005    @date   Fall 2008
00006    
00007    @brief
00008       Physically-correct modified phong model for glossy/specular surfaces, 
00009    defined at a single point on a surface in 3-space. The modified phong 
00010    model has two inputs: Kd, and Ks in [0, 1] subject to Kd + Ks <= 1, 
00011    where Kd and Ks represent the diffuse and specular reflectivity of the 
00012    surface respectively (fraction of incoming energy that is reflected 
00013    diffusely/specularly). A third input, n, represents the specular 
00014    shininess of the surface, where higher values of n correspond to tighter /
00015    sharper specular highlights, and lower values of n correspond to wider / 
00016    glossier highlights.
00017    
00018    @note For more information, please see: 
00019       E. Lafortune and Y. Willems. Using the modified Phong reflectance 
00020    model for physically based rendering. Technical Report CW197, Dept of 
00021    Computer Science, K.U. Leuven, 1994.
00022    
00023    @param
00024       kd - SpectralSampleSet which scales / attenuates the overall reflectance 
00025    of the material (diffuse albedo)
00026    
00027    @param
00028       ks - SpectralSampleSet which scales / attenuates the overall reflectance 
00029    of the material (specular albedo)
00030    
00031    @param
00032       n  - Wavelength-dependent shininess exponent
00033    
00034    @note Also known as modified Blinn-Phong model
00035    <!-------------------------------------------------------------------->**/
00036 
00037 #include "ModifiedPhongBSDF.h"
00038 #include <Material.h>
00039 #include <Random.h>
00040 
00041 enum {
00042    MODIFIED_PHONG_EVENT_ABSORPTION = 0, 
00043    MODIFIED_PHONG_EVENT_DIFFUSE, 
00044    MODIFIED_PHONG_EVENT_SPECULAR, 
00045 };
00046 
00047 void ModifiedPhongBSDF::init() {
00048    BSDF::init();
00049    
00050    if (m_parent->contains("ks")) {
00051       m_ks = m_parent->getSpectralSampleSet("ks", m_pt);
00052       m_kd = m_parent->getSpectralSampleSet("kd", SpectralSampleSet::black(), m_pt);
00053    } else if (m_parent->contains("kd")) {
00054       m_kd = m_parent->getSpectralSampleSet("kd", m_pt);
00055       m_ks = m_parent->getSpectralSampleSet("ks", SpectralSampleSet::black(), m_pt);
00056    } else {
00057       m_kd = m_parent->getSpectralSampleSet("kd", SpectralSampleSet::fill(.5), m_pt);
00058       m_ks = m_parent->getSpectralSampleSet("ks", SpectralSampleSet::fill(.5), m_pt);
00059    }
00060    
00061       /*unsigned k = m_ks.getMaxFrequency();
00062       if (m_ks[k] > 0) {
00063          real_t ratio = (1 - m_ks[k]) / m_ks[k];
00064          
00065          for(unsigned i = m_kd.N; i--;)
00066             m_kd[i] = m_ks[i] * ratio;
00067       }*/
00068    //m_ks = SpectralSampleSet::black(); // TODO: temporary
00069    //m_kd = SpectralSampleSet::black(); // TODO: temporary
00070    //cerr << "temporary\n\n" << endl;
00071    
00072    m_n = m_parent->getSpectralSampleSet("n" , SpectralSampleSet::fill(1.0), m_pt);
00073    
00074    ASSERT(m_kd + m_ks <= SpectralSampleSet::identity());
00075    ASSERT(m_n >= SpectralSampleSet::black());
00076    
00077    m_kda = m_kd.getAverage();
00078    m_ksa = m_ks.getAverage();
00079    m_na  = m_n.getAverage();
00080 }
00081 
00082 Event ModifiedPhongBSDF::sample() {
00083    ASSERT(m_n >= SpectralSampleSet::fill(0));
00084    
00085    const real_t p0 = Random::sample() - EPSILON;
00086    unsigned index  = MODIFIED_PHONG_EVENT_ABSORPTION;
00087    
00088    if (p0 <= m_kda) {               // diffuse reflection
00089       index = MODIFIED_PHONG_EVENT_DIFFUSE;
00090       
00091       const Vector3 &wo = Vector3::cosRandom(m_pt.normalS);
00092       
00093       return Event(wo, this, index);
00094    } else if (p0 <= m_kda + m_ksa) { // specular reflection
00095       index = MODIFIED_PHONG_EVENT_SPECULAR;
00096       
00097       const real_t alpha = acos(pow(Random::sample(), 
00098                                     1.0 / (m_na + 1)));
00099       const real_t phi   = 2.0 * M_PI * Random::sample();
00100       
00101       // perfect specular direction
00102       const Vector3 &R = m_wi.reflectVector(m_pt.normalS);
00103       Vector3 r = R;
00104       Vector3 u, v;
00105       
00106       if (ABS(m_pt.normal.dot(r)) < .95) {
00107          // construct orthonormal basis with r and v in the plane shared by 
00108          // r and the local surface normal
00109          u = r.cross(m_pt.normalS).getNormalized();
00110          v = u.cross(r);
00111       } else { // r approximately collinear with normal
00112          r.getOrthonormalBasis(v, u);
00113       }
00114       
00115       const Vector3 &w  = Vector3::fromSpherical(alpha, phi);
00116       const Vector3 &wo = u * w[0] + r * w[1] + v * w[2];
00117       
00118       if (m_pt.normal.dot(wo) > 0)
00119          return Event(wo, this, index);
00120       
00121       // randomly sampled vector on opposite side of hemisphere
00122       return Event(R, this, index);
00123    }
00124    
00125    // absorbed
00126    return Event(Vector3(), this, index);
00127 }
00128 
00129 real_t ModifiedPhongBSDF::getPdf(const Event &event) {
00130    const unsigned index = event.getMetadata<unsigned>();
00131    real_t pdf = 1;
00132    
00133    if (index == MODIFIED_PHONG_EVENT_ABSORPTION) {
00134       pdf = (1 - (m_kda + m_ksa));
00135       ASSERT(pdf > 0);
00136       
00137       return pdf;
00138    } else {
00139       const Vector3 &wo = event.getValue<const Vector3&>();
00140       const real_t cosA = m_pt.normal.dot(wo);
00141       if (cosA <= 0)
00142          return 0;
00143       
00144       if (index == MODIFIED_PHONG_EVENT_SPECULAR) { // specular reflection
00145          const Vector3 &r  = m_wi.reflectVector(m_pt.normalS);
00146          const real_t cosA = ABS(r.dot(wo));
00147          ASSERT(m_ksa > 0);
00148          ASSERT(r.isUnit());
00149          
00150          pdf = MIN(1, (m_na + 1) / (2 * M_PI) * pow(cosA, m_na));
00151       } else {        // diffuse reflection
00152          ASSERT(index == MODIFIED_PHONG_EVENT_DIFFUSE);
00153          ASSERT(m_kda > 0);
00154          
00155          pdf = (ABS(m_pt.normal.dot(wo)) / M_PI);
00156          ASSERT(pdf > 0);
00157       }
00158       
00159       return pdf / cosA;
00160    }
00161 }
00162 
00163 SpectralSampleSet ModifiedPhongBSDF::getBSDF(const Vector3 &wi, const Vector3 &wo) {
00164    if (m_pt.normal.dot(wo) <= 0)
00165       return SpectralSampleSet::black();
00166    
00167    const Vector3 &r = wi.reflectVector(m_pt.normalS);
00168    const real_t   a = CAP(r.dot(wo), 0, 1);
00169    SpectralSampleSet a_n(m_n);
00170    
00171    for(unsigned i = m_n.getN(); i--;)
00172       a_n[i].value = pow(a, m_n[i].value);
00173    
00174    const SpectralSampleSet &fs_s = 
00175       (m_ksa > 0 ? ((m_n + SpectralSampleSet::fill(2)) / (2 * M_PI * m_ksa)) * a_n : 
00176        SpectralSampleSet::black());
00177    const real_t fs_d = (m_kda > 0 ? 1.0 / (M_PI * m_kda) : 0);
00178    
00179    return m_kd * fs_d + m_ks * fs_s;
00180 }
00181 

Generated on 28 Feb 2009 for Milton by doxygen 1.5.6