BsRenderBeastIBLUtility.cpp 15 KB

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