BsRendererTextures.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsRendererTextures.h"
  4. #include "Math/BsVector2.h"
  5. #include "Image/BsColor.h"
  6. #include "Math/BsMath.h"
  7. #include "Image/BsTexture.h"
  8. #include "Image/BsPixelData.h"
  9. #include "Renderer/BsIBLUtility.h"
  10. namespace bs { namespace ct
  11. {
  12. SPtr<Texture> generate4x4RandomizationTexture()
  13. {
  14. UINT32 mapping[16] = { 13, 5, 1, 9, 14, 3, 7, 11, 15, 2, 6, 12, 4, 8, 0, 10 };
  15. Vector2 bases[16];
  16. for (UINT32 i = 0; i < 16; ++i)
  17. {
  18. float angle = (mapping[i] / 16.0f) * Math::PI;
  19. bases[i].x = cos(angle);
  20. bases[i].y = sin(angle);
  21. }
  22. SPtr<PixelData> pixelData = PixelData::create(4, 4, 1, PF_RG8);
  23. for(UINT32 y = 0; y < 4; ++y)
  24. for(UINT32 x = 0; x < 4; ++x)
  25. {
  26. UINT32 base = (y * 4) + x;
  27. Color color;
  28. color.r = bases[base].x * 0.5f + 0.5f;
  29. color.g = bases[base].y * 0.5f + 0.5f;
  30. pixelData->setColorAt(color, x, y);
  31. }
  32. return Texture::create(pixelData);
  33. }
  34. // Reverse bits functions used for Hammersley sequence
  35. float reverseBits(UINT32 bits)
  36. {
  37. bits = (bits << 16u) | (bits >> 16u);
  38. bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
  39. bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
  40. bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
  41. bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
  42. return (float)(double(bits) / (double)0x100000000LL);
  43. }
  44. void hammersleySequence(UINT32 i, UINT32 count, float& e0, float& e1)
  45. {
  46. e0 = i / (float)count;
  47. e1 = reverseBits(i);
  48. }
  49. Vector3 sphericalToCartesian(float cosTheta, float sinTheta, float phi)
  50. {
  51. Vector3 output;
  52. output.x = sinTheta * cos(phi);
  53. output.y = sinTheta * sin(phi);
  54. output.z = cosTheta;
  55. return output;
  56. }
  57. // Generates an angle in spherical coordinates, importance sampled for the specified roughness based on some uniformly
  58. // distributed random variables in range [0, 1].
  59. void importanceSampleGGX(float e0, float e1, float roughness4, float& cosTheta, float& phi)
  60. {
  61. // See GGXImportanceSample.nb for derivation (essentially, take base GGX, normalize it, generate PDF, split PDF into
  62. // marginal probability for theta and conditional probability for phi. Plug those into the CDF, invert it.)
  63. cosTheta = sqrt((1.0f - e0) / (1.0f + (roughness4 - 1.0f) * e0));
  64. phi = 2.0f * Math::PI * e1;
  65. }
  66. float calcMicrofacetShadowingSmithGGX(float roughness4, float NoV, float NoL)
  67. {
  68. // Note: See lighting shader for derivation. Includes microfacet BRDF divisor.
  69. float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
  70. float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
  71. return 1.0f / (g1V * g1L);
  72. }
  73. SPtr<Texture> generatePreintegratedEnvBRDF()
  74. {
  75. TEXTURE_DESC desc;
  76. desc.type = TEX_TYPE_2D;
  77. desc.format = PF_RG16F;
  78. desc.width = 128;
  79. desc.height = 32;
  80. SPtr<Texture> texture = Texture::create(desc);
  81. PixelData pixelData = texture->lock(GBL_WRITE_ONLY_DISCARD);
  82. for (UINT32 y = 0; y < desc.height; y++)
  83. {
  84. float roughness = (float)(y + 0.5f) / desc.height;
  85. float m = roughness * roughness;
  86. float m2 = m*m;
  87. for (UINT32 x = 0; x < desc.width; x++)
  88. {
  89. float NoV = (float)(x + 0.5f) / desc.width;
  90. Vector3 V;
  91. V.x = sqrt(1.0f - NoV * NoV); // sine
  92. V.y = 0.0f;
  93. V.z = NoV;
  94. // These are the two integrals resulting from the second part of the split-sum approximation. Described in
  95. // Epic's 2013 paper:
  96. // http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
  97. float scale = 0.0f;
  98. float offset = 0.0f;
  99. // We use the same importance sampling function we use for reflection cube importance sampling, only we
  100. // sample G and F, instead of D factors of the microfactet BRDF. See GGXImportanceSample.nb for derivation.
  101. constexpr UINT32 NumSamples = 128;
  102. for (UINT32 i = 0; i < NumSamples; i++)
  103. {
  104. float e0, e1;
  105. hammersleySequence(i, NumSamples, e0, e1);
  106. float cosTheta, phi;
  107. importanceSampleGGX(e0, e1, m2, cosTheta, phi);
  108. float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
  109. Vector3 H = sphericalToCartesian(cosTheta, sinTheta, phi);
  110. Vector3 L = 2.0f * Vector3::dot(V, H) * H - V;
  111. float VoH = std::max(Vector3::dot(V, H), 0.0f);
  112. float NoL = std::max(L.z, 0.0f); // N assumed (0, 0, 1)
  113. float NoH = std::max(H.z, 0.0f); // N assumed (0, 0, 1)
  114. // Set second part of the split sum integral is split into two parts:
  115. // F0*I[G * (1 - (1 - v.h)^5) * cos(theta)] + I[G * (1 - v.h)^5 * cos(theta)] (F0 * scale + bias)
  116. // We calculate the fresnel scale (1 - (1 - v.h)^5) and bias ((1 - v.h)^5) parts
  117. float fc = pow(1.0f - VoH, 5.0f);
  118. float fresnelScale = 1.0f - fc;
  119. float fresnelOffset = fc;
  120. // We calculate the G part
  121. float G = calcMicrofacetShadowingSmithGGX(m2, NoV, NoL);
  122. // When we factor out G and F, then divide D by PDF, this is what's left
  123. // Note: This is based on PDF: D * NoH / (4 * VoH). (4 * VoH) factor comes from the Jacobian of the
  124. // transformation from half vector to light vector
  125. float pdfFactor = 4.0f * VoH / NoH;
  126. if (NoL > 0.0f)
  127. {
  128. scale += NoL * pdfFactor * G * fresnelScale;
  129. offset += NoL * pdfFactor * G * fresnelOffset;
  130. }
  131. }
  132. scale /= NumSamples;
  133. offset /= NumSamples;
  134. Color color;
  135. color.r = Math::clamp01(scale);
  136. color.g = Math::clamp01(offset);
  137. pixelData.setColorAt(color, x, y);
  138. }
  139. }
  140. texture->unlock();
  141. return texture;
  142. }
  143. SPtr<Texture> generateDefaultIndirect()
  144. {
  145. TEXTURE_DESC dummySkyDesc;
  146. dummySkyDesc.type = TEX_TYPE_CUBE_MAP;
  147. dummySkyDesc.format = PF_RG11B10F;
  148. dummySkyDesc.width = 2;
  149. dummySkyDesc.height = 2;
  150. // Note: Eventually replace this with a time of day model
  151. float intensity = 1.0f;
  152. Color skyColor = Color::White * intensity;
  153. SPtr<Texture> skyTexture = Texture::create(dummySkyDesc);
  154. UINT32 sides[] = { CF_PositiveX, CF_NegativeX, CF_PositiveZ, CF_NegativeZ };
  155. for(UINT32 i = 0; i < 4; ++i)
  156. {
  157. PixelData data = skyTexture->lock(GBL_WRITE_ONLY_DISCARD, 0, sides[i]);
  158. data.setColorAt(skyColor, 0, 0);
  159. data.setColorAt(skyColor, 1, 0);
  160. data.setColorAt(Color::Black, 0, 1);
  161. data.setColorAt(Color::Black, 1, 1);
  162. skyTexture->unlock();
  163. }
  164. {
  165. PixelData data = skyTexture->lock(GBL_WRITE_ONLY_DISCARD, 0, CF_PositiveY);
  166. data.setColorAt(skyColor, 0, 0);
  167. data.setColorAt(skyColor, 1, 0);
  168. data.setColorAt(skyColor, 0, 1);
  169. data.setColorAt(skyColor, 1, 1);
  170. skyTexture->unlock();
  171. }
  172. {
  173. PixelData data = skyTexture->lock(GBL_WRITE_ONLY_DISCARD, 0, CF_NegativeY);
  174. data.setColorAt(Color::Black, 0, 0);
  175. data.setColorAt(Color::Black, 1, 0);
  176. data.setColorAt(Color::Black, 0, 1);
  177. data.setColorAt(Color::Black, 1, 1);
  178. skyTexture->unlock();
  179. }
  180. TEXTURE_DESC irradianceCubemapDesc;
  181. irradianceCubemapDesc.type = TEX_TYPE_CUBE_MAP;
  182. irradianceCubemapDesc.format = PF_RG11B10F;
  183. irradianceCubemapDesc.width = IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
  184. irradianceCubemapDesc.height = IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
  185. irradianceCubemapDesc.numMips = 0;
  186. irradianceCubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  187. SPtr<Texture> irradiance = Texture::create(irradianceCubemapDesc);
  188. gIBLUtility().filterCubemapForIrradiance(skyTexture, irradiance);
  189. return irradiance;
  190. }
  191. SPtr<Texture> RendererTextures::preintegratedEnvGF;
  192. SPtr<Texture> RendererTextures::ssaoRandomization4x4;
  193. SPtr<Texture> RendererTextures::defaultIndirect;
  194. void RendererTextures::startUp()
  195. {
  196. preintegratedEnvGF = generatePreintegratedEnvBRDF();
  197. ssaoRandomization4x4 = generate4x4RandomizationTexture();
  198. defaultIndirect = generateDefaultIndirect();
  199. }
  200. void RendererTextures::shutDown()
  201. {
  202. preintegratedEnvGF = nullptr;
  203. ssaoRandomization4x4 = nullptr;
  204. defaultIndirect = nullptr;
  205. }
  206. }}