00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "sphericalfunction.h"
00024 #include "sphericalfunction_ies.h"
00025 #include "mipmap.h"
00026 #include "mcdistribution.h"
00027 #include "paramset.h"
00028 #include "imagereader.h"
00029
00030 namespace lux {
00031
00032
00033
00034 MipMapSphericalFunction::MipMapSphericalFunction() {
00035 }
00036
00037 MipMapSphericalFunction::MipMapSphericalFunction( boost::shared_ptr< const MIPMap<RGBColor> > aMipMap, bool flipZ ) {
00038 SetMipMap( aMipMap );
00039 }
00040
00041 RGBColor MipMapSphericalFunction::f(float phi, float theta) const {
00042 return mipMap->Lookup( phi * INV_TWOPI, theta * INV_PI );
00043 }
00044
00045
00046
00047 SampleableSphericalFunction::SampleableSphericalFunction(
00048 boost::shared_ptr<const SphericalFunction> aFunc, int xRes, int yRes)
00049 {
00050 func = aFunc;
00051 nVDistribs = xRes;
00052
00053
00054 float *img = new float[xRes*yRes];
00055 for (int x = 0; x < xRes; ++x) {
00056 float xp = (float)(x + .5f) / (float)xRes;
00057 for (int y = 0; y < yRes; ++y) {
00058 float yp = (float)(y + .5f) / (float)yRes;
00059 img[y+x*yRes] = func->f(xp * 2.f * M_PI, yp * M_PI).Y();
00060 }
00061 }
00062
00063 int nu = xRes, nv = yRes;
00064 float *func = (float *)alloca(max(nu, nv) * sizeof(float));
00065 float *sinVals = (float *)alloca(nv * sizeof(float));
00066 for (int i = 0; i < nv; ++i)
00067 sinVals[i] = sin(M_PI * float(i + .5f)/float(nv));
00068 vDistribs = new Distribution1D *[nu];
00069 for (int u = 0; u < nu; ++u) {
00070
00071 for (int v = 0; v < nv; ++v)
00072 func[v] = img[u*nv+v] *= sinVals[v];
00073 vDistribs[u] = new Distribution1D(func, nv);
00074 }
00075
00076 for (int u = 0; u < nu; ++u)
00077 func[u] = vDistribs[u]->funcInt;
00078 uDistrib = new Distribution1D(func, nu);
00079 delete[] img;
00080 }
00081 SampleableSphericalFunction::~SampleableSphericalFunction() {
00082 delete uDistrib;
00083 for(int i = 0; i < nVDistribs; i++) {
00084 delete vDistribs[i];
00085 }
00086 delete[] vDistribs;
00087 }
00088
00089 RGBColor SampleableSphericalFunction::f(float phi, float theta) const {
00090 return func->f(phi, theta);
00091 }
00092
00093 RGBColor SampleableSphericalFunction::Sample_f(float u1, float u2, Vector *w, float *pdf) const {
00094
00095 float pdfs[2];
00096 float fu = uDistrib->Sample(u1, &pdfs[0]);
00097 int u = Float2Int(fu);
00098 float fv = vDistribs[u]->Sample(u2, &pdfs[1]);
00099
00100 float theta = fv * vDistribs[u]->invCount * M_PI;
00101 float phi = fu * uDistrib->invCount * 2.f * M_PI;
00102 float costheta = cos(theta), sintheta = sin(theta);
00103 float sinphi = sin(phi), cosphi = cos(phi);
00104 *w = Vector(sintheta * cosphi, sintheta * sinphi, costheta);
00105
00106 *pdf = (pdfs[0] * pdfs[1]) / (2.f * M_PI * M_PI * sintheta);
00107
00108 return f(phi, theta);
00109 }
00110
00111 float SampleableSphericalFunction::Pdf(const Vector& w) const {
00112 float theta = SphericalTheta(w), phi = SphericalPhi(w);
00113 int u = Clamp(Float2Int(phi * INV_TWOPI * uDistrib->count),
00114 0, uDistrib->count-1);
00115 int v = Clamp(Float2Int(theta * INV_PI * vDistribs[u]->count),
00116 0, vDistribs[u]->count-1);
00117 return (uDistrib->func[u] * vDistribs[u]->func[v]) /
00118 (uDistrib->funcInt * vDistribs[u]->funcInt) *
00119 1.f / (2.f * M_PI * M_PI * sin(theta));
00120 }
00121
00122 float SampleableSphericalFunction::Average_f() const {
00123 return uDistrib->funcInt;
00124 }
00125
00126 SphericalFunction *CreateSphericalFunction(const ParamSet ¶mSet, const TextureParams &tp) {
00127 bool flipZ = paramSet.FindOneBool("flipz", false);
00128 string texname = paramSet.FindOneString("mapname", "");
00129 string iesname = paramSet.FindOneString("iesname", "");
00130
00131
00132 SphericalFunction *mipmapFunc = NULL;
00133 if( texname.length() > 0 ) {
00134 auto_ptr<ImageData> imgdata(ReadImage(texname));
00135 if (imgdata.get()!=NULL) {
00136 mipmapFunc = new MipMapSphericalFunction(
00137 boost::shared_ptr< MIPMap<RGBColor> >(imgdata->createMIPMap<RGBColor>()), flipZ
00138 );
00139 }
00140 }
00141
00142 SphericalFunction *iesFunc = NULL;
00143 if( iesname.length() > 0 ) {
00144 PhotometricDataIES data(iesname.c_str());
00145 if( data.IsValid() ) {
00146 iesFunc = new IESSphericalFunction( data, flipZ );
00147 }
00148 else {
00149 stringstream ss;
00150 ss << "Invalid IES file: " << iesname;
00151 luxError( LUX_BADFILE, LUX_WARNING, ss.str().c_str() );
00152 }
00153 }
00154
00155 if( !iesFunc && !mipmapFunc )
00156 return NULL;
00157 else if( !iesFunc )
00158 return mipmapFunc;
00159 else if( !mipmapFunc )
00160 return iesFunc;
00161 else {
00162 CompositeSphericalFunction *compositeFunc = new CompositeSphericalFunction();
00163 compositeFunc->Add(
00164 boost::shared_ptr<const SphericalFunction>(mipmapFunc) );
00165 compositeFunc->Add(
00166 boost::shared_ptr<const SphericalFunction>(iesFunc) );
00167 return compositeFunc;
00168 }
00169 }
00170
00171 }