IrradianceComputeSH.bsl 4.0 KB

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