Ingen beskrivning
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

QuasiRandom.hlsl 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #ifndef _SAMPLING_RANDOM_HLSL_
  2. #define _SAMPLING_RANDOM_HLSL_
  3. #include "Common.hlsl"
  4. #include "Hashes.hlsl"
  5. // Low discrepancy sequence generator with various implementations available
  6. /* One of the following must be defined by the file that includes QuasiRandom.hlsl to select an implementation:
  7. - QRNG_METHOD_SOBOL (Sobol sampler with Owen scrambling, from paper: Practical Hash-based Owen Scrambling by Burley)
  8. infinite dims, 2097151 max samples, pixel tiling wraps at 65536
  9. define QRNG_SOBOL_GENERATIVE_DIMS <multipleOf2> to control how many source dimensions are used, Default value 1024
  10. - QRNG_METHOD_SOBOL_BLUE_NOISE (from paper: "A Low-Discrepancy Sampler that Distributes Monte Carlo Errors as a Blue Noise in Screen Space" by Heitz and Belcour)
  11. 256 max dims, 256 max samples (beyond 256, the sequence keeps going with another set of 256 samples belonging to another dim, and so on every 256 samples), pixel tiling wraps at 128
  12. - QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE (from paper: "Screen-Space Blue-Noise Diffusion of Monte Carlo Sampling Error via Hierarchical Ordering of Pixels" by Ahmed and Wonka)
  13. infinite dims and samples, pixel tiling depends on target sample count. The more samples, the smaller the tile (ex: for 256 samples, tiling size is 4096)
  14. define QRNG_GLOBAL_SOBOL_ENHANCED_TILING to get tiling to always wrap at 65536
  15. define QRNG_SOBOL_GENERATIVE_DIMS <multipleOf2> to control how many source dimensions are used, Default value 1024
  16. - QRNG_METHOD_KRONECKER (Kronecker sequence from paper "Optimizing Kronecker Sequences for Multidimensional Sampling")
  17. fast but lower quality than Sobol, infinite dims and samples, pixel tiling wraps at 65536
  18. define QRNG_KRONECKER_ENHANCED_QUALITY to add small scale jitter
  19. These last 2 aren't low discrepancy sequences but traditional Pseudorandom number generators
  20. - QRNG_METHOD_RANDOM_XOR_SHIFT Xor shift PRNG
  21. - QRNG_METHOD_RANDOM_PCG_4D (from paper: "Hash Functions for GPU Rendering" by Jarzynski & Olano)
  22. */
  23. #if defined(QRNG_METHOD_SOBOL) || defined(QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE)
  24. #include "SobolSampling.hlsl"
  25. #ifndef QRNG_SOBOL_GENERATIVE_DIMS
  26. #define QRNG_SOBOL_GENERATIVE_DIMS SOBOL_MATRICES_COUNT
  27. #endif
  28. static const uint kMaxSobolDim = QRNG_SOBOL_GENERATIVE_DIMS;
  29. #endif
  30. uint PixelHash(uint2 pixelCoord)
  31. {
  32. return LowBiasHash32((pixelCoord.x & 0xFFFF) | (pixelCoord.y << 16));
  33. }
  34. #if defined(QRNG_METHOD_SOBOL)
  35. struct QuasiRandomGenerator
  36. {
  37. uint pixelSeed;
  38. uint sampleIndex;
  39. void Init(uint2 pixelCoord, uint startSampleIndex)
  40. {
  41. pixelSeed = PixelHash(pixelCoord);
  42. sampleIndex = startSampleIndex;
  43. }
  44. float GetFloat(uint dimension)
  45. {
  46. uint scrambleSeed = LowBiasHash32(pixelSeed, dimension);
  47. uint shuffleSeed = pixelSeed;
  48. return GetOwenScrambledSobolSample(sampleIndex ^ shuffleSeed, dimension % kMaxSobolDim, scrambleSeed);
  49. }
  50. void NextSample()
  51. {
  52. sampleIndex++;
  53. }
  54. };
  55. #elif defined(QRNG_METHOD_SOBOL_BLUE_NOISE)
  56. #include "SobolBluenoiseSampling.hlsl"
  57. struct QuasiRandomGenerator
  58. {
  59. uint2 pixelCoord;
  60. uint sampleIndex;
  61. void Init(uint2 pixelCoord_, uint startSampleIndex)
  62. {
  63. pixelCoord = pixelCoord_;
  64. sampleIndex = startSampleIndex;
  65. }
  66. float GetFloat(uint dimension)
  67. {
  68. // If we go past the number of stored samples per dim, just shift all to the next pair of dimensions
  69. dimension += (sampleIndex / 256) * 2;
  70. return GetBNDSequenceSample(pixelCoord, sampleIndex, dimension);
  71. }
  72. void NextSample()
  73. {
  74. sampleIndex++;
  75. }
  76. };
  77. #elif defined(QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE)
  78. struct QuasiRandomGenerator
  79. {
  80. uint pixelMortonCode;
  81. uint log2SamplesPerPixel;
  82. uint sampleIndex;
  83. void Init(uint2 pixelCoord, uint startSampleIndex, uint perPixelSampleCount = 256)
  84. {
  85. pixelMortonCode = EncodeMorton2D(pixelCoord);
  86. log2SamplesPerPixel = Log2IntUp(perPixelSampleCount);
  87. sampleIndex = startSampleIndex;
  88. }
  89. float GetFloat(uint dimension)
  90. {
  91. return GetOwenScrambledZShuffledSobolSample(sampleIndex, dimension, kMaxSobolDim, pixelMortonCode, log2SamplesPerPixel);
  92. }
  93. void NextSample()
  94. {
  95. sampleIndex++;
  96. }
  97. };
  98. #elif defined(QRNG_METHOD_KRONECKER)
  99. struct QuasiRandomGenerator
  100. {
  101. uint cranleyPattersonSeed;
  102. uint shuffledSampleIndex;
  103. #ifdef QRNG_KRONECKER_ENHANCED_QUALITY
  104. int sampleIndex;
  105. #endif
  106. void Init(uint2 pixelCoord, uint startSampleIndex)
  107. {
  108. uint hash = PixelHash(pixelCoord);
  109. cranleyPattersonSeed = hash;
  110. uint shuffledStartIndex = (startSampleIndex + hash) % (1 << 20);
  111. shuffledSampleIndex = shuffledStartIndex;
  112. #ifdef QRNG_KRONECKER_ENHANCED_QUALITY
  113. sampleIndex = startSampleIndex+1;
  114. #endif
  115. }
  116. float GetFloat(uint dimension)
  117. {
  118. const uint alphas[]= { // values are stored multiplied by (1 << 32)
  119. // R2 from http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
  120. 3242174889, 2447445414,
  121. // K21_2 from Optimizing Kronecker Sequences for Multidimensional Sampling
  122. 3316612456, 1538627358,
  123. };
  124. // compute random offset to apply to the sequence (using another Kronecker sequence)
  125. uint cranleyPattersonRot = cranleyPattersonSeed + 3646589397 * (dimension / 4);
  126. #ifdef QRNG_KRONECKER_ENHANCED_QUALITY // add small scale jitter as explained in paper
  127. const float alphaJitter[] = { 2681146017, 685201898 };
  128. uint jitter = alphaJitter[dimension % 2] * shuffledSampleIndex;
  129. float amplitude = 0.05 * 0.78 / sqrt(2) * rsqrt(float(sampleIndex));
  130. cranleyPattersonRot += jitter * uint(amplitude);
  131. #endif
  132. // Kronecker sequence evaluation
  133. return UintToFloat01(cranleyPattersonRot + alphas[dimension % 4] * shuffledSampleIndex);
  134. }
  135. void NextSample()
  136. {
  137. // shuffledSampleIndex modulo 1048576 to avoid numerical precision issues when evaluating the Kronecker sequence
  138. shuffledSampleIndex = (shuffledSampleIndex + 1) % (1 << 20);
  139. #ifdef QRNG_KRONECKER_ENHANCED_QUALITY
  140. sampleIndex++;
  141. #endif
  142. }
  143. };
  144. #elif defined(QRNG_METHOD_RANDOM_XOR_SHIFT)
  145. struct QuasiRandomGenerator
  146. {
  147. uint state;
  148. void Init(uint2 pixelCoord, uint startSampleIndex)
  149. {
  150. state = PixelHash(pixelCoord);
  151. }
  152. float GetFloat(uint dimension)
  153. {
  154. state = XorShift32(state);
  155. return UintToFloat01(state);
  156. }
  157. void NextSample()
  158. {
  159. }
  160. };
  161. #elif defined(QRNG_METHOD_RANDOM_PCG_4D)
  162. struct QuasiRandomGenerator
  163. {
  164. uint4 state;
  165. void Init(uint2 pixelCoord, uint startSampleIndex)
  166. {
  167. // Seed for PCG uses a sequential sample number in 4th channel, which increments on every RNG call and starts from 0
  168. state = uint4(pixelCoord, startSampleIndex, 0);
  169. }
  170. float GetFloat(int dimension)
  171. {
  172. state.w++;
  173. return UintToFloat01(Pcg4d(state).x);
  174. }
  175. void NextSample()
  176. {
  177. state.z++;
  178. }
  179. };
  180. #endif
  181. // global dimension offset (could be used to alter the noise pattern)
  182. #ifndef QRNG_OFFSET
  183. #define QRNG_OFFSET 0
  184. #endif
  185. #ifndef QRNG_SAMPLES_PER_BOUNCE
  186. #define QRNG_SAMPLES_PER_BOUNCE 64
  187. #endif
  188. struct PathTracingSampler
  189. {
  190. QuasiRandomGenerator generator;
  191. int bounceIndex;
  192. void Init(uint2 pixelCoord, uint startPathIndex, uint perPixelPathCount = 256)
  193. {
  194. #if defined(QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE)
  195. generator.Init(pixelCoord, startPathIndex, perPixelPathCount);
  196. #else
  197. generator.Init(pixelCoord, startPathIndex);
  198. #endif
  199. bounceIndex = 0;
  200. }
  201. float GetFloatSample(int dimension)
  202. {
  203. uint actualDimension = QRNG_OFFSET + QRNG_SAMPLES_PER_BOUNCE * bounceIndex + dimension;
  204. return generator.GetFloat(actualDimension);
  205. }
  206. void NextBounce()
  207. {
  208. bounceIndex++;
  209. }
  210. void NextPath()
  211. {
  212. generator.NextSample();
  213. bounceIndex = 0;
  214. }
  215. };
  216. #endif // _SAMPLING_RANDOM_HLSL_