IrradianceComputeSH.bsl 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #include "$ENGINE$\ReflectionCubemapCommon.bslinc"
  2. #include "$ENGINE$\SHCommon.bslinc"
  3. technique IrradianceComputeSH
  4. {
  5. mixin ReflectionCubemapCommon;
  6. mixin SHCommon;
  7. code
  8. {
  9. struct SHCoeffsAndWeight
  10. {
  11. SHVectorRGB coeffs;
  12. float weight;
  13. };
  14. SamplerState gInputSamp;
  15. TextureCube gInputTex;
  16. RWStructuredBuffer<SHCoeffsAndWeight> gOutput;
  17. [internal]
  18. cbuffer Params
  19. {
  20. uint gCubeFace;
  21. uint gFaceSize;
  22. uint2 gDispatchSize;
  23. }
  24. groupshared SHCoeffsAndWeight sCoeffs[TILE_WIDTH * TILE_HEIGHT];
  25. /**
  26. * Integrates area of a cube face projected onto the surface of the sphere, from [0, 0] to [u, v].
  27. * u & v expected in [-1, -1] to [1, 1] range.
  28. *
  29. * See http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/ for derivation.
  30. */
  31. float integrateProjectedCubeArea(float u, float v)
  32. {
  33. return atan2(u * v, sqrt(u * u + v * v + 1.0f));
  34. }
  35. /** Calculates solid angle of a texel projected onto a sphere. */
  36. float texelSolidAngle(float u, float v, float invFaceSize)
  37. {
  38. float x0 = u - invFaceSize;
  39. float x1 = u + invFaceSize;
  40. float y0 = v - invFaceSize;
  41. float y1 = v + invFaceSize;
  42. return integrateProjectedCubeArea(x1, y1)
  43. - integrateProjectedCubeArea(x0, y1)
  44. - integrateProjectedCubeArea(x1, y0)
  45. + integrateProjectedCubeArea(x0, y0);
  46. }
  47. [numthreads(TILE_WIDTH, TILE_HEIGHT, 1)]
  48. void csmain(
  49. uint groupIdx : SV_GroupIndex,
  50. uint3 groupId : SV_GroupID,
  51. uint3 dispatchThreadId : SV_DispatchThreadID)
  52. {
  53. SHCoeffsAndWeight data;
  54. data.weight = 0;
  55. SHZero(data.coeffs.R);
  56. SHZero(data.coeffs.G);
  57. SHZero(data.coeffs.B);
  58. float invFaceSize = 1.0f / gFaceSize;
  59. uint2 pixelCoords = dispatchThreadId.xy * PIXELS_PER_THREAD;
  60. uint2 pixelCoordsEnd = pixelCoords + uint2(PIXELS_PER_THREAD, PIXELS_PER_THREAD);
  61. for(uint y = pixelCoords.y; y < pixelCoordsEnd.y; y++)
  62. {
  63. for(uint x = pixelCoords.x; x < pixelCoordsEnd.x; x++)
  64. {
  65. // Ignore pixels out of valid range
  66. if (x >= gFaceSize || y >= gFaceSize)
  67. break;
  68. // Map from [0, size-1] to [-1.0 + invSize, 1.0 - invSize].
  69. // (+0.5 in order to sample center of texel)
  70. float u = 2.0f * (x + 0.5f) * invFaceSize - 1.0f;
  71. float v = 2.0f * (y + 0.5f) * invFaceSize - 1.0f;
  72. float3 dir = getDirFromCubeFace(gCubeFace, float2(u, v));
  73. dir = normalize(dir);
  74. // Need to calculate solid angle (weight) of the texel, as cube face corners have
  75. // much smaller solid angle, meaning many of them occupy the same area when projected
  76. // on a sphere. Without weighing that area would look too bright.
  77. float weight = texelSolidAngle(u, v, invFaceSize);
  78. SHVector shBasis = SHBasis(dir);
  79. float3 radiance = gInputTex.SampleLevel(gInputSamp, dir, 0).rgb;
  80. SHMultiplyAdd(data.coeffs.R, shBasis, radiance.r * weight);
  81. SHMultiplyAdd(data.coeffs.G, shBasis, radiance.g * weight);
  82. SHMultiplyAdd(data.coeffs.B, shBasis, radiance.b * weight);
  83. data.weight += weight;
  84. }
  85. }
  86. sCoeffs[groupIdx] = data;
  87. GroupMemoryBarrierWithGroupSync();
  88. int numThreads = TILE_WIDTH * TILE_HEIGHT;
  89. [unroll]
  90. for(int tc = numThreads / 2; tc > 0; tc >>= 1)
  91. {
  92. if(groupIdx < tc)
  93. {
  94. SHAdd(sCoeffs[groupIdx].coeffs.R, sCoeffs[groupIdx + tc].coeffs.R);
  95. SHAdd(sCoeffs[groupIdx].coeffs.G, sCoeffs[groupIdx + tc].coeffs.G);
  96. SHAdd(sCoeffs[groupIdx].coeffs.B, sCoeffs[groupIdx + tc].coeffs.B);
  97. sCoeffs[groupIdx].weight += sCoeffs[groupIdx + tc].weight;
  98. }
  99. GroupMemoryBarrierWithGroupSync();
  100. }
  101. if(groupIdx == 0)
  102. {
  103. uint faceOffset = gDispatchSize.x * gDispatchSize.y * gCubeFace;
  104. uint outputIdx = faceOffset + groupId.y * gDispatchSize.x + groupId.x;
  105. gOutput[outputIdx] = sCoeffs[0];
  106. }
  107. }
  108. };
  109. };