BsRenderBeastIBLUtility.cpp 15 KB

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