BsRenderBeastIBLUtility.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  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. namespace bs { namespace ct
  9. {
  10. ReflectionCubeDownsampleParamDef gReflectionCubeDownsampleParamDef;
  11. ReflectionCubeDownsampleMat::ReflectionCubeDownsampleMat()
  12. {
  13. mParamBuffer = gReflectionCubeDownsampleParamDef.createBuffer();
  14. SPtr<GpuParams> gpuParams = mParamsSet->getGpuParams();
  15. gpuParams->setParamBlockBuffer("Input", mParamBuffer);
  16. gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  17. }
  18. void ReflectionCubeDownsampleMat::_initVariations(ShaderVariations& variations)
  19. {
  20. // Do nothing
  21. }
  22. void ReflectionCubeDownsampleMat::execute(
  23. const SPtr<Texture>& source,
  24. UINT32 face,
  25. UINT32 mip,
  26. const SPtr<RenderTarget>& target)
  27. {
  28. mInputTexture.set(source);
  29. gReflectionCubeDownsampleParamDef.gCubeFace.set(mParamBuffer, face);
  30. gReflectionCubeDownsampleParamDef.gMipLevel.set(mParamBuffer, mip);
  31. RenderAPI& rapi = RenderAPI::instance();
  32. rapi.setRenderTarget(target);
  33. gRendererUtility().setPass(mMaterial);
  34. gRendererUtility().setPassParams(mParamsSet);
  35. gRendererUtility().drawScreenQuad();
  36. }
  37. const UINT32 ReflectionCubeImportanceSampleMat::NUM_SAMPLES = 1024;
  38. ReflectionCubeImportanceSampleParamDef gReflectionCubeImportanceSampleParamDef;
  39. ReflectionCubeImportanceSampleMat::ReflectionCubeImportanceSampleMat()
  40. {
  41. mParamBuffer = gReflectionCubeImportanceSampleParamDef.createBuffer();
  42. SPtr<GpuParams> gpuParams = mParamsSet->getGpuParams();
  43. gpuParams->setParamBlockBuffer("Input", mParamBuffer);
  44. gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
  45. }
  46. void ReflectionCubeImportanceSampleMat::_initVariations(ShaderVariations& variations)
  47. {
  48. ShaderVariation variation({
  49. ShaderVariation::Param("NUM_SAMPLES", NUM_SAMPLES)
  50. });
  51. variations.add(variation);
  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. gRendererUtility().setPass(mMaterial);
  69. gRendererUtility().setPassParams(mParamsSet);
  70. gRendererUtility().drawScreenQuad();
  71. }
  72. IrradianceComputeSHParamDef gIrradianceComputeSHParamDef;
  73. // TILE_WIDTH * TILE_HEIGHT must be pow2 because of parallel reduction algorithm
  74. const static UINT32 TILE_WIDTH = 8;
  75. const static UINT32 TILE_HEIGHT = 8;
  76. // For very small textures this should be reduced so number of launched threads can properly utilize GPU cores
  77. const static UINT32 PIXELS_PER_THREAD = 4;
  78. ShaderVariation IrradianceComputeSHMat::VAR_Order3 = ShaderVariation({
  79. ShaderVariation::Param("TILE_WIDTH", TILE_WIDTH),
  80. ShaderVariation::Param("TILE_HEIGHT", TILE_HEIGHT),
  81. ShaderVariation::Param("PIXELS_PER_THREAD", PIXELS_PER_THREAD),
  82. ShaderVariation::Param("SH_ORDER", 3),
  83. });
  84. ShaderVariation IrradianceComputeSHMat::VAR_Order5 = ShaderVariation({
  85. ShaderVariation::Param("TILE_WIDTH", TILE_WIDTH),
  86. ShaderVariation::Param("TILE_HEIGHT", TILE_HEIGHT),
  87. ShaderVariation::Param("PIXELS_PER_THREAD", PIXELS_PER_THREAD),
  88. ShaderVariation::Param("SH_ORDER", 5),
  89. });
  90. IrradianceComputeSHMat::IrradianceComputeSHMat()
  91. {
  92. mParamBuffer = gIrradianceComputeSHParamDef.createBuffer();
  93. SPtr<GpuParams> params = mParamsSet->getGpuParams();
  94. params->setParamBlockBuffer("Params", mParamBuffer);
  95. params->getTextureParam(GPT_COMPUTE_PROGRAM, "gInputTex", mInputTexture);
  96. params->getBufferParam(GPT_COMPUTE_PROGRAM, "gOutput", mOutputBuffer);
  97. }
  98. void IrradianceComputeSHMat::_initVariations(ShaderVariations& variations)
  99. {
  100. variations.add(VAR_Order3);
  101. variations.add(VAR_Order5);
  102. }
  103. void IrradianceComputeSHMat::execute(const SPtr<Texture>& source, UINT32 face, const SPtr<GpuBuffer>& output)
  104. {
  105. auto& props = source->getProperties();
  106. UINT32 faceSize = props.getWidth();
  107. assert(faceSize == props.getHeight());
  108. Vector2I dispatchSize;
  109. dispatchSize.x = Math::divideAndRoundUp(faceSize, TILE_WIDTH * PIXELS_PER_THREAD);
  110. dispatchSize.y = Math::divideAndRoundUp(faceSize, TILE_HEIGHT * PIXELS_PER_THREAD);
  111. mInputTexture.set(source);
  112. gIrradianceComputeSHParamDef.gCubeFace.set(mParamBuffer, face);
  113. gIrradianceComputeSHParamDef.gFaceSize.set(mParamBuffer, source->getProperties().getWidth());
  114. gIrradianceComputeSHParamDef.gDispatchSize.set(mParamBuffer, dispatchSize);
  115. mOutputBuffer.set(output);
  116. RenderAPI& rapi = RenderAPI::instance();
  117. gRendererUtility().setComputePass(mMaterial);
  118. gRendererUtility().setPassParams(mParamsSet);
  119. rapi.dispatchCompute(dispatchSize.x, dispatchSize.y);
  120. }
  121. SPtr<GpuBuffer> IrradianceComputeSHMat::createOutputBuffer(const SPtr<Texture>& source, UINT32& numCoeffSets)
  122. {
  123. auto& props = source->getProperties();
  124. UINT32 faceSize = props.getWidth();
  125. assert(faceSize == props.getHeight());
  126. Vector2I dispatchSize;
  127. dispatchSize.x = Math::divideAndRoundUp(faceSize, TILE_WIDTH * PIXELS_PER_THREAD);
  128. dispatchSize.y = Math::divideAndRoundUp(faceSize, TILE_HEIGHT * PIXELS_PER_THREAD);
  129. numCoeffSets = dispatchSize.x * dispatchSize.y * 6;
  130. GPU_BUFFER_DESC bufferDesc;
  131. bufferDesc.type = GBT_STRUCTURED;
  132. bufferDesc.elementCount = numCoeffSets;
  133. bufferDesc.format = BF_UNKNOWN;
  134. bufferDesc.randomGpuWrite = true;
  135. if(mVariation.getInt("SH_ORDER") == 3)
  136. bufferDesc.elementSize = sizeof(SHCoeffsAndWeight3);
  137. else
  138. bufferDesc.elementSize = sizeof(SHCoeffsAndWeight5);
  139. return GpuBuffer::create(bufferDesc);
  140. }
  141. IrradianceComputeSHMat* IrradianceComputeSHMat::getVariation(int order)
  142. {
  143. if (order == 3)
  144. return get(VAR_Order3);
  145. return get(VAR_Order5);
  146. }
  147. IrradianceReduceSHParamDef gIrradianceReduceSHParamDef;
  148. ShaderVariation IrradianceReduceSHMat::VAR_Order3 = ShaderVariation({
  149. ShaderVariation::Param("SH_ORDER", 3),
  150. });
  151. ShaderVariation IrradianceReduceSHMat::VAR_Order5 = ShaderVariation({
  152. ShaderVariation::Param("SH_ORDER", 5),
  153. });
  154. IrradianceReduceSHMat::IrradianceReduceSHMat()
  155. {
  156. mParamBuffer = gIrradianceReduceSHParamDef.createBuffer();
  157. SPtr<GpuParams> params = mParamsSet->getGpuParams();
  158. params->setParamBlockBuffer("Params", mParamBuffer);
  159. params->getBufferParam(GPT_COMPUTE_PROGRAM, "gInput", mInputBuffer);
  160. params->getBufferParam(GPT_COMPUTE_PROGRAM, "gOutput", mOutputBuffer);
  161. }
  162. void IrradianceReduceSHMat::_initVariations(ShaderVariations& variations)
  163. {
  164. variations.add(VAR_Order3);
  165. variations.add(VAR_Order5);
  166. }
  167. void IrradianceReduceSHMat::execute(const SPtr<GpuBuffer>& source, UINT32 numCoeffSets,
  168. const SPtr<GpuBuffer>& output, UINT32 outputIdx)
  169. {
  170. gIrradianceReduceSHParamDef.gNumEntries.set(mParamBuffer, numCoeffSets);
  171. gIrradianceReduceSHParamDef.gOutputIdx.set(mParamBuffer, outputIdx);
  172. mInputBuffer.set(source);
  173. mOutputBuffer.set(output);
  174. RenderAPI& rapi = RenderAPI::instance();
  175. gRendererUtility().setComputePass(mMaterial);
  176. gRendererUtility().setPassParams(mParamsSet);
  177. rapi.dispatchCompute(1);
  178. }
  179. SPtr<GpuBuffer> IrradianceReduceSHMat::createOutputBuffer(UINT32 numEntries)
  180. {
  181. GPU_BUFFER_DESC bufferDesc;
  182. bufferDesc.type = GBT_STRUCTURED;
  183. bufferDesc.elementCount = numEntries;
  184. bufferDesc.format = BF_UNKNOWN;
  185. bufferDesc.randomGpuWrite = true;
  186. if(mVariation.getInt("SH_ORDER") == 3)
  187. bufferDesc.elementSize = sizeof(SHVector3RGB);
  188. else
  189. bufferDesc.elementSize = sizeof(SHVector5RGB);
  190. return GpuBuffer::create(bufferDesc);
  191. }
  192. IrradianceReduceSHMat* IrradianceReduceSHMat::getVariation(int order)
  193. {
  194. if (order == 3)
  195. return get(VAR_Order3);
  196. return get(VAR_Order5);
  197. }
  198. IrradianceProjectSHParamDef gIrradianceProjectSHParamDef;
  199. IrradianceProjectSHMat::IrradianceProjectSHMat()
  200. {
  201. mParamBuffer = gIrradianceProjectSHParamDef.createBuffer();
  202. SPtr<GpuParams> params = mParamsSet->getGpuParams();
  203. params->setParamBlockBuffer("Params", mParamBuffer);
  204. params->getBufferParam(GPT_FRAGMENT_PROGRAM, "gSHCoeffs", mInputBuffer);
  205. }
  206. void IrradianceProjectSHMat::_initVariations(ShaderVariations& variations)
  207. {
  208. // Do nothing
  209. }
  210. void IrradianceProjectSHMat::execute(const SPtr<GpuBuffer>& shCoeffs, UINT32 face, const SPtr<RenderTarget>& target)
  211. {
  212. gIrradianceProjectSHParamDef.gCubeFace.set(mParamBuffer, face);
  213. mInputBuffer.set(shCoeffs);
  214. RenderAPI& rapi = RenderAPI::instance();
  215. rapi.setRenderTarget(target);
  216. gRendererUtility().setPass(mMaterial);
  217. gRendererUtility().setPassParams(mParamsSet);
  218. gRendererUtility().drawScreenQuad();
  219. }
  220. void RenderBeastIBLUtility::filterCubemapForSpecular(const SPtr<Texture>& cubemap, const SPtr<Texture>& scratch) const
  221. {
  222. auto& props = cubemap->getProperties();
  223. SPtr<Texture> scratchCubemap = scratch;
  224. if (scratchCubemap == nullptr)
  225. {
  226. TEXTURE_DESC cubemapDesc;
  227. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  228. cubemapDesc.format = props.getFormat();
  229. cubemapDesc.width = props.getWidth();
  230. cubemapDesc.height = props.getHeight();
  231. cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
  232. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  233. scratchCubemap = Texture::create(cubemapDesc);
  234. }
  235. // We sample the cubemaps using importance sampling to generate roughness
  236. UINT32 numMips = props.getNumMipmaps();
  237. // Before importance sampling the cubemaps we first create box filtered versions for each mip level. This helps fix
  238. // the aliasing artifacts that would otherwise be noticeable on importance sampled cubemaps. The aliasing happens
  239. // because:
  240. // 1. We use the same random samples for all pixels, which appears to duplicate reflections instead of creating
  241. // noise, which is usually more acceptable
  242. // 2. Even if we were to use fully random samples we would need a lot to avoid noticeable noise, which isn't
  243. // practical
  244. // Copy base mip level to scratch cubemap
  245. for (UINT32 face = 0; face < 6; face++)
  246. cubemap->copy(scratchCubemap, face, 0, face, 0);
  247. // Fill out remaining scratch mip levels by downsampling
  248. for (UINT32 mip = 1; mip < numMips; mip++)
  249. {
  250. UINT32 sourceMip = mip - 1;
  251. downsampleCubemap(scratchCubemap, sourceMip, scratchCubemap, mip);
  252. }
  253. // Importance sample
  254. for (UINT32 mip = 1; mip < numMips; mip++)
  255. {
  256. for (UINT32 face = 0; face < 6; face++)
  257. {
  258. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  259. cubeFaceRTDesc.colorSurfaces[0].texture = cubemap;
  260. cubeFaceRTDesc.colorSurfaces[0].face = face;
  261. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  262. cubeFaceRTDesc.colorSurfaces[0].mipLevel = mip;
  263. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  264. ReflectionCubeImportanceSampleMat* material = ReflectionCubeImportanceSampleMat::get();
  265. material->execute(scratchCubemap, face, mip, target);
  266. }
  267. }
  268. RenderAPI& rapi = RenderAPI::instance();
  269. rapi.setRenderTarget(nullptr);
  270. }
  271. void RenderBeastIBLUtility::filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<Texture>& output) const
  272. {
  273. IrradianceComputeSHMat* shCompute = IrradianceComputeSHMat::getVariation(5);
  274. IrradianceReduceSHMat* shReduce = IrradianceReduceSHMat::getVariation(5);
  275. IrradianceProjectSHMat* shProject = IrradianceProjectSHMat::get();
  276. UINT32 numCoeffSets;
  277. SPtr<GpuBuffer> coeffSetBuffer = shCompute->createOutputBuffer(cubemap, numCoeffSets);
  278. for (UINT32 face = 0; face < 6; face++)
  279. shCompute->execute(cubemap, face, coeffSetBuffer);
  280. SPtr<GpuBuffer> coeffBuffer = shReduce->createOutputBuffer(1);
  281. shReduce->execute(coeffSetBuffer, numCoeffSets, coeffBuffer, 0);
  282. for (UINT32 face = 0; face < 6; face++)
  283. {
  284. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  285. cubeFaceRTDesc.colorSurfaces[0].texture = output;
  286. cubeFaceRTDesc.colorSurfaces[0].face = face;
  287. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  288. cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
  289. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  290. shProject->execute(coeffBuffer, face, target);
  291. }
  292. }
  293. void RenderBeastIBLUtility::filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<GpuBuffer>& output,
  294. UINT32 outputIdx) const
  295. {
  296. IrradianceComputeSHMat* shCompute = IrradianceComputeSHMat::getVariation(3);
  297. IrradianceReduceSHMat* shReduce = IrradianceReduceSHMat::getVariation(3);
  298. UINT32 numCoeffSets;
  299. SPtr<GpuBuffer> coeffSetBuffer = shCompute->createOutputBuffer(cubemap, numCoeffSets);
  300. for (UINT32 face = 0; face < 6; face++)
  301. shCompute->execute(cubemap, face, coeffSetBuffer);
  302. shReduce->execute(coeffSetBuffer, numCoeffSets, output, outputIdx);
  303. }
  304. void RenderBeastIBLUtility::scaleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst,
  305. UINT32 dstMip) const
  306. {
  307. auto& srcProps = src->getProperties();
  308. auto& dstProps = dst->getProperties();
  309. SPtr<Texture> scratchTex = src;
  310. int sizeSrcLog2 = (int)log2((float)srcProps.getWidth());
  311. int sizeDstLog2 = (int)log2((float)dstProps.getWidth());
  312. int sizeLog2Diff = sizeSrcLog2 - sizeDstLog2;
  313. // If size difference is greater than one mip-level and we're downscaling, we need to generate intermediate mip
  314. // levels
  315. if(sizeLog2Diff > 1)
  316. {
  317. UINT32 mipSize = (UINT32)exp2((float)(sizeSrcLog2 - 1));
  318. UINT32 numDownsamples = sizeLog2Diff - 1;
  319. TEXTURE_DESC cubemapDesc;
  320. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  321. cubemapDesc.format = srcProps.getFormat();
  322. cubemapDesc.width = mipSize;
  323. cubemapDesc.height = mipSize;
  324. cubemapDesc.numMips = numDownsamples - 1;
  325. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  326. scratchTex = Texture::create(cubemapDesc);
  327. downsampleCubemap(src, srcMip, scratchTex, 0);
  328. for(UINT32 i = 0; i < cubemapDesc.numMips; i++)
  329. downsampleCubemap(scratchTex, i, scratchTex, i + 1);
  330. srcMip = cubemapDesc.numMips;
  331. }
  332. // Same size so just copy
  333. if(sizeSrcLog2 == sizeDstLog2)
  334. {
  335. for (UINT32 face = 0; face < 6; face++)
  336. src->copy(dst, face, srcMip, face, dstMip);
  337. }
  338. else
  339. downsampleCubemap(scratchTex, srcMip, dst, dstMip);
  340. }
  341. void RenderBeastIBLUtility::downsampleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst,
  342. UINT32 dstMip)
  343. {
  344. for (UINT32 face = 0; face < 6; face++)
  345. {
  346. RENDER_TEXTURE_DESC cubeFaceRTDesc;
  347. cubeFaceRTDesc.colorSurfaces[0].texture = dst;
  348. cubeFaceRTDesc.colorSurfaces[0].face = face;
  349. cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
  350. cubeFaceRTDesc.colorSurfaces[0].mipLevel = dstMip;
  351. SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
  352. ReflectionCubeDownsampleMat* material = ReflectionCubeDownsampleMat::get();
  353. material->execute(src, face, srcMip, target);
  354. }
  355. }
  356. }}