BsRenderBeastIBLUtility.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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.gCoeffIdx.set(mParamBuffer, coefficientIdx);
  148. // Render
  149. RenderAPI& rapi = RenderAPI::instance();
  150. rapi.setRenderTarget(output);
  151. bind();
  152. gRendererUtility().drawScreenQuad();
  153. rapi.setRenderTarget(nullptr);
  154. }
  155. POOLED_RENDER_TEXTURE_DESC IrradianceComputeSHFragMat::getOutputDesc(const SPtr<Texture>& input)
  156. {
  157. auto& props = input->getProperties();
  158. return POOLED_RENDER_TEXTURE_DESC::createCube(PF_RGBA16F, props.getWidth(), props.getHeight(), TU_RENDERTARGET);
  159. }
  160. IrradianceAccumulateSHParamDef gIrradianceAccumulateSHParamDef;
  161. IrradianceAccumulateSHMat::IrradianceAccumulateSHMat()
  162. {
  163. mParamBuffer = gIrradianceAccumulateSHParamDef.createBuffer();
  164. mParams->setParamBlockBuffer("Params", mParamBuffer);
  165. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  166. }
  167. void IrradianceAccumulateSHMat::execute(const SPtr<Texture>& source, UINT32 face, UINT32 sourceMip,
  168. const SPtr<RenderTarget>& output)
  169. {
  170. // Set parameters
  171. mInputTexture.set(source);
  172. auto& props = source->getProperties();
  173. Vector2 halfPixel(0.5f / props.getWidth(), 0.5f / props.getHeight());
  174. gIrradianceAccumulateSHParamDef.gCubeFace.set(mParamBuffer, face);
  175. gIrradianceAccumulateSHParamDef.gCubeMip.set(mParamBuffer, sourceMip);
  176. gIrradianceAccumulateSHParamDef.gHalfPixel.set(mParamBuffer, halfPixel);
  177. // Render
  178. RenderAPI& rapi = RenderAPI::instance();
  179. rapi.setRenderTarget(output);
  180. bind();
  181. gRendererUtility().drawScreenQuad();
  182. rapi.setRenderTarget(nullptr);
  183. }
  184. POOLED_RENDER_TEXTURE_DESC IrradianceAccumulateSHMat::getOutputDesc(const SPtr<Texture>& input)
  185. {
  186. auto& props = input->getProperties();
  187. // Assuming it's a cubemap
  188. UINT32 size = std::max(1U, (UINT32)(props.getWidth() * 0.5f));
  189. return POOLED_RENDER_TEXTURE_DESC::createCube(PF_RGBA16F, size, size, TU_RENDERTARGET);
  190. }
  191. IrradianceAccumulateCubeSHMat::IrradianceAccumulateCubeSHMat()
  192. {
  193. mParamBuffer = gIrradianceAccumulateSHParamDef.createBuffer();
  194. mParams->setParamBlockBuffer("Params", mParamBuffer);
  195. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  196. }
  197. void IrradianceAccumulateCubeSHMat::execute(const SPtr<Texture>& source, UINT32 sourceMip, const Vector2I& outputOffset,
  198. UINT32 coefficientIdx, const SPtr<RenderTarget>& output)
  199. {
  200. // Set parameters
  201. mInputTexture.set(source);
  202. auto& props = source->getProperties();
  203. Vector2 halfPixel(0.5f / props.getWidth(), 0.5f / props.getHeight());
  204. gIrradianceAccumulateSHParamDef.gCubeFace.set(mParamBuffer, 0);
  205. gIrradianceAccumulateSHParamDef.gCubeMip.set(mParamBuffer, sourceMip);
  206. gIrradianceAccumulateSHParamDef.gHalfPixel.set(mParamBuffer, halfPixel);
  207. auto& rtProps = output->getProperties();
  208. // Render to just one pixel corresponding to the coefficient
  209. Rect2 viewRect;
  210. viewRect.x = (outputOffset.x + coefficientIdx) / (float)rtProps.width;
  211. viewRect.y = outputOffset.y / (float)rtProps.height;
  212. viewRect.width = 1.0f / rtProps.width;
  213. viewRect.height = 1.0f / rtProps.height;
  214. // Render
  215. RenderAPI& rapi = RenderAPI::instance();
  216. rapi.setRenderTarget(output);
  217. rapi.setViewport(viewRect);
  218. bind();
  219. gRendererUtility().drawScreenQuad();
  220. rapi.setRenderTarget(nullptr);
  221. rapi.setViewport(Rect2(0, 0, 1, 1));
  222. }
  223. POOLED_RENDER_TEXTURE_DESC IrradianceAccumulateCubeSHMat::getOutputDesc()
  224. {
  225. return POOLED_RENDER_TEXTURE_DESC::create2D(PF_RGBA32F, 9, 1, TU_RENDERTARGET);
  226. }
  227. IrradianceReduceSHParamDef gIrradianceReduceSHParamDef;
  228. IrradianceReduceSHMat::IrradianceReduceSHMat()
  229. {
  230. mParamBuffer = gIrradianceReduceSHParamDef.createBuffer();
  231. mParams->setParamBlockBuffer("Params", mParamBuffer);
  232. mParams->getBufferParam(GPT_COMPUTE_PROGRAM, "gInput", mInputBuffer);
  233. mParams->getLoadStoreTextureParam(GPT_COMPUTE_PROGRAM, "gOutput", mOutputTexture);
  234. }
  235. void IrradianceReduceSHMat::execute(const SPtr<GpuBuffer>& source, UINT32 numCoeffSets,
  236. const SPtr<Texture>& output, UINT32 outputIdx)
  237. {
  238. UINT32 shOrder = (UINT32)mVariation.getInt("SH_ORDER");
  239. Vector2I outputCoords = IBLUtility::getSHCoeffXYFromIdx(outputIdx, shOrder);
  240. gIrradianceReduceSHParamDef.gOutputIdx.set(mParamBuffer, outputCoords);
  241. gIrradianceReduceSHParamDef.gNumEntries.set(mParamBuffer, numCoeffSets);
  242. mInputBuffer.set(source);
  243. mOutputTexture.set(output);
  244. bind();
  245. RenderAPI& rapi = RenderAPI::instance();
  246. rapi.dispatchCompute(1);
  247. }
  248. SPtr<Texture> IrradianceReduceSHMat::createOutputTexture(UINT32 numCoeffSets)
  249. {
  250. UINT32 shOrder = (UINT32)mVariation.getInt("SH_ORDER");
  251. Vector2I size = IBLUtility::getSHCoeffTextureSize(numCoeffSets, shOrder);
  252. TEXTURE_DESC textureDesc;
  253. textureDesc.width = (UINT32)size.x;
  254. textureDesc.height = (UINT32)size.y;
  255. textureDesc.format = PF_RGBA32F;
  256. textureDesc.usage = TU_STATIC | TU_LOADSTORE;
  257. return Texture::create(textureDesc);
  258. }
  259. IrradianceReduceSHMat* IrradianceReduceSHMat::getVariation(int order)
  260. {
  261. if (order == 3)
  262. return get(getVariation<3>());
  263. return get(getVariation<5>());
  264. }
  265. IrradianceProjectSHParamDef gIrradianceProjectSHParamDef;
  266. IrradianceProjectSHMat::IrradianceProjectSHMat()
  267. {
  268. mParamBuffer = gIrradianceProjectSHParamDef.createBuffer();
  269. mParams->setParamBlockBuffer("Params", mParamBuffer);
  270. mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSHCoeffs", mInputTexture);
  271. }
  272. void IrradianceProjectSHMat::execute(const SPtr<Texture>& shCoeffs, UINT32 face, const SPtr<RenderTarget>& target)
  273. {
  274. gIrradianceProjectSHParamDef.gCubeFace.set(mParamBuffer, face);
  275. mInputTexture.set(shCoeffs);
  276. RenderAPI& rapi = RenderAPI::instance();
  277. rapi.setRenderTarget(target);
  278. bind();
  279. gRendererUtility().drawScreenQuad();
  280. }
  281. void RenderBeastIBLUtility::filterCubemapForSpecular(const SPtr<Texture>& cubemap, const SPtr<Texture>& scratch) const
  282. {
  283. auto& props = cubemap->getProperties();
  284. SPtr<Texture> scratchCubemap = scratch;
  285. if (scratchCubemap == nullptr)
  286. {
  287. TEXTURE_DESC cubemapDesc;
  288. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  289. cubemapDesc.format = props.getFormat();
  290. cubemapDesc.width = props.getWidth();
  291. cubemapDesc.height = props.getHeight();
  292. cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
  293. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  294. scratchCubemap = Texture::create(cubemapDesc);
  295. }
  296. // We sample the cubemaps using importance sampling to generate roughness
  297. UINT32 numMips = props.getNumMipmaps();
  298. // Before importance sampling the cubemaps we first create box filtered versions for each mip level. This helps fix
  299. // the aliasing artifacts that would otherwise be noticeable on importance sampled cubemaps. The aliasing happens
  300. // because:
  301. // 1. We use the same random samples for all pixels, which appears to duplicate reflections instead of creating
  302. // noise, which is usually more acceptable
  303. // 2. Even if we were to use fully random samples we would need a lot to avoid noticeable noise, which isn't
  304. // practical
  305. // Copy base mip level to scratch cubemap
  306. for (UINT32 face = 0; face < 6; face++)
  307. {
  308. TEXTURE_COPY_DESC copyDesc;
  309. copyDesc.srcFace = face;
  310. copyDesc.dstFace = face;
  311. cubemap->copy(scratchCubemap, copyDesc);
  312. }
  313. // Fill out remaining scratch mip levels by downsampling
  314. for (UINT32 mip = 1; mip < numMips; mip++)
  315. {
  316. UINT32 sourceMip = mip - 1;
  317. downsampleCubemap(scratchCubemap, sourceMip, scratchCubemap, mip);
  318. }
  319. // Importance sample
  320. for (UINT32 mip = 1; mip < numMips; mip++)
  321. {
  322. for (UINT32 face = 0; face < 6; face++)
  323. {
  324. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  325. cubeFaceRTDesc.colorSurfaces[0].texture = cubemap;
  326. cubeFaceRTDesc.colorSurfaces[0].face = face;
  327. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  328. cubeFaceRTDesc.colorSurfaces[0].mipLevel = mip;
  329. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  330. ReflectionCubeImportanceSampleMat* material = ReflectionCubeImportanceSampleMat::get();
  331. material->execute(scratchCubemap, face, mip, target);
  332. }
  333. }
  334. RenderAPI& rapi = RenderAPI::instance();
  335. rapi.setRenderTarget(nullptr);
  336. }
  337. bool supportsComputeSH()
  338. {
  339. return gRenderBeast()->getFeatureSet() == RenderBeastFeatureSet::Desktop;
  340. }
  341. void RenderBeastIBLUtility::filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<Texture>& output) const
  342. {
  343. SPtr<Texture> coeffTexture;
  344. if(supportsComputeSH())
  345. {
  346. IrradianceComputeSHMat* shCompute = IrradianceComputeSHMat::getVariation(5);
  347. IrradianceReduceSHMat* shReduce = IrradianceReduceSHMat::getVariation(5);
  348. UINT32 numCoeffSets;
  349. SPtr<GpuBuffer> coeffSetBuffer = shCompute->createOutputBuffer(cubemap, numCoeffSets);
  350. for (UINT32 face = 0; face < 6; face++)
  351. shCompute->execute(cubemap, face, coeffSetBuffer);
  352. coeffTexture = shReduce->createOutputTexture(1);
  353. shReduce->execute(coeffSetBuffer, numCoeffSets, coeffTexture, 0);
  354. }
  355. else
  356. {
  357. GpuResourcePool& resPool = GpuResourcePool::instance();
  358. SPtr<PooledRenderTexture> finalCoeffs = resPool.get(IrradianceAccumulateCubeSHMat::getOutputDesc());
  359. filterCubemapForIrradianceNonCompute(cubemap, 0, finalCoeffs->renderTexture);
  360. coeffTexture = finalCoeffs->texture;
  361. }
  362. IrradianceProjectSHMat* shProject = IrradianceProjectSHMat::get();
  363. for (UINT32 face = 0; face < 6; face++)
  364. {
  365. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  366. cubeFaceRTDesc.colorSurfaces[0].texture = output;
  367. cubeFaceRTDesc.colorSurfaces[0].face = face;
  368. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  369. cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
  370. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  371. shProject->execute(coeffTexture, face, target);
  372. }
  373. }
  374. void RenderBeastIBLUtility::filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<Texture>& output,
  375. UINT32 outputIdx) const
  376. {
  377. if(supportsComputeSH())
  378. {
  379. IrradianceComputeSHMat* shCompute = IrradianceComputeSHMat::getVariation(3);
  380. IrradianceReduceSHMat* shReduce = IrradianceReduceSHMat::getVariation(3);
  381. UINT32 numCoeffSets;
  382. SPtr<GpuBuffer> coeffSetBuffer = shCompute->createOutputBuffer(cubemap, numCoeffSets);
  383. for (UINT32 face = 0; face < 6; face++)
  384. shCompute->execute(cubemap, face, coeffSetBuffer);
  385. shReduce->execute(coeffSetBuffer, numCoeffSets, output, outputIdx);
  386. }
  387. else
  388. {
  389. RENDER_TEXTURE_DESC rtDesc;
  390. rtDesc.colorSurfaces[0].texture = output;
  391. SPtr<RenderTexture> target = RenderTexture::create(rtDesc);
  392. filterCubemapForIrradianceNonCompute(cubemap, outputIdx, target);
  393. }
  394. }
  395. void RenderBeastIBLUtility::scaleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst,
  396. UINT32 dstMip) const
  397. {
  398. auto& srcProps = src->getProperties();
  399. auto& dstProps = dst->getProperties();
  400. SPtr<Texture> scratchTex = src;
  401. int sizeSrcLog2 = (int)log2((float)srcProps.getWidth());
  402. int sizeDstLog2 = (int)log2((float)dstProps.getWidth());
  403. int sizeLog2Diff = sizeSrcLog2 - sizeDstLog2;
  404. // If size difference is greater than one mip-level and we're downscaling, we need to generate intermediate mip
  405. // levels
  406. if(sizeLog2Diff > 1)
  407. {
  408. UINT32 mipSize = (UINT32)exp2((float)(sizeSrcLog2 - 1));
  409. UINT32 numDownsamples = sizeLog2Diff - 1;
  410. TEXTURE_DESC cubemapDesc;
  411. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  412. cubemapDesc.format = srcProps.getFormat();
  413. cubemapDesc.width = mipSize;
  414. cubemapDesc.height = mipSize;
  415. cubemapDesc.numMips = numDownsamples - 1;
  416. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  417. scratchTex = Texture::create(cubemapDesc);
  418. downsampleCubemap(src, srcMip, scratchTex, 0);
  419. for(UINT32 i = 0; i < cubemapDesc.numMips; i++)
  420. downsampleCubemap(scratchTex, i, scratchTex, i + 1);
  421. srcMip = cubemapDesc.numMips;
  422. }
  423. // Same size so just copy
  424. if(sizeSrcLog2 == sizeDstLog2)
  425. {
  426. for (UINT32 face = 0; face < 6; face++)
  427. {
  428. TEXTURE_COPY_DESC copyDesc;
  429. copyDesc.srcFace = face;
  430. copyDesc.srcMip = srcMip;
  431. copyDesc.dstFace = face;
  432. copyDesc.dstMip = dstMip;
  433. src->copy(dst, copyDesc);
  434. }
  435. }
  436. else
  437. downsampleCubemap(scratchTex, srcMip, dst, dstMip);
  438. }
  439. void RenderBeastIBLUtility::downsampleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst,
  440. UINT32 dstMip)
  441. {
  442. for (UINT32 face = 0; face < 6; face++)
  443. {
  444. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  445. cubeFaceRTDesc.colorSurfaces[0].texture = dst;
  446. cubeFaceRTDesc.colorSurfaces[0].face = face;
  447. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  448. cubeFaceRTDesc.colorSurfaces[0].mipLevel = dstMip;
  449. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  450. ReflectionCubeDownsampleMat* material = ReflectionCubeDownsampleMat::get();
  451. material->execute(src, face, srcMip, target);
  452. }
  453. }
  454. void RenderBeastIBLUtility::filterCubemapForIrradianceNonCompute(const SPtr<Texture>& cubemap, UINT32 outputIdx,
  455. const SPtr<RenderTexture>& output)
  456. {
  457. static const UINT32 NUM_COEFFS = 9;
  458. GpuResourcePool& resPool = GpuResourcePool::instance();
  459. IrradianceComputeSHFragMat* shCompute = IrradianceComputeSHFragMat::get();
  460. IrradianceAccumulateSHMat* shAccum = IrradianceAccumulateSHMat::get();
  461. IrradianceAccumulateCubeSHMat* shAccumCube = IrradianceAccumulateCubeSHMat::get();
  462. for(UINT32 coeff = 0; coeff < NUM_COEFFS; ++coeff)
  463. {
  464. SPtr<PooledRenderTexture> coeffsTex = resPool.get(shCompute->getOutputDesc(cubemap));
  465. // Generate SH coefficients and weights per-texel
  466. for(UINT32 face = 0; face < 6; face++)
  467. {
  468. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  469. cubeFaceRTDesc.colorSurfaces[0].texture = coeffsTex->texture;
  470. cubeFaceRTDesc.colorSurfaces[0].face = face;
  471. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  472. cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
  473. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  474. shCompute->execute(cubemap, face, coeff, target);
  475. }
  476. // Downsample, summing up coefficients and weights all the way down to 1x1
  477. auto& sourceProps = cubemap->getProperties();
  478. UINT32 numMips = PixelUtil::getMaxMipmaps(sourceProps.getWidth(), sourceProps.getHeight(), 1,
  479. sourceProps.getFormat());
  480. SPtr<PooledRenderTexture> downsampleInput = coeffsTex;
  481. coeffsTex = nullptr;
  482. for(UINT32 mip = 0; mip < numMips; mip++)
  483. {
  484. SPtr<PooledRenderTexture> accumCoeffsTex = resPool.get(shAccum->getOutputDesc(downsampleInput->texture));
  485. for(UINT32 face = 0; face < 6; face++)
  486. {
  487. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  488. cubeFaceRTDesc.colorSurfaces[0].texture = accumCoeffsTex->texture;
  489. cubeFaceRTDesc.colorSurfaces[0].face = face;
  490. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  491. cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
  492. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  493. shAccum->execute(downsampleInput->texture, face, 0, target);
  494. }
  495. downsampleInput = accumCoeffsTex;
  496. }
  497. // Sum up all the faces and write the coefficient to the final texture
  498. Vector2I outputOffset = getSHCoeffXYFromIdx(outputIdx, 3);
  499. shAccumCube->execute(downsampleInput->texture, 0, outputOffset, coeff, output);
  500. }
  501. }
  502. }}