BsLightProbeVolume.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Renderer/BsLightProbeVolume.h"
  4. #include "RTTI/BsLightProbeVolumeRTTI.h"
  5. #include "Renderer/BsRenderer.h"
  6. #include "Renderer/BsLight.h"
  7. #include "Image/BsTexture.h"
  8. #include "Renderer/BsIBLUtility.h"
  9. #include "Scene/BsSceneObject.h"
  10. namespace bs
  11. {
  12. LightProbeVolume::LightProbeVolume()
  13. : mVolume(AABox::UNIT_BOX), mCellCount { 1, 1, 1 }
  14. { }
  15. LightProbeVolume::LightProbeVolume(const AABox& volume, const Vector3I& cellCount)
  16. :mVolume(volume), mCellCount(cellCount)
  17. {
  18. reset();
  19. }
  20. LightProbeVolume::~LightProbeVolume()
  21. {
  22. if (mRendererTask)
  23. mRendererTask->cancel();
  24. }
  25. UINT32 LightProbeVolume::addProbe(const Vector3& position)
  26. {
  27. UINT32 handle = mNextProbeId++;
  28. mProbes[handle] = ProbeInfo(LightProbeFlags::Clean, position);
  29. _markCoreDirty();
  30. return handle;
  31. }
  32. void LightProbeVolume::removeProbe(UINT32 handle)
  33. {
  34. auto iterFind = mProbes.find(handle);
  35. if (iterFind != mProbes.end() && mProbes.size() > 4)
  36. {
  37. iterFind->second.flags = LightProbeFlags::Removed;
  38. _markCoreDirty();
  39. }
  40. }
  41. void LightProbeVolume::setProbePosition(UINT32 handle, const Vector3& position)
  42. {
  43. auto iterFind = mProbes.find(handle);
  44. if (iterFind != mProbes.end())
  45. {
  46. iterFind->second.position = position;
  47. _markCoreDirty();
  48. }
  49. }
  50. Vector3 LightProbeVolume::getProbePosition(UINT32 handle) const
  51. {
  52. auto iterFind = mProbes.find(handle);
  53. if (iterFind != mProbes.end())
  54. return iterFind->second.position;
  55. return Vector3::ZERO;
  56. }
  57. Vector<LightProbeInfo> LightProbeVolume::getProbes() const
  58. {
  59. Vector<LightProbeInfo> output;
  60. for(auto& entry : mProbes)
  61. {
  62. if (entry.second.flags == LightProbeFlags::Removed || entry.second.flags == LightProbeFlags::Empty)
  63. continue;
  64. LightProbeInfo info;
  65. info.position = entry.second.position;
  66. info.handle = entry.first;
  67. info.shCoefficients = entry.second.coefficients;
  68. output.push_back(info);
  69. }
  70. return output;
  71. }
  72. void LightProbeVolume::resize(const AABox& volume, const Vector3I& cellCount)
  73. {
  74. UINT32 numProbesX = std::max(1, mCellCount.x) + 1;
  75. UINT32 numProbesY = std::max(1, mCellCount.y) + 1;
  76. UINT32 numProbesZ = std::max(1, mCellCount.z) + 1;
  77. Vector3 size = mVolume.getSize();
  78. for(UINT32 z = 0; z < numProbesZ; ++z)
  79. {
  80. for(UINT32 y = 0; y < numProbesY; ++y)
  81. {
  82. for(UINT32 x = 0; x < numProbesX; ++x)
  83. {
  84. Vector3 position = mVolume.getMin();
  85. position.x += size.x * (x / (float)numProbesX);
  86. position.y += size.y * (y / (float)numProbesY);
  87. position.z += size.z * (z / (float)numProbesZ);
  88. if (mVolume.contains(position))
  89. continue;
  90. addProbe(position);
  91. }
  92. }
  93. }
  94. mVolume = volume;
  95. mCellCount = cellCount;
  96. _markCoreDirty();
  97. }
  98. void LightProbeVolume::reset()
  99. {
  100. UINT32 numProbesX = std::max(1, mCellCount.x) + 1;
  101. UINT32 numProbesY = std::max(1, mCellCount.y) + 1;
  102. UINT32 numProbesZ = std::max(1, mCellCount.z) + 1;
  103. UINT32 numProbes = numProbesX * numProbesY * numProbesZ;
  104. // Make sure there are adequate number of probes to fill the volume
  105. while((UINT32)mProbes.size() < numProbes)
  106. addProbe(Vector3::ZERO);
  107. UINT32 idx = 0;
  108. UINT32 rowPitch = numProbesX;
  109. UINT32 slicePitch = numProbesX * numProbesY;
  110. Vector3 size = mVolume.getSize();
  111. auto iter = mProbes.begin();
  112. while (iter != mProbes.end())
  113. {
  114. UINT32 x = idx % numProbesX;
  115. UINT32 y = (idx / rowPitch) % numProbesY;
  116. UINT32 z = (idx / slicePitch);
  117. Vector3 position = mVolume.getMin();
  118. position.x += size.x * (x / (float)(numProbesX - 1));
  119. position.y += size.y * (y / (float)(numProbesY - 1));
  120. position.z += size.z * (z / (float)(numProbesZ - 1));
  121. iter->second.position = position;
  122. iter->second.flags = LightProbeFlags::Clean;
  123. ++idx;
  124. ++iter;
  125. if (idx >= numProbes)
  126. break;
  127. }
  128. // Set remaining probes to removed state
  129. while(iter != mProbes.end())
  130. {
  131. iter->second.flags = LightProbeFlags::Removed;
  132. ++iter;
  133. }
  134. _markCoreDirty();
  135. }
  136. void LightProbeVolume::clip()
  137. {
  138. for (auto& entry : mProbes)
  139. {
  140. if (!mVolume.contains(entry.second.position))
  141. entry.second.flags = LightProbeFlags::Removed;
  142. }
  143. _markCoreDirty();
  144. }
  145. void LightProbeVolume::renderProbe(UINT32 handle)
  146. {
  147. auto iterFind = mProbes.find(handle);
  148. if (iterFind != mProbes.end())
  149. {
  150. if (iterFind->second.flags == LightProbeFlags::Clean)
  151. {
  152. iterFind->second.flags = LightProbeFlags::Dirty;
  153. _markCoreDirty();
  154. runRenderProbeTask();
  155. }
  156. }
  157. }
  158. void LightProbeVolume::renderProbes()
  159. {
  160. bool anyModified = false;
  161. for(auto& entry : mProbes)
  162. {
  163. if (entry.second.flags == LightProbeFlags::Clean)
  164. {
  165. entry.second.flags = LightProbeFlags::Dirty;
  166. anyModified = true;
  167. }
  168. }
  169. if (anyModified)
  170. {
  171. _markCoreDirty();
  172. runRenderProbeTask();
  173. }
  174. }
  175. void LightProbeVolume::runRenderProbeTask()
  176. {
  177. // If a task is already running cancel it
  178. // Note: If the task is just about to start processing, cancelling it will skip the update this frame
  179. // (which might be fine if we just changed positions of dirty probes it was about to update, but it might also
  180. // waste a frame if those positions needed to be updated anyway). For now I'm ignoring it as it seems like a rare
  181. // enough situation, plus it's one that will only happen during development time.
  182. if (mRendererTask)
  183. mRendererTask->cancel();
  184. auto renderComplete = [this]()
  185. {
  186. mRendererTask = nullptr;
  187. };
  188. SPtr<ct::LightProbeVolume> coreProbeVolume = getCore();
  189. auto renderProbes = [coreProbeVolume]()
  190. {
  191. return coreProbeVolume->renderProbes(3);
  192. };
  193. mRendererTask = ct::RendererTask::create("RenderLightProbes", renderProbes);
  194. mRendererTask->onComplete.connect(renderComplete);
  195. ct::gRenderer()->addTask(mRendererTask);
  196. }
  197. void LightProbeVolume::updateCoefficients()
  198. {
  199. // Ensure all light probe coefficients are generated
  200. if (mRendererTask)
  201. mRendererTask->wait();
  202. ct::LightProbeVolume* coreVolume = getCore().get();
  203. Vector<LightProbeCoefficientInfo> coeffInfo;
  204. auto getSaveData = [coreVolume, &coeffInfo]()
  205. {
  206. coreVolume->getProbeCoefficients(coeffInfo);
  207. };
  208. gCoreThread().queueCommand(getSaveData);
  209. gCoreThread().submit(true);
  210. for(auto& entry : coeffInfo)
  211. {
  212. auto iterFind = mProbes.find(entry.handle);
  213. if (iterFind == mProbes.end())
  214. continue;
  215. iterFind->second.coefficients = entry.coefficients;
  216. }
  217. }
  218. SPtr<ct::LightProbeVolume> LightProbeVolume::getCore() const
  219. {
  220. return std::static_pointer_cast<ct::LightProbeVolume>(mCoreSpecific);
  221. }
  222. SPtr<LightProbeVolume> LightProbeVolume::create(const AABox& volume, const Vector3I& cellCount)
  223. {
  224. LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume(volume, cellCount);
  225. SPtr<LightProbeVolume> probeVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
  226. probeVolumePtr->_setThisPtr(probeVolumePtr);
  227. probeVolumePtr->initialize();
  228. return probeVolumePtr;
  229. }
  230. SPtr<LightProbeVolume> LightProbeVolume::createEmpty()
  231. {
  232. LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume();
  233. SPtr<LightProbeVolume> probleVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
  234. probleVolumePtr->_setThisPtr(probleVolumePtr);
  235. return probleVolumePtr;
  236. }
  237. SPtr<ct::CoreObject> LightProbeVolume::createCore() const
  238. {
  239. ct::LightProbeVolume* handler = new (bs_alloc<ct::LightProbeVolume>()) ct::LightProbeVolume(mProbes);
  240. SPtr<ct::LightProbeVolume> handlerPtr = bs_shared_ptr<ct::LightProbeVolume>(handler);
  241. handlerPtr->_setThisPtr(handlerPtr);
  242. return handlerPtr;
  243. }
  244. CoreSyncData LightProbeVolume::syncToCore(FrameAlloc* allocator)
  245. {
  246. UINT32 size = 0;
  247. UINT8* buffer = nullptr;
  248. bs_frame_mark();
  249. {
  250. FrameVector<std::pair<UINT32, ProbeInfo>> dirtyProbes;
  251. FrameVector<UINT32> removedProbes;
  252. for (auto& probe : mProbes)
  253. {
  254. if (probe.second.flags == LightProbeFlags::Dirty)
  255. {
  256. dirtyProbes.push_back(std::make_pair(probe.first, probe.second));
  257. probe.second.flags = LightProbeFlags::Clean;
  258. }
  259. else if (probe.second.flags == LightProbeFlags::Removed)
  260. {
  261. removedProbes.push_back(probe.first);
  262. probe.second.flags = LightProbeFlags::Empty;
  263. }
  264. }
  265. for (auto& probe : removedProbes)
  266. mProbes.erase(probe);
  267. UINT32 numDirtyProbes = (UINT32)dirtyProbes.size();
  268. UINT32 numRemovedProbes = (UINT32)removedProbes.size();
  269. size += getActorSyncDataSize();
  270. size += rttiGetElemSize(numDirtyProbes);
  271. size += rttiGetElemSize(numRemovedProbes);
  272. size += (sizeof(UINT32) + sizeof(Vector3) + sizeof(LightProbeFlags)) * numDirtyProbes;
  273. size += sizeof(UINT32) * numRemovedProbes;
  274. buffer = allocator->alloc(size);
  275. char* dataPtr = (char*)buffer;
  276. dataPtr = syncActorTo(dataPtr);
  277. dataPtr = rttiWriteElem(numDirtyProbes, dataPtr);
  278. dataPtr = rttiWriteElem(numRemovedProbes, dataPtr);
  279. for (auto& entry : dirtyProbes)
  280. {
  281. dataPtr = rttiWriteElem(entry.first, dataPtr);
  282. dataPtr = rttiWriteElem(entry.second.position, dataPtr);
  283. dataPtr = rttiWriteElem(entry.second.flags, dataPtr);
  284. }
  285. for(auto& entry : removedProbes)
  286. dataPtr = rttiWriteElem(entry, dataPtr);
  287. }
  288. bs_frame_clear();
  289. return CoreSyncData(buffer, size);
  290. }
  291. void LightProbeVolume::_markCoreDirty(ActorDirtyFlag dirtyFlag)
  292. {
  293. markCoreDirty((UINT32)dirtyFlag);
  294. }
  295. RTTITypeBase* LightProbeVolume::getRTTIStatic()
  296. {
  297. return LightProbeVolumeRTTI::instance();
  298. }
  299. RTTITypeBase* LightProbeVolume::getRTTI() const
  300. {
  301. return LightProbeVolume::getRTTIStatic();
  302. }
  303. namespace ct
  304. {
  305. LightProbeVolume::LightProbeVolume(const UnorderedMap<UINT32, bs::LightProbeVolume::ProbeInfo>& probes)
  306. {
  307. mInitCoefficients.resize(probes.size());
  308. mProbePositions.resize(probes.size());
  309. mProbeInfos.resize(probes.size());
  310. UINT32 probeIdx = 0;
  311. for(auto& entry : probes)
  312. {
  313. mProbeMap[entry.first] = probeIdx;
  314. mProbePositions[probeIdx] = entry.second.position;
  315. LightProbeInfo probeInfo;
  316. probeInfo.flags = LightProbeFlags::Dirty;
  317. probeInfo.bufferIdx = probeIdx;
  318. probeInfo.handle = entry.first;
  319. mProbeInfos[probeIdx] = probeInfo;
  320. mInitCoefficients[probeIdx] = entry.second.coefficients;
  321. probeIdx++;
  322. }
  323. }
  324. LightProbeVolume::~LightProbeVolume()
  325. {
  326. gRenderer()->notifyLightProbeVolumeRemoved(this);
  327. }
  328. void LightProbeVolume::initialize()
  329. {
  330. // Set SH coefficients loaded from the file
  331. UINT32 numCoefficients = (UINT32)mInitCoefficients.size();
  332. assert(mInitCoefficients.size() == mProbeMap.size());
  333. resizeCoefficientTexture(std::max(32U, numCoefficients));
  334. SPtr<PixelData> coeffData = mCoefficients->getProperties().allocBuffer(0, 0);
  335. UINT32 probesPerRow = coeffData->getWidth() / 9;
  336. UINT32 probeIdx = 0;
  337. for(UINT32 y = 0; y < coeffData->getHeight(); ++y)
  338. {
  339. for(UINT32 x = 0; x < probesPerRow; ++x)
  340. {
  341. if(probeIdx >= numCoefficients)
  342. break;
  343. for(UINT32 i = 0; i < 9; i++)
  344. {
  345. Color value;
  346. value.r = mInitCoefficients[probeIdx].coeffsR[i];
  347. value.g = mInitCoefficients[probeIdx].coeffsG[i];
  348. value.b = mInitCoefficients[probeIdx].coeffsB[i];
  349. coeffData->setColorAt(value, x * 9, y);
  350. }
  351. probeIdx++;
  352. }
  353. }
  354. mCoefficients->writeData(*coeffData, 0, 0, true);
  355. mInitCoefficients.clear();
  356. gRenderer()->notifyLightProbeVolumeAdded(this);
  357. CoreObject::initialize();
  358. }
  359. bool LightProbeVolume::renderProbes(UINT32 maxProbes)
  360. {
  361. // Probe map only contains active probes
  362. UINT32 numUsedProbes = (UINT32)mProbeMap.size();
  363. if(numUsedProbes > mCoeffBufferSize)
  364. resizeCoefficientTexture(std::max(32U, numUsedProbes * 2));
  365. UINT32 numProbeUpdates = 0;
  366. for (; mFirstDirtyProbe < (UINT32)mProbeInfos.size(); ++mFirstDirtyProbe)
  367. {
  368. LightProbeInfo& probeInfo = mProbeInfos[mFirstDirtyProbe];
  369. if(probeInfo.flags == LightProbeFlags::Dirty)
  370. {
  371. TEXTURE_DESC cubemapDesc;
  372. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  373. cubemapDesc.format = PF_RGBA16F;
  374. cubemapDesc.width = 256; // Note: Test different sizes and their effect on quality
  375. cubemapDesc.height = 256;
  376. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  377. SPtr<Texture> cubemap = Texture::create(cubemapDesc);
  378. Vector3 localPos = mProbePositions[mFirstDirtyProbe];
  379. const Transform& tfrm = getTransform();
  380. const Vector3& position = tfrm.getPosition();
  381. const Quaternion& rotation = tfrm.getRotation();
  382. Vector3 transformedPos = rotation.rotate(localPos) + position;
  383. gRenderer()->captureSceneCubeMap(cubemap, transformedPos, CaptureSettings());
  384. gIBLUtility().filterCubemapForIrradiance(cubemap, mCoefficients, probeInfo.bufferIdx);
  385. probeInfo.flags = LightProbeFlags::Clean;
  386. numProbeUpdates++;
  387. }
  388. if (maxProbes != 0 && numProbeUpdates >= maxProbes)
  389. break;
  390. }
  391. gRenderer()->notifyLightProbeVolumeUpdated(this);
  392. return mFirstDirtyProbe == (UINT32)mProbeInfos.size();
  393. }
  394. void LightProbeVolume::syncToCore(const CoreSyncData& data)
  395. {
  396. char* dataPtr = (char*)data.getBuffer();
  397. bool oldIsActive = mActive;
  398. dataPtr = syncActorFrom(dataPtr);
  399. UINT32 numDirtyProbes, numRemovedProbes;
  400. dataPtr = rttiReadElem(numDirtyProbes, dataPtr);
  401. dataPtr = rttiReadElem(numRemovedProbes, dataPtr);
  402. for (UINT32 i = 0; i < numDirtyProbes; ++i)
  403. {
  404. UINT32 handle;
  405. dataPtr = rttiReadElem(handle, dataPtr);
  406. Vector3 position;
  407. dataPtr = rttiReadElem(position, dataPtr);
  408. LightProbeFlags flags;
  409. dataPtr = rttiReadElem(flags, dataPtr);
  410. auto iterFind = mProbeMap.find(handle);
  411. if(iterFind != mProbeMap.end())
  412. {
  413. // Update existing probe information
  414. UINT32 compactIdx = iterFind->second;
  415. mProbeInfos[compactIdx].flags = LightProbeFlags::Dirty;
  416. mProbePositions[compactIdx] = position;
  417. mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
  418. }
  419. else // Add a new probe
  420. {
  421. // Empty slots always start at a specific index because we always move them to the back of the array
  422. UINT32 emptyProbeStartIdx = (UINT32)mProbeMap.size();
  423. UINT32 numProbes = (UINT32)mProbeInfos.size();
  424. // Find an empty slot to place the probe information at
  425. UINT32 compactIdx = -1;
  426. for(UINT32 j = emptyProbeStartIdx; j < numProbes; ++j)
  427. {
  428. if(mProbeInfos[j].flags == LightProbeFlags::Empty)
  429. {
  430. compactIdx = j;
  431. break;
  432. }
  433. }
  434. // Found an empty slot
  435. if (compactIdx == (UINT32)-1)
  436. {
  437. compactIdx = (UINT32)mProbeInfos.size();
  438. LightProbeInfo info;
  439. info.flags = LightProbeFlags::Dirty;
  440. info.bufferIdx = compactIdx;
  441. info.handle = handle;
  442. mProbeInfos.push_back(info);
  443. mProbePositions.push_back(position);
  444. }
  445. else // No empty slot, add a new one
  446. {
  447. LightProbeInfo& info = mProbeInfos[compactIdx];
  448. info.flags = LightProbeFlags::Dirty;
  449. info.handle = handle;
  450. mProbePositions[compactIdx] = position;
  451. }
  452. mProbeMap[handle] = compactIdx;
  453. mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
  454. }
  455. }
  456. // Mark slots for removed probes as empty, and move them back to the end of the array
  457. for (UINT32 i = 0; i < numRemovedProbes; ++i)
  458. {
  459. UINT32 idx;
  460. dataPtr = rttiReadElem(idx, dataPtr);
  461. auto iterFind = mProbeMap.find(idx);
  462. if(iterFind != mProbeMap.end())
  463. {
  464. UINT32 compactIdx = iterFind->second;
  465. LightProbeInfo& info = mProbeInfos[compactIdx];
  466. info.flags = LightProbeFlags::Empty;
  467. // Move the empty info to the back of the array so all non-empty probes are contiguous
  468. // Search from back to current index, and find first non-empty probe to switch switch
  469. UINT32 lastSearchIdx = (UINT32)mProbeInfos.size() - 1;
  470. while (lastSearchIdx >= (UINT32)compactIdx)
  471. {
  472. LightProbeFlags flags = mProbeInfos[lastSearchIdx].flags;
  473. if (flags != LightProbeFlags::Empty)
  474. {
  475. std::swap(mProbeInfos[i], mProbeInfos[lastSearchIdx]);
  476. std::swap(mProbePositions[i], mProbePositions[lastSearchIdx]);
  477. mProbeMap[mProbeInfos[lastSearchIdx].handle] = i;
  478. break;
  479. }
  480. lastSearchIdx--;
  481. }
  482. mProbeMap.erase(iterFind);
  483. }
  484. }
  485. if (oldIsActive != mActive)
  486. {
  487. if (mActive)
  488. gRenderer()->notifyLightProbeVolumeAdded(this);
  489. else
  490. gRenderer()->notifyLightProbeVolumeRemoved(this);
  491. }
  492. }
  493. void LightProbeVolume::getProbeCoefficients(Vector<LightProbeCoefficientInfo>& output) const
  494. {
  495. UINT32 numActiveProbes = (UINT32)mProbeMap.size();
  496. if (numActiveProbes == 0)
  497. return;
  498. output.resize(numActiveProbes);
  499. LightProbeSHCoefficients* coefficients = bs_stack_alloc<LightProbeSHCoefficients>(numActiveProbes);
  500. SPtr<PixelData> coeffData = mCoefficients->getProperties().allocBuffer(0, 0);
  501. mCoefficients->readData(*coeffData);
  502. UINT32 probesPerRow = coeffData->getWidth() / 9;
  503. UINT32 probeIdx = 0;
  504. for(UINT32 y = 0; y < coeffData->getHeight(); ++y)
  505. {
  506. for(UINT32 x = 0; x < probesPerRow; ++x)
  507. {
  508. if(probeIdx >= numActiveProbes)
  509. break;
  510. for(UINT32 i = 0; i < 9; i++)
  511. {
  512. Color value = coeffData->getColorAt(x * 9, y);
  513. coefficients[probeIdx].coeffsR[i] = value.r;
  514. coefficients[probeIdx].coeffsG[i] = value.g;
  515. coefficients[probeIdx].coeffsB[i] = value.b;
  516. }
  517. probeIdx++;
  518. }
  519. }
  520. for(UINT32 i = 0; i < numActiveProbes; ++i)
  521. {
  522. output[i].coefficients = coefficients[mProbeInfos[i].bufferIdx];
  523. output[i].handle = mProbeInfos[i].handle;
  524. }
  525. bs_stack_free(coefficients);
  526. }
  527. void LightProbeVolume::resizeCoefficientTexture(UINT32 count)
  528. {
  529. Vector2I texSize = IBLUtility::getSHCoeffTextureSize(count, 3);
  530. TEXTURE_DESC desc;
  531. desc.width = (UINT32)texSize.x;
  532. desc.height = (UINT32)texSize.y;
  533. desc.usage = TU_LOADSTORE | TU_RENDERTARGET;
  534. desc.format = PF_RGBA32F;
  535. SPtr<Texture> newTexture = Texture::create(desc);
  536. newTexture->clear(Color::ZERO);
  537. if (mCoefficients)
  538. mCoefficients->copy(newTexture);
  539. mCoefficients = newTexture;
  540. mCoeffBufferSize = count;
  541. }
  542. }}