BsRenderBeastIBLUtility.cpp 24 KB

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