BsRenderBeastIBLUtility.cpp 23 KB

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