BsRenderBeastIBLUtility.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsRenderBeastIBLUtility.h"
  4. #include "Image/BsTexture.h"
  5. #include "Material/BsGpuParamsSet.h"
  6. #include "Renderer/BsRendererUtility.h"
  7. #include "RenderAPI/BsGpuBuffer.h"
  8. #include "BsRenderBeast.h"
  9. namespace bs { namespace ct
  10. {
  11. ReflectionCubeDownsampleParamDef gReflectionCubeDownsampleParamDef;
  12. ReflectionCubeDownsampleMat::ReflectionCubeDownsampleMat()
  13. {
  14. mParamBuffer = gReflectionCubeDownsampleParamDef.createBuffer();
  15. mParams->setParamBlockBuffer("Input", mParamBuffer);
  16. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  17. }
  18. void ReflectionCubeDownsampleMat::execute(
  19. const SPtr<Texture>& source,
  20. UINT32 face,
  21. UINT32 mip,
  22. const SPtr<RenderTarget>& target)
  23. {
  24. RenderAPI& rapi = RenderAPI::instance();
  25. const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
  26. gReflectionCubeDownsampleParamDef.gCubeFace.set(mParamBuffer, face);
  27. if(rapiInfo.isFlagSet(RenderAPIFeatureFlag::TextureViews))
  28. {
  29. mInputTexture.set(source, TextureSurface(mip, 1, 0, 6));
  30. gReflectionCubeDownsampleParamDef.gMipLevel.set(mParamBuffer, 0);
  31. }
  32. else
  33. {
  34. mInputTexture.set(source);
  35. gReflectionCubeDownsampleParamDef.gMipLevel.set(mParamBuffer, mip);
  36. }
  37. rapi.setRenderTarget(target);
  38. bind();
  39. gRendererUtility().drawScreenQuad();
  40. }
  41. const UINT32 ReflectionCubeImportanceSampleMat::NUM_SAMPLES = 1024;
  42. ReflectionCubeImportanceSampleParamDef gReflectionCubeImportanceSampleParamDef;
  43. ReflectionCubeImportanceSampleMat::ReflectionCubeImportanceSampleMat()
  44. {
  45. mParamBuffer = gReflectionCubeImportanceSampleParamDef.createBuffer();
  46. mParams->setParamBlockBuffer("Input", mParamBuffer);
  47. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  48. }
  49. void ReflectionCubeImportanceSampleMat::_initDefines(ShaderDefines& defines)
  50. {
  51. defines.set("NUM_SAMPLES", NUM_SAMPLES);
  52. }
  53. void ReflectionCubeImportanceSampleMat::execute(const SPtr<Texture>& source, UINT32 face, UINT32 mip,
  54. const SPtr<RenderTarget>& target)
  55. {
  56. mInputTexture.set(source);
  57. gReflectionCubeImportanceSampleParamDef.gCubeFace.set(mParamBuffer, face);
  58. gReflectionCubeImportanceSampleParamDef.gMipLevel.set(mParamBuffer, mip);
  59. gReflectionCubeImportanceSampleParamDef.gNumMips.set(mParamBuffer, source->getProperties().getNumMipmaps() + 1);
  60. float width = (float)source->getProperties().getWidth();
  61. float height = (float)source->getProperties().getHeight();
  62. // First part of the equation for determining mip level to sample from.
  63. // See http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html
  64. float mipFactor = 0.5f * std::log2(width * height / NUM_SAMPLES);
  65. gReflectionCubeImportanceSampleParamDef.gPrecomputedMipFactor.set(mParamBuffer, mipFactor);
  66. RenderAPI& rapi = RenderAPI::instance();
  67. rapi.setRenderTarget(target);
  68. bind();
  69. gRendererUtility().drawScreenQuad();
  70. }
  71. IrradianceComputeSHParamDef gIrradianceComputeSHParamDef;
  72. // TILE_WIDTH * TILE_HEIGHT must be pow2 because of parallel reduction algorithm
  73. const static UINT32 TILE_WIDTH = 8;
  74. const static UINT32 TILE_HEIGHT = 8;
  75. // For very small textures this should be reduced so number of launched threads can properly utilize GPU cores
  76. const static UINT32 PIXELS_PER_THREAD = 4;
  77. IrradianceComputeSHMat::IrradianceComputeSHMat()
  78. {
  79. mParamBuffer = gIrradianceComputeSHParamDef.createBuffer();
  80. mParams->setParamBlockBuffer("Params", mParamBuffer);
  81. mParams->getTextureParam(GPT_COMPUTE_PROGRAM, "gInputTex", mInputTexture);
  82. mParams->getBufferParam(GPT_COMPUTE_PROGRAM, "gOutput", mOutputBuffer);
  83. }
  84. void IrradianceComputeSHMat::_initDefines(ShaderDefines& defines)
  85. {
  86. defines.set("TILE_WIDTH", TILE_WIDTH);
  87. defines.set("TILE_HEIGHT", TILE_HEIGHT);
  88. defines.set("PIXELS_PER_THREAD", PIXELS_PER_THREAD);
  89. }
  90. void IrradianceComputeSHMat::execute(const SPtr<Texture>& source, UINT32 face, const SPtr<GpuBuffer>& output)
  91. {
  92. auto& props = source->getProperties();
  93. UINT32 faceSize = props.getWidth();
  94. assert(faceSize == props.getHeight());
  95. Vector2I dispatchSize;
  96. dispatchSize.x = Math::divideAndRoundUp(faceSize, TILE_WIDTH * PIXELS_PER_THREAD);
  97. dispatchSize.y = Math::divideAndRoundUp(faceSize, TILE_HEIGHT * PIXELS_PER_THREAD);
  98. mInputTexture.set(source);
  99. gIrradianceComputeSHParamDef.gCubeFace.set(mParamBuffer, face);
  100. gIrradianceComputeSHParamDef.gFaceSize.set(mParamBuffer, source->getProperties().getWidth());
  101. gIrradianceComputeSHParamDef.gDispatchSize.set(mParamBuffer, dispatchSize);
  102. mOutputBuffer.set(output);
  103. RenderAPI& rapi = RenderAPI::instance();
  104. bind();
  105. rapi.dispatchCompute(dispatchSize.x, dispatchSize.y);
  106. }
  107. SPtr<GpuBuffer> IrradianceComputeSHMat::createOutputBuffer(const SPtr<Texture>& source, UINT32& numCoeffSets)
  108. {
  109. auto& props = source->getProperties();
  110. UINT32 faceSize = props.getWidth();
  111. assert(faceSize == props.getHeight());
  112. Vector2I dispatchSize;
  113. dispatchSize.x = Math::divideAndRoundUp(faceSize, TILE_WIDTH * PIXELS_PER_THREAD);
  114. dispatchSize.y = Math::divideAndRoundUp(faceSize, TILE_HEIGHT * PIXELS_PER_THREAD);
  115. numCoeffSets = dispatchSize.x * dispatchSize.y * 6;
  116. GPU_BUFFER_DESC bufferDesc;
  117. bufferDesc.type = GBT_STRUCTURED;
  118. bufferDesc.elementCount = numCoeffSets;
  119. bufferDesc.format = BF_UNKNOWN;
  120. bufferDesc.randomGpuWrite = true;
  121. if(mVariation.getInt("SH_ORDER") == 3)
  122. bufferDesc.elementSize = sizeof(SHCoeffsAndWeight3);
  123. else
  124. bufferDesc.elementSize = sizeof(SHCoeffsAndWeight5);
  125. return GpuBuffer::create(bufferDesc);
  126. }
  127. IrradianceComputeSHMat* IrradianceComputeSHMat::getVariation(int order)
  128. {
  129. if (order == 3)
  130. return get(getVariation<3>());
  131. return get(getVariation<5>());
  132. }
  133. IrradianceComputeSHFragParamDef gIrradianceComputeSHFragParamDef;
  134. IrradianceComputeSHFragMat::IrradianceComputeSHFragMat()
  135. {
  136. mParamBuffer = gIrradianceComputeSHFragParamDef.createBuffer();
  137. mParams->setParamBlockBuffer("Params", mParamBuffer);
  138. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  139. }
  140. void IrradianceComputeSHFragMat::execute(const SPtr<Texture>& source, UINT32 face, UINT32 coefficientIdx,
  141. const SPtr<RenderTarget>& output)
  142. {
  143. // Set parameters
  144. mInputTexture.set(source);
  145. gIrradianceComputeSHFragParamDef.gCubeFace.set(mParamBuffer, face);
  146. gIrradianceComputeSHFragParamDef.gFaceSize.set(mParamBuffer, source->getProperties().getWidth());
  147. gIrradianceComputeSHFragParamDef.gCoeffEntryIdx.set(mParamBuffer, coefficientIdx / 4);
  148. gIrradianceComputeSHFragParamDef.gCoeffComponentIdx.set(mParamBuffer, coefficientIdx % 4);
  149. // Render
  150. RenderAPI& rapi = RenderAPI::instance();
  151. rapi.setRenderTarget(output);
  152. bind();
  153. gRendererUtility().drawScreenQuad();
  154. rapi.setRenderTarget(nullptr);
  155. }
  156. POOLED_RENDER_TEXTURE_DESC IrradianceComputeSHFragMat::getOutputDesc(const SPtr<Texture>& input)
  157. {
  158. auto& props = input->getProperties();
  159. return POOLED_RENDER_TEXTURE_DESC::createCube(PF_RGBA16F, props.getWidth(), props.getHeight(), TU_RENDERTARGET);
  160. }
  161. IrradianceAccumulateSHParamDef gIrradianceAccumulateSHParamDef;
  162. IrradianceAccumulateSHMat::IrradianceAccumulateSHMat()
  163. {
  164. mParamBuffer = gIrradianceAccumulateSHParamDef.createBuffer();
  165. mParams->setParamBlockBuffer("Params", mParamBuffer);
  166. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  167. }
  168. void IrradianceAccumulateSHMat::execute(const SPtr<Texture>& source, UINT32 face, UINT32 sourceMip,
  169. const SPtr<RenderTarget>& output)
  170. {
  171. // Set parameters
  172. mInputTexture.set(source);
  173. auto& props = source->getProperties();
  174. Vector2 halfPixel(0.5f / props.getWidth(), 0.5f / props.getHeight());
  175. gIrradianceAccumulateSHParamDef.gCubeFace.set(mParamBuffer, face);
  176. gIrradianceAccumulateSHParamDef.gCubeMip.set(mParamBuffer, sourceMip);
  177. gIrradianceAccumulateSHParamDef.gHalfPixel.set(mParamBuffer, halfPixel);
  178. // Render
  179. RenderAPI& rapi = RenderAPI::instance();
  180. rapi.setRenderTarget(output);
  181. bind();
  182. gRendererUtility().drawScreenQuad();
  183. rapi.setRenderTarget(nullptr);
  184. }
  185. POOLED_RENDER_TEXTURE_DESC IrradianceAccumulateSHMat::getOutputDesc(const SPtr<Texture>& input)
  186. {
  187. auto& props = input->getProperties();
  188. // Assuming it's a cubemap
  189. UINT32 size = std::max(1U, (UINT32)(props.getWidth() * 0.5f));
  190. return POOLED_RENDER_TEXTURE_DESC::createCube(PF_RGBA16F, size, size, TU_RENDERTARGET);
  191. }
  192. IrradianceAccumulateCubeSHMat::IrradianceAccumulateCubeSHMat()
  193. {
  194. mParamBuffer = gIrradianceAccumulateSHParamDef.createBuffer();
  195. mParams->setParamBlockBuffer("Params", mParamBuffer);
  196. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  197. }
  198. void IrradianceAccumulateCubeSHMat::execute(const SPtr<Texture>& source, UINT32 sourceMip, const Vector2I& outputOffset,
  199. UINT32 coefficientIdx, const SPtr<RenderTarget>& output)
  200. {
  201. // Set parameters
  202. mInputTexture.set(source);
  203. auto& props = source->getProperties();
  204. Vector2 halfPixel(0.5f / props.getWidth(), 0.5f / props.getHeight());
  205. gIrradianceAccumulateSHParamDef.gCubeFace.set(mParamBuffer, 0);
  206. gIrradianceAccumulateSHParamDef.gCubeMip.set(mParamBuffer, sourceMip);
  207. gIrradianceAccumulateSHParamDef.gHalfPixel.set(mParamBuffer, halfPixel);
  208. auto& rtProps = output->getProperties();
  209. // Render to just one pixel corresponding to the coefficient
  210. Rect2 viewRect;
  211. viewRect.x = (outputOffset.x + coefficientIdx) / (float)rtProps.width;
  212. viewRect.y = outputOffset.y / (float)rtProps.height;
  213. viewRect.width = 1.0f / rtProps.width;
  214. viewRect.height = 1.0f / rtProps.height;
  215. // Render
  216. RenderAPI& rapi = RenderAPI::instance();
  217. rapi.setRenderTarget(output);
  218. rapi.setViewport(viewRect);
  219. bind();
  220. gRendererUtility().drawScreenQuad();
  221. rapi.setRenderTarget(nullptr);
  222. rapi.setViewport(Rect2(0, 0, 1, 1));
  223. }
  224. POOLED_RENDER_TEXTURE_DESC IrradianceAccumulateCubeSHMat::getOutputDesc()
  225. {
  226. return POOLED_RENDER_TEXTURE_DESC::create2D(PF_RGBA32F, 9, 1, TU_RENDERTARGET);
  227. }
  228. IrradianceReduceSHParamDef gIrradianceReduceSHParamDef;
  229. IrradianceReduceSHMat::IrradianceReduceSHMat()
  230. {
  231. mParamBuffer = gIrradianceReduceSHParamDef.createBuffer();
  232. mParams->setParamBlockBuffer("Params", mParamBuffer);
  233. mParams->getBufferParam(GPT_COMPUTE_PROGRAM, "gInput", mInputBuffer);
  234. mParams->getLoadStoreTextureParam(GPT_COMPUTE_PROGRAM, "gOutput", mOutputTexture);
  235. }
  236. void IrradianceReduceSHMat::execute(const SPtr<GpuBuffer>& source, UINT32 numCoeffSets,
  237. const SPtr<Texture>& output, UINT32 outputIdx)
  238. {
  239. UINT32 shOrder = (UINT32)mVariation.getInt("SH_ORDER");
  240. Vector2I outputCoords = IBLUtility::getSHCoeffXYFromIdx(outputIdx, shOrder);
  241. gIrradianceReduceSHParamDef.gOutputIdx.set(mParamBuffer, outputCoords);
  242. gIrradianceReduceSHParamDef.gNumEntries.set(mParamBuffer, numCoeffSets);
  243. mInputBuffer.set(source);
  244. mOutputTexture.set(output);
  245. bind();
  246. RenderAPI& rapi = RenderAPI::instance();
  247. rapi.dispatchCompute(1);
  248. }
  249. SPtr<Texture> IrradianceReduceSHMat::createOutputTexture(UINT32 numCoeffSets)
  250. {
  251. UINT32 shOrder = (UINT32)mVariation.getInt("SH_ORDER");
  252. Vector2I size = IBLUtility::getSHCoeffTextureSize(numCoeffSets, shOrder);
  253. TEXTURE_DESC textureDesc;
  254. textureDesc.width = (UINT32)size.x;
  255. textureDesc.height = (UINT32)size.y;
  256. textureDesc.format = PF_RGBA32F;
  257. textureDesc.usage = TU_STATIC | TU_LOADSTORE;
  258. return Texture::create(textureDesc);
  259. }
  260. IrradianceReduceSHMat* IrradianceReduceSHMat::getVariation(int order)
  261. {
  262. if (order == 3)
  263. return get(getVariation<3>());
  264. return get(getVariation<5>());
  265. }
  266. IrradianceProjectSHParamDef gIrradianceProjectSHParamDef;
  267. IrradianceProjectSHMat::IrradianceProjectSHMat()
  268. {
  269. mParamBuffer = gIrradianceProjectSHParamDef.createBuffer();
  270. mParams->setParamBlockBuffer("Params", mParamBuffer);
  271. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSHCoeffs", mInputTexture);
  272. }
  273. void IrradianceProjectSHMat::execute(const SPtr<Texture>& shCoeffs, UINT32 face, const SPtr<RenderTarget>& target)
  274. {
  275. gIrradianceProjectSHParamDef.gCubeFace.set(mParamBuffer, face);
  276. mInputTexture.set(shCoeffs);
  277. RenderAPI& rapi = RenderAPI::instance();
  278. rapi.setRenderTarget(target);
  279. bind();
  280. gRendererUtility().drawScreenQuad();
  281. }
  282. void RenderBeastIBLUtility::filterCubemapForSpecular(const SPtr<Texture>& cubemap, const SPtr<Texture>& scratch) const
  283. {
  284. auto& props = cubemap->getProperties();
  285. SPtr<Texture> scratchCubemap = scratch;
  286. if (scratchCubemap == nullptr)
  287. {
  288. TEXTURE_DESC cubemapDesc;
  289. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  290. cubemapDesc.format = props.getFormat();
  291. cubemapDesc.width = props.getWidth();
  292. cubemapDesc.height = props.getHeight();
  293. cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
  294. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  295. scratchCubemap = Texture::create(cubemapDesc);
  296. }
  297. // We sample the cubemaps using importance sampling to generate roughness
  298. UINT32 numMips = props.getNumMipmaps();
  299. // Before importance sampling the cubemaps we first create box filtered versions for each mip level. This helps fix
  300. // the aliasing artifacts that would otherwise be noticeable on importance sampled cubemaps. The aliasing happens
  301. // because:
  302. // 1. We use the same random samples for all pixels, which appears to duplicate reflections instead of creating
  303. // noise, which is usually more acceptable
  304. // 2. Even if we were to use fully random samples we would need a lot to avoid noticeable noise, which isn't
  305. // practical
  306. // Copy base mip level to scratch cubemap
  307. for (UINT32 face = 0; face < 6; face++)
  308. {
  309. TEXTURE_COPY_DESC copyDesc;
  310. copyDesc.srcFace = face;
  311. copyDesc.dstFace = face;
  312. cubemap->copy(scratchCubemap, copyDesc);
  313. }
  314. // Fill out remaining scratch mip levels by downsampling
  315. for (UINT32 mip = 1; mip < numMips; mip++)
  316. {
  317. UINT32 sourceMip = mip - 1;
  318. downsampleCubemap(scratchCubemap, sourceMip, scratchCubemap, mip);
  319. }
  320. // Importance sample
  321. for (UINT32 mip = 1; mip < numMips; mip++)
  322. {
  323. for (UINT32 face = 0; face < 6; face++)
  324. {
  325. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  326. cubeFaceRTDesc.colorSurfaces[0].texture = cubemap;
  327. cubeFaceRTDesc.colorSurfaces[0].face = face;
  328. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  329. cubeFaceRTDesc.colorSurfaces[0].mipLevel = mip;
  330. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  331. ReflectionCubeImportanceSampleMat* material = ReflectionCubeImportanceSampleMat::get();
  332. material->execute(scratchCubemap, face, mip, target);
  333. }
  334. }
  335. RenderAPI& rapi = RenderAPI::instance();
  336. rapi.setRenderTarget(nullptr);
  337. }
  338. bool supportsComputeSH()
  339. {
  340. return gRenderBeast()->getFeatureSet() == RenderBeastFeatureSet::Desktop;
  341. }
  342. void RenderBeastIBLUtility::filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<Texture>& output) const
  343. {
  344. SPtr<Texture> coeffTexture;
  345. if(supportsComputeSH())
  346. {
  347. IrradianceComputeSHMat* shCompute = IrradianceComputeSHMat::getVariation(5);
  348. IrradianceReduceSHMat* shReduce = IrradianceReduceSHMat::getVariation(5);
  349. UINT32 numCoeffSets;
  350. SPtr<GpuBuffer> coeffSetBuffer = shCompute->createOutputBuffer(cubemap, numCoeffSets);
  351. for (UINT32 face = 0; face < 6; face++)
  352. shCompute->execute(cubemap, face, coeffSetBuffer);
  353. coeffTexture = shReduce->createOutputTexture(1);
  354. shReduce->execute(coeffSetBuffer, numCoeffSets, coeffTexture, 0);
  355. }
  356. else
  357. {
  358. GpuResourcePool& resPool = GpuResourcePool::instance();
  359. SPtr<PooledRenderTexture> finalCoeffs = resPool.get(IrradianceAccumulateCubeSHMat::getOutputDesc());
  360. filterCubemapForIrradianceNonCompute(cubemap, 0, finalCoeffs->renderTexture);
  361. coeffTexture = finalCoeffs->texture;
  362. }
  363. IrradianceProjectSHMat* shProject = IrradianceProjectSHMat::get();
  364. for (UINT32 face = 0; face < 6; face++)
  365. {
  366. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  367. cubeFaceRTDesc.colorSurfaces[0].texture = output;
  368. cubeFaceRTDesc.colorSurfaces[0].face = face;
  369. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  370. cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
  371. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  372. shProject->execute(coeffTexture, face, target);
  373. }
  374. }
  375. void RenderBeastIBLUtility::filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<Texture>& output,
  376. UINT32 outputIdx) const
  377. {
  378. if(supportsComputeSH())
  379. {
  380. IrradianceComputeSHMat* shCompute = IrradianceComputeSHMat::getVariation(3);
  381. IrradianceReduceSHMat* shReduce = IrradianceReduceSHMat::getVariation(3);
  382. UINT32 numCoeffSets;
  383. SPtr<GpuBuffer> coeffSetBuffer = shCompute->createOutputBuffer(cubemap, numCoeffSets);
  384. for (UINT32 face = 0; face < 6; face++)
  385. shCompute->execute(cubemap, face, coeffSetBuffer);
  386. shReduce->execute(coeffSetBuffer, numCoeffSets, output, outputIdx);
  387. }
  388. else
  389. {
  390. RENDER_TEXTURE_DESC rtDesc;
  391. rtDesc.colorSurfaces[0].texture = output;
  392. SPtr<RenderTexture> target = RenderTexture::create(rtDesc);
  393. filterCubemapForIrradianceNonCompute(cubemap, outputIdx, target);
  394. }
  395. }
  396. void RenderBeastIBLUtility::scaleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst,
  397. UINT32 dstMip) const
  398. {
  399. auto& srcProps = src->getProperties();
  400. auto& dstProps = dst->getProperties();
  401. SPtr<Texture> scratchTex = src;
  402. int sizeSrcLog2 = (int)log2((float)srcProps.getWidth());
  403. int sizeDstLog2 = (int)log2((float)dstProps.getWidth());
  404. int sizeLog2Diff = sizeSrcLog2 - sizeDstLog2;
  405. // If size difference is greater than one mip-level and we're downscaling, we need to generate intermediate mip
  406. // levels
  407. if(sizeLog2Diff > 1)
  408. {
  409. UINT32 mipSize = (UINT32)exp2((float)(sizeSrcLog2 - 1));
  410. UINT32 numDownsamples = sizeLog2Diff - 1;
  411. TEXTURE_DESC cubemapDesc;
  412. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  413. cubemapDesc.format = srcProps.getFormat();
  414. cubemapDesc.width = mipSize;
  415. cubemapDesc.height = mipSize;
  416. cubemapDesc.numMips = numDownsamples - 1;
  417. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  418. scratchTex = Texture::create(cubemapDesc);
  419. downsampleCubemap(src, srcMip, scratchTex, 0);
  420. for(UINT32 i = 0; i < cubemapDesc.numMips; i++)
  421. downsampleCubemap(scratchTex, i, scratchTex, i + 1);
  422. srcMip = cubemapDesc.numMips;
  423. }
  424. // Same size so just copy
  425. if(sizeSrcLog2 == sizeDstLog2)
  426. {
  427. for (UINT32 face = 0; face < 6; face++)
  428. {
  429. TEXTURE_COPY_DESC copyDesc;
  430. copyDesc.srcFace = face;
  431. copyDesc.srcMip = srcMip;
  432. copyDesc.dstFace = face;
  433. copyDesc.dstMip = dstMip;
  434. src->copy(dst, copyDesc);
  435. }
  436. }
  437. else
  438. downsampleCubemap(scratchTex, srcMip, dst, dstMip);
  439. }
  440. void RenderBeastIBLUtility::downsampleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst,
  441. UINT32 dstMip)
  442. {
  443. for (UINT32 face = 0; face < 6; face++)
  444. {
  445. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  446. cubeFaceRTDesc.colorSurfaces[0].texture = dst;
  447. cubeFaceRTDesc.colorSurfaces[0].face = face;
  448. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  449. cubeFaceRTDesc.colorSurfaces[0].mipLevel = dstMip;
  450. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  451. ReflectionCubeDownsampleMat* material = ReflectionCubeDownsampleMat::get();
  452. material->execute(src, face, srcMip, target);
  453. }
  454. }
  455. void RenderBeastIBLUtility::filterCubemapForIrradianceNonCompute(const SPtr<Texture>& cubemap, UINT32 outputIdx,
  456. const SPtr<RenderTexture>& output)
  457. {
  458. static const UINT32 NUM_COEFFS = 9;
  459. GpuResourcePool& resPool = GpuResourcePool::instance();
  460. IrradianceComputeSHFragMat* shCompute = IrradianceComputeSHFragMat::get();
  461. IrradianceAccumulateSHMat* shAccum = IrradianceAccumulateSHMat::get();
  462. IrradianceAccumulateCubeSHMat* shAccumCube = IrradianceAccumulateCubeSHMat::get();
  463. for(UINT32 coeff = 0; coeff < NUM_COEFFS; ++coeff)
  464. {
  465. SPtr<PooledRenderTexture> coeffsTex = resPool.get(shCompute->getOutputDesc(cubemap));
  466. // Generate SH coefficients and weights per-texel
  467. for(UINT32 face = 0; face < 6; face++)
  468. {
  469. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  470. cubeFaceRTDesc.colorSurfaces[0].texture = coeffsTex->texture;
  471. cubeFaceRTDesc.colorSurfaces[0].face = face;
  472. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  473. cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
  474. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  475. shCompute->execute(cubemap, face, coeff, target);
  476. }
  477. // Downsample, summing up coefficients and weights all the way down to 1x1
  478. auto& sourceProps = cubemap->getProperties();
  479. UINT32 numMips = PixelUtil::getMaxMipmaps(sourceProps.getWidth(), sourceProps.getHeight(), 1,
  480. sourceProps.getFormat());
  481. SPtr<PooledRenderTexture> downsampleInput = coeffsTex;
  482. coeffsTex = nullptr;
  483. for(UINT32 mip = 0; mip < numMips; mip++)
  484. {
  485. SPtr<PooledRenderTexture> accumCoeffsTex = resPool.get(shAccum->getOutputDesc(downsampleInput->texture));
  486. for(UINT32 face = 0; face < 6; face++)
  487. {
  488. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  489. cubeFaceRTDesc.colorSurfaces[0].texture = accumCoeffsTex->texture;
  490. cubeFaceRTDesc.colorSurfaces[0].face = face;
  491. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  492. cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
  493. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  494. shAccum->execute(downsampleInput->texture, face, 0, target);
  495. }
  496. downsampleInput = accumCoeffsTex;
  497. }
  498. // Sum up all the faces and write the coefficient to the final texture
  499. Vector2I outputOffset = getSHCoeffXYFromIdx(outputIdx, 3);
  500. shAccumCube->execute(downsampleInput->texture, 0, outputOffset, coeff, output);
  501. }
  502. }
  503. }}