Torque3D Documentation / _generateds / IBLUtilities.cpp

IBLUtilities.cpp

Engine/source/T3D/lighting/IBLUtilities.cpp

More...

Namespaces:

namespace

Detailed Description

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "T3D/lighting/IBLUtilities.h"
 25#include "console/engineAPI.h"
 26#include "materials/shaderData.h"
 27#include "gfx/gfxTextureManager.h"
 28#include "gfx/gfxTransformSaver.h"
 29#include "gfx/bitmap/cubemapSaver.h"
 30#include "core/stream/fileStream.h"
 31#include "gfx/bitmap/imageUtils.h"
 32
 33namespace IBLUtilities
 34{
 35   void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut)
 36   {
 37      GFXTransformSaver saver;
 38
 39      GFXStateBlockRef irrStateBlock;
 40
 41      ShaderData *irrShaderData;
 42      GFXShaderRef irrShader = Sim::findObject("IrradianceShader", irrShaderData) ? irrShaderData->getShader() : NULL;
 43      if (!irrShader)
 44      {
 45         Con::errorf("IBLUtilities::GenerateIrradianceMap() - could not find IrradianceShader");
 46         return;
 47      }
 48
 49      GFXShaderConstBufferRef irrConsts = irrShader->allocConstBuffer();
 50      GFXShaderConstHandle* irrEnvMapSC = irrShader->getShaderConstHandle("$environmentMap");
 51      GFXShaderConstHandle* irrFaceSC = irrShader->getShaderConstHandle("$face");
 52
 53      GFXStateBlockDesc desc;
 54      desc.zEnable = false;
 55      desc.samplersDefined = true;
 56      desc.samplers[0].addressModeU = GFXAddressClamp;
 57      desc.samplers[0].addressModeV = GFXAddressClamp;
 58      desc.samplers[0].addressModeW = GFXAddressClamp;
 59      desc.samplers[0].magFilter = GFXTextureFilterLinear;
 60      desc.samplers[0].minFilter = GFXTextureFilterLinear;
 61      desc.samplers[0].mipFilter = GFXTextureFilterLinear;
 62
 63      irrStateBlock = GFX->createStateBlock(desc);
 64
 65      GFX->pushActiveRenderTarget();
 66      GFX->setShader(irrShader);
 67      GFX->setShaderConstBuffer(irrConsts);
 68      GFX->setStateBlock(irrStateBlock);
 69      GFX->setVertexBuffer(NULL);
 70      GFX->setCubeTexture(0, cubemap);
 71
 72      for (U32 i = 0; i < 6; i++)
 73      {
 74         renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, i);
 75         irrConsts->setSafe(irrFaceSC, (S32)i);
 76         GFX->setActiveRenderTarget(renderTarget);
 77         GFX->clear(GFXClearTarget, LinearColorF::BLACK, 1.0f, 0);
 78         GFX->drawPrimitive(GFXTriangleList, 0, 1);
 79         renderTarget->resolve();
 80      }
 81
 82      GFX->popActiveRenderTarget();
 83   }
 84
 85   void GenerateAndSaveIrradianceMap(String outputPath, S32 resolution, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut)
 86   {
 87      if (outputPath.isEmpty())
 88      {
 89         Con::errorf("IBLUtilities::GenerateAndSaveIrradianceMap - Cannot save to an empty path!");
 90         return;
 91      }
 92
 93      GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget(false);
 94
 95      IBLUtilities::GenerateIrradianceMap(renderTarget, cubemap, cubemapOut);
 96
 97      //Write it out
 98      CubemapSaver::save(cubemapOut, outputPath);
 99
100      if (!Platform::isFile(outputPath))
101      {
102         Con::errorf("IBLUtilities::GenerateAndSaveIrradianceMap - Failed to properly save out the baked irradiance!");
103      }
104   }
105
106   void SaveCubeMap(String outputPath, GFXCubemapHandle &cubemap)
107   {
108      if (outputPath.isEmpty())
109      {
110         Con::errorf("IBLUtilities::SaveCubeMap - Cannot save to an empty path!");
111         return;
112      }
113
114      //Write it out
115      CubemapSaver::save(cubemap, outputPath);
116
117      if (!Platform::isFile(outputPath))
118      {
119         Con::errorf("IBLUtilities::SaveCubeMap - Failed to properly save out the baked irradiance!");
120      }
121   }
122
123   void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut)
124   {
125      GFXTransformSaver saver;
126
127      ShaderData *prefilterShaderData;
128      GFXShaderRef prefilterShader = Sim::findObject("PrefiterCubemapShader", prefilterShaderData) ? prefilterShaderData->getShader() : NULL;
129      if (!prefilterShader)
130      {
131         Con::errorf("IBLUtilities::GeneratePrefilterMap() - could not find PrefiterCubemapShader");
132         return;
133      }
134
135      GFXShaderConstBufferRef prefilterConsts = prefilterShader->allocConstBuffer();
136      GFXShaderConstHandle* prefilterEnvMapSC = prefilterShader->getShaderConstHandle("$environmentMap");
137      GFXShaderConstHandle* prefilterFaceSC = prefilterShader->getShaderConstHandle("$face");
138      GFXShaderConstHandle* prefilterRoughnessSC = prefilterShader->getShaderConstHandle("$roughness");
139      GFXShaderConstHandle* prefilterMipSizeSC = prefilterShader->getShaderConstHandle("$mipSize");
140      GFXShaderConstHandle* prefilterResolutionSC = prefilterShader->getShaderConstHandle("$resolution");
141
142      GFXStateBlockDesc desc;
143      desc.zEnable = false;
144      desc.samplersDefined = true;
145      desc.samplers[0].addressModeU = GFXAddressClamp;
146      desc.samplers[0].addressModeV = GFXAddressClamp;
147      desc.samplers[0].addressModeW = GFXAddressClamp;
148      desc.samplers[0].magFilter = GFXTextureFilterLinear;
149      desc.samplers[0].minFilter = GFXTextureFilterLinear;
150      desc.samplers[0].mipFilter = GFXTextureFilterLinear;
151
152      GFXStateBlockRef preStateBlock;
153      preStateBlock = GFX->createStateBlock(desc);
154      GFX->setStateBlock(preStateBlock);
155
156      GFX->pushActiveRenderTarget();
157      GFX->setShader(prefilterShader);
158      GFX->setShaderConstBuffer(prefilterConsts);
159      GFX->setCubeTexture(0, cubemap);
160
161      U32 prefilterSize = cubemapOut->getSize();
162
163      U32 resolutionSize = prefilterSize;
164
165      for (U32 face = 0; face < 6; face++)
166      {
167         prefilterConsts->setSafe(prefilterFaceSC, (S32)face);
168         prefilterConsts->setSafe(prefilterResolutionSC, (S32)resolutionSize);
169
170         for (U32 mip = 0; mip < mipLevels; mip++)
171         {
172            S32 mipSize = prefilterSize >> mip;
173            F32 roughness = (float)mip / (float)(mipLevels - 1);
174            prefilterConsts->setSafe(prefilterRoughnessSC, roughness);
175            prefilterConsts->setSafe(prefilterMipSizeSC, mipSize);
176            U32 size = prefilterSize * mPow(0.5f, mip);
177            renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, face, mip);
178            GFX->setActiveRenderTarget(renderTarget, false);//we set the viewport ourselves
179            GFX->setViewport(RectI(0, 0, size, size));
180            GFX->clear(GFXClearTarget, LinearColorF::BLACK, 1.0f, 0);
181            GFX->drawPrimitive(GFXTriangleList, 0, 1);
182            renderTarget->resolve();
183         }
184      }
185
186      GFX->popActiveRenderTarget();
187   }
188
189   void GenerateAndSavePrefilterMap(String outputPath, S32 resolution, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut)
190   {
191      if (outputPath.isEmpty())
192      {
193         Con::errorf("IBLUtilities::GenerateAndSavePrefilterMap - Cannot save to an empty path!");
194         return;
195      }
196
197      GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget(false);
198
199      IBLUtilities::GeneratePrefilterMap(renderTarget, cubemap, mipLevels, cubemapOut);
200
201      //Write it out
202      CubemapSaver::save(cubemapOut, outputPath);
203
204      if (!Platform::isFile(outputPath))
205      {
206         Con::errorf("IBLUtilities::GenerateAndSavePrefilterMap - Failed to properly save out the baked irradiance!");
207      }
208   }   
209
210   void bakeReflection(String outputPath, S32 resolution)
211   {
212      //GFXDEBUGEVENT_SCOPE(ReflectionProbe_Bake, ColorI::WHITE);
213
214      /*PostEffect *preCapture = dynamic_cast<PostEffect*>(Sim::findObject("AL_PreCapture"));
215      PostEffect *deferredShading = dynamic_cast<PostEffect*>(Sim::findObject("AL_DeferredShading"));
216      if (preCapture)
217         preCapture->enable();
218      if (deferredShading)
219         deferredShading->disable();
220
221      //if (mReflectionModeType == StaticCubemap || mReflectionModeType == BakedCubemap || mReflectionModeType == SkyLight)
222      {
223         if (!mCubemap)
224         {
225            mCubemap = new CubemapData();
226            mCubemap->registerObject();
227         }
228      }
229
230      if (mReflectionModeType == DynamicCubemap && mDynamicCubemap.isNull())
231      {
232         //mCubemap->createMap();
233         mDynamicCubemap = GFX->createCubemap();
234         mDynamicCubemap->initDynamic(resolution, GFXFormatR8G8B8);
235      }
236      else if (mReflectionModeType != DynamicCubemap)
237      {
238         if (mReflectionPath.isEmpty() || !mPersistentId)
239         {
240            if (!mPersistentId)
241               mPersistentId = getOrCreatePersistentId();
242
243            mReflectionPath = outputPath.c_str();
244
245            mProbeUniqueID = std::to_string(mPersistentId->getUUID().getHash()).c_str();
246         }
247      }
248
249      bool validCubemap = true;
250
251      // Save the current transforms so we can restore
252      // it for child control rendering below.
253      GFXTransformSaver saver;
254
255      //bool saveEditingMission = gEditingMission;
256      //gEditingMission = false;
257
258      //Set this to true to use the prior method where it goes through the SPT_Reflect path for the bake
259      bool probeRenderState = ReflectionProbe::smRenderReflectionProbes;
260      ReflectionProbe::smRenderReflectionProbes = false;
261      for (U32 i = 0; i < 6; ++i)
262      {
263         GFXTexHandle blendTex;
264         blendTex.set(resolution, resolution, GFXFormatR8G8B8A8, &GFXRenderTargetProfile, "");
265
266         GFXTextureTargetRef mBaseTarget = GFX->allocRenderToTextureTarget();
267
268         GFX->clearTextureStateImmediate(0);
269         if (mReflectionModeType == DynamicCubemap)
270            mBaseTarget->attachTexture(GFXTextureTarget::Color0, mDynamicCubemap, i);
271         else
272            mBaseTarget->attachTexture(GFXTextureTarget::Color0, blendTex);
273
274         // Standard view that will be overridden below.
275         VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
276
277         switch (i)
278         {
279            case 0: // D3DCUBEMAP_FACE_POSITIVE_X:
280               vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
281               vUpVec = VectorF(0.0f, 1.0f, 0.0f);
282               break;
283            case 1: // D3DCUBEMAP_FACE_NEGATIVE_X:
284               vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
285               vUpVec = VectorF(0.0f, 1.0f, 0.0f);
286               break;
287            case 2: // D3DCUBEMAP_FACE_POSITIVE_Y:
288               vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
289               vUpVec = VectorF(0.0f, 0.0f, -1.0f);
290               break;
291            case 3: // D3DCUBEMAP_FACE_NEGATIVE_Y:
292               vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
293               vUpVec = VectorF(0.0f, 0.0f, 1.0f);
294               break;
295            case 4: // D3DCUBEMAP_FACE_POSITIVE_Z:
296               vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
297               vUpVec = VectorF(0.0f, 1.0f, 0.0f);
298               break;
299            case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
300               vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
301               vUpVec = VectorF(0.0f, 1.0f, 0.0f);
302               break;
303         }
304
305         // create camera matrix
306         VectorF cross = mCross(vUpVec, vLookatPt);
307         cross.normalizeSafe();
308
309         MatrixF matView(true);
310         matView.setColumn(0, cross);
311         matView.setColumn(1, vLookatPt);
312         matView.setColumn(2, vUpVec);
313         matView.setPosition(getPosition());
314         matView.inverse();
315
316         // set projection to 90 degrees vertical and horizontal
317         F32 left, right, top, bottom;
318         F32 nearPlane = 0.01f;
319         F32 farDist = 1000.f;
320
321         MathUtils::makeFrustum(&left, &right, &top, &bottom, M_HALFPI_F, 1.0f, nearPlane);
322         Frustum frustum(false, left, right, top, bottom, nearPlane, farDist);
323
324         renderFrame(&mBaseTarget, matView, frustum, StaticObjectType | StaticShapeObjectType & EDITOR_RENDER_TYPEMASK, gCanvasClearColor);
325
326         mBaseTarget->resolve();
327
328         mCubemap->setCubeFaceTexture(i, blendTex);
329      }
330
331      if (mReflectionModeType != DynamicCubemap && validCubemap)
332      {
333         if (mCubemap->mCubemap)
334            mCubemap->updateFaces();
335         else
336            mCubemap->createMap();
337
338         char fileName[256];
339         dSprintf(fileName, 256, "%s%s.DDS", mReflectionPath.c_str(), mProbeUniqueID.c_str());
340
341         CubemapSaver::save(mCubemap->mCubemap, fileName);
342
343         if (!Platform::isFile(fileName))
344         {
345            validCubemap = false; //if we didn't save right, just 
346            Con::errorf("Failed to properly save out the skylight baked cubemap!");
347         }
348
349         mDirty = false;
350      }
351
352      //calculateSHTerms();
353
354      ReflectionProbe::smRenderReflectionProbes = probeRenderState;
355      setMaskBits(-1);
356
357      if (preCapture)
358         preCapture->disable();
359      if (deferredShading)
360         deferredShading->enable();*/
361   }
362
363   LinearColorF decodeSH(Point3F normal, const LinearColorF SHTerms[9], const F32 SHConstants[5])
364   {
365      float x = normal.x;
366      float y = normal.y;
367      float z = normal.z;
368
369      LinearColorF l00 = SHTerms[0];
370
371      LinearColorF l10 = SHTerms[1];
372      LinearColorF l11 = SHTerms[2];
373      LinearColorF l12 = SHTerms[3];
374
375      LinearColorF l20 = SHTerms[4];
376      LinearColorF l21 = SHTerms[5];
377      LinearColorF l22 = SHTerms[6];
378      LinearColorF l23 = SHTerms[7];
379      LinearColorF l24 = SHTerms[8];
380
381      LinearColorF result = (
382         l00 * SHConstants[0] +
383
384         l12 * SHConstants[1] * x +
385         l10 * SHConstants[1] * y +
386         l11 * SHConstants[1] * z +
387
388         l20 * SHConstants[2] * x*y +
389         l21 * SHConstants[2] * y*z +
390         l22 * SHConstants[3] * (3.0*z*z - 1.0) +
391         l23 * SHConstants[2] * x*z +
392         l24 * SHConstants[4] * (x*x - y * y)
393         );
394
395      return LinearColorF(mMax(result.red, 0), mMax(result.green, 0), mMax(result.blue, 0));
396   }
397
398   MatrixF getSideMatrix(U32 side)
399   {
400      // Standard view that will be overridden below.
401      VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
402
403      switch (side)
404      {
405         case 0: // D3DCUBEMAP_FACE_POSITIVE_X:
406            vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
407            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
408            break;
409         case 1: // D3DCUBEMAP_FACE_NEGATIVE_X:
410            vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
411            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
412            break;
413         case 2: // D3DCUBEMAP_FACE_POSITIVE_Y:
414            vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
415            vUpVec = VectorF(0.0f, 0.0f, -1.0f);
416            break;
417         case 3: // D3DCUBEMAP_FACE_NEGATIVE_Y:
418            vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
419            vUpVec = VectorF(0.0f, 0.0f, 1.0f);
420            break;
421         case 4: // D3DCUBEMAP_FACE_POSITIVE_Z:
422            vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
423            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
424            break;
425         case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
426            vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
427            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
428            break;
429      }
430
431      // create camera matrix
432      VectorF cross = mCross(vUpVec, vLookatPt);
433      cross.normalizeSafe();
434
435      MatrixF rotMat(true);
436      rotMat.setColumn(0, cross);
437      rotMat.setColumn(1, vLookatPt);
438      rotMat.setColumn(2, vUpVec);
439      //rotMat.inverse();
440
441      return rotMat;
442   }
443
444   F32 harmonics(U32 termId, Point3F normal)
445   {
446      F32 x = normal.x;
447      F32 y = normal.y;
448      F32 z = normal.z;
449
450      switch (termId)
451      {
452         case 0:
453            return 1.0;
454         case 1:
455            return y;
456         case 2:
457            return z;
458         case 3:
459            return x;
460         case 4:
461            return x * y;
462         case 5:
463            return y * z;
464         case 6:
465            return 3.0*z*z - 1.0;
466         case 7:
467            return x * z;
468         default:
469            return x * x - y * y;
470      }
471   }
472
473   LinearColorF sampleSide(GBitmap* cubeFaceBitmaps[6], const U32& cubemapResolution, const U32& termindex, const U32& sideIndex)
474   {
475      MatrixF sideRot = getSideMatrix(sideIndex);
476
477      LinearColorF result = LinearColorF::ZERO;
478      F32 divider = 0;
479
480      for (int y = 0; y<cubemapResolution; y++)
481      {
482         for (int x = 0; x<cubemapResolution; x++)
483         {
484            Point2F sidecoord = ((Point2F(x, y) + Point2F(0.5, 0.5)) / Point2F(cubemapResolution, cubemapResolution))*2.0 - Point2F(1.0, 1.0);
485            Point3F normal = Point3F(sidecoord.x, sidecoord.y, -1.0);
486            normal.normalize();
487
488            F32 minBrightness = Con::getFloatVariable("$pref::GI::Cubemap_Sample_MinBrightness", 0.001f);
489
490            LinearColorF texel = cubeFaceBitmaps[sideIndex]->sampleTexel(y, x);
491            texel = LinearColorF(mMax(texel.red, minBrightness), mMax(texel.green, minBrightness), mMax(texel.blue, minBrightness)) * Con::getFloatVariable("$pref::GI::Cubemap_Gain", 1.5);
492
493            Point3F dir;
494            sideRot.mulP(normal, &dir);
495
496            result += texel * harmonics(termindex, dir) * -normal.z;
497            divider += -normal.z;
498         }
499      }
500
501      result /= divider;
502
503      return result;
504   }
505
506   //
507   //SH Calculations
508   // From http://sunandblackcat.com/tipFullView.php?l=eng&topicid=32&topic=Spherical-Harmonics-From-Cube-Texture
509   // With shader decode logic from https://github.com/nicknikolov/cubemap-sh
510   void calculateSHTerms(GFXCubemapHandle cubemap, LinearColorF SHTerms[9], F32 SHConstants[5])
511   {
512      if (!cubemap)
513         return;
514
515      const VectorF cubemapFaceNormals[6] =
516      {
517         // D3DCUBEMAP_FACE_POSITIVE_X:
518         VectorF(1.0f, 0.0f, 0.0f),
519         // D3DCUBEMAP_FACE_NEGATIVE_X:
520         VectorF(-1.0f, 0.0f, 0.0f),
521         // D3DCUBEMAP_FACE_POSITIVE_Y:
522         VectorF(0.0f, 1.0f, 0.0f),
523         // D3DCUBEMAP_FACE_NEGATIVE_Y:
524         VectorF(0.0f, -1.0f, 0.0f),
525         // D3DCUBEMAP_FACE_POSITIVE_Z:
526         VectorF(0.0f, 0.0f, 1.0f),
527         // D3DCUBEMAP_FACE_NEGATIVE_Z:
528         VectorF(0.0f, 0.0f, -1.0f),
529      };
530
531      U32 cubemapResolution = cubemap->getSize();
532
533      GBitmap* cubeFaceBitmaps[6];
534
535      for (U32 i = 0; i < 6; i++)
536      {
537         cubeFaceBitmaps[i] = new GBitmap(cubemapResolution, cubemapResolution, false, GFXFormatR16G16B16A16F);
538      }
539
540      //If we fail to parse the cubemap for whatever reason, we really can't continue
541      if (!CubemapSaver::getBitmaps(cubemap, GFXFormatR8G8B8A8, cubeFaceBitmaps))
542         return;
543
544      //Set up our constants
545      F32 L0 = Con::getFloatVariable("$pref::GI::SH_Term_L0", 1.0f);
546      F32 L1 = Con::getFloatVariable("$pref::GI::SH_Term_L1", 1.8f);
547      F32 L2 = Con::getFloatVariable("$pref::GI::SH_Term_L2", 0.83f);
548      F32 L2m2_L2m1_L21 = Con::getFloatVariable("$pref::GI::SH_Term_L2m2", 2.9f);
549      F32 L20 = Con::getFloatVariable("$pref::GI::SH_Term_L20", 0.58f);
550      F32 L22 = Con::getFloatVariable("$pref::GI::SH_Term_L22", 1.1f);
551
552      SHConstants[0] = L0;
553      SHConstants[1] = L1;
554      SHConstants[2] = L2 * L2m2_L2m1_L21;
555      SHConstants[3] = L2 * L20;
556      SHConstants[4] = L2 * L22;
557
558      for (U32 i = 0; i < 9; i++)
559      {
560         //Clear it, just to be sure
561         SHTerms[i] = LinearColorF(0.f, 0.f, 0.f);
562
563         //Now, encode for each side
564         SHTerms[i] = sampleSide(cubeFaceBitmaps, cubemapResolution, i, 0); //POS_X
565         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 1); //NEG_X
566         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 2); //POS_Y
567         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 3); //NEG_Y
568         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 4); //POS_Z
569         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 5); //NEG_Z
570
571                                                      //Average
572         SHTerms[i] /= 6;
573      }
574
575      for (U32 i = 0; i < 6; i++)
576         SAFE_DELETE(cubeFaceBitmaps[i]);
577
578      /*bool mExportSHTerms = false;
579      if (mExportSHTerms)
580      {
581         for (U32 f = 0; f < 6; f++)
582         {
583            char fileName[256];
584            dSprintf(fileName, 256, "%s%s_DecodedFaces_%d.png", mReflectionPath.c_str(),
585               mProbeUniqueID.c_str(), f);
586
587            LinearColorF color = decodeSH(cubemapFaceNormals[f]);
588
589            FileStream stream;
590            if (stream.open(fileName, Torque::FS::File::Write))
591            {
592               GBitmap bitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
593
594               bitmap.fill(color.toColorI());
595
596               bitmap.writeBitmap("png", stream);
597            }
598         }
599
600         for (U32 f = 0; f < 9; f++)
601         {
602            char fileName[256];
603            dSprintf(fileName, 256, "%s%s_SHTerms_%d.png", mReflectionPath.c_str(),
604               mProbeUniqueID.c_str(), f);
605
606            LinearColorF color = mProbeInfo->SHTerms[f];
607
608            FileStream stream;
609            if (stream.open(fileName, Torque::FS::File::Write))
610            {
611               GBitmap bitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
612
613               bitmap.fill(color.toColorI());
614
615               bitmap.writeBitmap("png", stream);
616            }
617         }
618      }*/
619   }
620
621   F32 areaElement(F32 x, F32 y)
622   {
623      return mAtan2(x * y, (F32)mSqrt(x * x + y * y + 1.0));
624   }
625
626   F32 texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height)
627   {
628      // transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)]
629      // ( 0.5 is for texel center addressing)
630      const F32 U = (2.0 * (aU + 0.5) / width) - 1.0;
631      const F32 V = (2.0 * (aV + 0.5) / height) - 1.0;
632
633      // shift from a demi texel, mean 1.0 / size  with U and V in [-1..1]
634      const F32 invResolutionW = 1.0 / width;
635      const F32 invResolutionH = 1.0 / height;
636
637      // U and V are the -1..1 texture coordinate on the current face.
638      // get projected area for this texel
639      const F32 x0 = U - invResolutionW;
640      const F32 y0 = V - invResolutionH;
641      const F32 x1 = U + invResolutionW;
642      const F32 y1 = V + invResolutionH;
643      const F32 angle = areaElement(x0, y0) - areaElement(x0, y1) - areaElement(x1, y0) + areaElement(x1, y1);
644
645      return angle;
646   }
647};
648