00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <cstdio>
00023
00024 #include "luxrays/core/pixeldevice.h"
00025 #include "luxrays/core/context.h"
00026
00027 using namespace luxrays;
00028
00029
00030
00031
00032
00033 size_t NativePixelDevice::SampleBufferSize = 4096;
00034
00035 NativePixelDevice::NativePixelDevice(const Context *context,
00036 const unsigned int threadIndex, const unsigned int devIndex) :
00037 PixelDevice(context, DEVICE_TYPE_NATIVE_THREAD, devIndex) {
00038 char buf[64];
00039 sprintf(buf, "NativePixelThread-%03d", (int)threadIndex);
00040 deviceName = std::string(buf);
00041
00042 sampleFrameBuffer = NULL;
00043 frameBuffer = NULL;
00044
00045 SetGamma();
00046
00047 sampleBuffers.resize(0);
00048 freeSampleBuffers.resize(0);
00049
00050
00051 filter = new GaussianFilter(1.5f, 1.5f, 2.f);
00052 filterLUTs = new FilterLUTs(*filter, 4);
00053 }
00054
00055 NativePixelDevice::~NativePixelDevice() {
00056 if (started)
00057 PixelDevice::Stop();
00058
00059 delete sampleFrameBuffer;
00060 delete frameBuffer;
00061
00062 for (size_t i = 0; i < sampleBuffers.size(); ++i)
00063 delete sampleBuffers[i];
00064
00065 delete filterLUTs;
00066 delete filter;
00067 }
00068
00069 void NativePixelDevice::Init(const unsigned int w, const unsigned int h) {
00070 PixelDevice::Init(w, h);
00071
00072 delete sampleFrameBuffer;
00073 delete frameBuffer;
00074
00075 sampleFrameBuffer = new SampleFrameBuffer(width, height);
00076 sampleFrameBuffer->Clear();
00077
00078 frameBuffer = new FrameBuffer(width, height);
00079 frameBuffer->Clear();
00080 }
00081
00082 void NativePixelDevice::ClearSampleFrameBuffer() {
00083 sampleFrameBuffer->Clear();
00084 }
00085
00086 void NativePixelDevice::ClearFrameBuffer() {
00087 frameBuffer->Clear();
00088 }
00089
00090 void NativePixelDevice::SetGamma(const float gamma) {
00091 float x = 0.f;
00092 const float dx = 1.f / GammaTableSize;
00093 for (unsigned int i = 0; i < GammaTableSize; ++i, x += dx)
00094 gammaTable[i] = powf(Clamp(x, 0.f, 1.f), 1.f / gamma);
00095 }
00096
00097 void NativePixelDevice::Start() {
00098 boost::mutex::scoped_lock lock(splatMutex);
00099 PixelDevice::Start();
00100 }
00101
00102 void NativePixelDevice::Interrupt() {
00103 boost::mutex::scoped_lock lock(splatMutex);
00104 assert (started);
00105 }
00106
00107 void NativePixelDevice::Stop() {
00108 boost::mutex::scoped_lock lock(splatMutex);
00109 PixelDevice::Stop();
00110 }
00111
00112 SampleBuffer *NativePixelDevice::GetFreeSampleBuffer() {
00113 boost::mutex::scoped_lock lock(splatMutex);
00114
00115
00116 if (freeSampleBuffers.size() > 0) {
00117 SampleBuffer *sb = freeSampleBuffers.front();
00118 freeSampleBuffers.pop_front();
00119
00120 sb->Reset();
00121 return sb;
00122 } else {
00123
00124 SampleBuffer *sb = new SampleBuffer(SampleBufferSize);
00125
00126 sampleBuffers.push_back(sb);
00127
00128 return sb;
00129 }
00130 }
00131
00132 void NativePixelDevice::FreeSampleBuffer(SampleBuffer *sampleBuffer) {
00133 boost::mutex::scoped_lock lock(splatMutex);
00134
00135 freeSampleBuffers.push_back(sampleBuffer);
00136 }
00137
00138 void NativePixelDevice::SplatPreview(const SampleBufferElem *sampleElem) {
00139 const int splatSize = 4;
00140
00141
00142 float dImageX = sampleElem->screenX - 0.5f;
00143 float dImageY = sampleElem->screenY - 0.5f;
00144 int x0 = Ceil2Int(dImageX - splatSize);
00145 int x1 = Floor2Int(dImageX + splatSize);
00146 int y0 = Ceil2Int(dImageY - splatSize);
00147 int y1 = Floor2Int(dImageY + splatSize);
00148 if (x1 < x0 || y1 < y0 || x1 < 0 || y1 < 0)
00149 return;
00150
00151 for (u_int y = static_cast<u_int>(Max<int>(y0, 0)); y <= static_cast<u_int>(Min<int>(y1, height - 1)); ++y)
00152 for (u_int x = static_cast<u_int>(Max<int>(x0, 0)); x <= static_cast<u_int>(Min<int>(x1, width - 1)); ++x)
00153 SplatRadiance(sampleElem->radiance, x, y, 0.01f);
00154 }
00155
00156 void NativePixelDevice::SplatFiltered(const SampleBufferElem *sampleElem) {
00157
00158 const float dImageX = sampleElem->screenX - 0.5f;
00159 const float dImageY = sampleElem->screenY - 0.5f;
00160 const FilterLUT *filterLUT = filterLUTs->GetLUT(dImageX - floorf(sampleElem->screenX), dImageY - floorf(sampleElem->screenY));
00161 const float *lut = filterLUT->GetLUT();
00162
00163 const int x0 = Ceil2Int(dImageX - filter->xWidth);
00164 const int x1 = x0 + filterLUT->GetWidth();
00165 const int y0 = Ceil2Int(dImageY - filter->yWidth);
00166 const int y1 = y0 + filterLUT->GetHeight();
00167
00168 for (int iy = y0; iy < y1; ++iy) {
00169 if (iy < 0) {
00170 lut += filterLUT->GetWidth();
00171 continue;
00172 } else if(iy >= int(height))
00173 break;
00174
00175 for (int ix = x0; ix < x1; ++ix) {
00176 const float filterWt = *lut++;
00177
00178 if ((ix < 0) || (ix >= int(width)))
00179 continue;
00180
00181 SplatRadiance(sampleElem->radiance, ix, iy, filterWt);
00182 }
00183 }
00184 }
00185
00186 void NativePixelDevice::AddSampleBuffer(const FilterType type, SampleBuffer *sampleBuffer) {
00187 boost::mutex::scoped_lock lock(splatMutex);
00188 assert (started);
00189
00190 const double t = WallClockTime();
00191 switch (type) {
00192 case FILTER_GAUSSIAN: {
00193 const SampleBufferElem *sbe = sampleBuffer->GetSampleBuffer();
00194 for (unsigned int i = 0; i < sampleBuffer->GetSampleCount(); ++i)
00195 SplatFiltered(&sbe[i]);
00196 break;
00197 }
00198 case FILTER_PREVIEW: {
00199 const SampleBufferElem *sbe = sampleBuffer->GetSampleBuffer();
00200 for (unsigned int i = 0; i < sampleBuffer->GetSampleCount(); ++i)
00201 SplatPreview(&sbe[i]);
00202 break;
00203 }
00204 case FILTER_NONE: {
00205 const SampleBufferElem *sbe = sampleBuffer->GetSampleBuffer();
00206 for (unsigned int i = 0; i < sampleBuffer->GetSampleCount(); ++i) {
00207 const SampleBufferElem *sampleElem = &sbe[i];
00208 const int x = (int)sampleElem->screenX;
00209 const int y = (int)sampleElem->screenY;
00210
00211 SplatRadiance(sampleElem->radiance, x, y, 1.f);
00212 }
00213 break;
00214 }
00215 default:
00216 assert (false);
00217 break;
00218 }
00219 statsTotalSampleTime += WallClockTime() - t;
00220 statsTotalSamplesCount += sampleBuffer->GetSampleCount();
00221
00222 freeSampleBuffers.push_back(sampleBuffer);
00223 }
00224
00225 void NativePixelDevice::UpdateFrameBuffer(const ToneMapParams ¶ms) {
00226 boost::mutex::scoped_lock lock(splatMutex);
00227
00228 switch (params.GetType()) {
00229 case TONEMAP_LINEAR: {
00230 const LinearToneMapParams &tm = (LinearToneMapParams &)params;
00231 const SamplePixel *sp = sampleFrameBuffer->GetPixels();
00232 Pixel *p = frameBuffer->GetPixels();
00233 const unsigned int pixelCount = width * height;
00234 for (unsigned int i = 0; i < pixelCount; ++i) {
00235 const float weight = sp[i].weight;
00236
00237 if (weight > 0.f) {
00238 const float invWeight = tm.scale / weight;
00239
00240 p[i].r = Radiance2PixelFloat(sp[i].radiance.r * invWeight);
00241 p[i].g = Radiance2PixelFloat(sp[i].radiance.g * invWeight);
00242 p[i].b = Radiance2PixelFloat(sp[i].radiance.b * invWeight);
00243 }
00244 }
00245 break;
00246 }
00247 case TONEMAP_REINHARD02: {
00248 const Reinhard02ToneMapParams &tm = (Reinhard02ToneMapParams &)params;
00249
00250 const float alpha = .1f;
00251 const float preScale = tm.preScale;
00252 const float postScale = tm.postScale;
00253 const float burn = tm.burn;
00254
00255 const SamplePixel *sp = sampleFrameBuffer->GetPixels();
00256 Pixel *p = frameBuffer->GetPixels();
00257 const unsigned int pixelCount = width * height;
00258
00259
00260 float Ywa = 0.f;
00261 for (unsigned int i = 0; i < pixelCount; ++i) {
00262 const float weight = sp[i].weight;
00263
00264 if (weight > 0.f) {
00265 const float invWeight = 1.f / weight;
00266
00267 Spectrum rgb = sp[i].radiance * invWeight;
00268
00269
00270 p[i].r = 0.412453f * rgb.r + 0.357580f * rgb.g + 0.180423f * rgb.b;
00271 p[i].g = 0.212671f * rgb.r + 0.715160f * rgb.g + 0.072169f * rgb.b;
00272 p[i].b = 0.019334f * rgb.r + 0.119193f * rgb.g + 0.950227f * rgb.b;
00273
00274 Ywa += p[i].g;
00275 }
00276 }
00277 Ywa /= pixelCount;
00278
00279
00280 if (Ywa == 0.f)
00281 Ywa = 1.f;
00282
00283 const float Yw = preScale * alpha * burn;
00284 const float invY2 = 1.f / (Yw * Yw);
00285 const float pScale = postScale * preScale * alpha / Ywa;
00286
00287 for (unsigned int i = 0; i < pixelCount; ++i) {
00288 Spectrum xyz = p[i];
00289
00290 const float ys = xyz.g;
00291 xyz *= pScale * (1.f + ys * invY2) / (1.f + ys);
00292
00293
00294 p[i].r = 3.240479f * xyz.r - 1.537150f * xyz.g - 0.498535f * xyz.b;
00295 p[i].g = -0.969256f * xyz.r + 1.875991f * xyz.g + 0.041556f * xyz.b;
00296 p[i].b = 0.055648f * xyz.r - 0.204043f * xyz.g + 1.057311f * xyz.b;
00297
00298
00299 p[i].r = Radiance2PixelFloat(p[i].r);
00300 p[i].g = Radiance2PixelFloat(p[i].g);
00301 p[i].b = Radiance2PixelFloat(p[i].b);
00302 }
00303 break;
00304 }
00305 default:
00306 assert (false);
00307 break;
00308 }
00309 }
00310
00311 void NativePixelDevice::Merge(const SampleFrameBuffer *sfb) {
00312 boost::mutex::scoped_lock lock(splatMutex);
00313
00314 const unsigned int pixelCount = width * height;
00315 for (unsigned int i = 0; i < pixelCount; ++i) {
00316 SamplePixel *sp = sfb->GetPixel(i);
00317 SamplePixel *spbase = sampleFrameBuffer->GetPixel(i);
00318
00319 spbase->radiance += sp->radiance;
00320 spbase->weight += sp->weight;
00321 }
00322 }
00323
00324 const SampleFrameBuffer *NativePixelDevice::GetSampleFrameBuffer() const {
00325 return sampleFrameBuffer;
00326 }