BsReflectionProbes.cpp 13 KB

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