MagicBloom-optimized.fx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*
  2. Magic Bloom by luluco250
  3. Attempts to simulate a natural-looking bloom.
  4. Features:
  5. --Wide bloom blurring, derived from the gaussian function
  6. defined here: https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics
  7. --Eye adaptation, decreases or increases the brightness
  8. of bloom according to the overall image luminance.
  9. --Lens dirt, as standard I suppose. Really not much here.
  10. It uses an image named "MagicBloom_Dirt.png" so make
  11. sure you have one in your textures directory.
  12. --Unwanted features can be disabled through
  13. preprocessor definitions, saving performance.
  14. Preprocessor definitions:
  15. --MAGICBLOOM_ADAPT_RESOLUTION:
  16. Determines the width/height of the texture used for adaptation.
  17. It is recommended to use 256, but you can use as far as 1024 without issues.
  18. Too low resolutions will make adaptation seem "unstable".
  19. Must be a power of two value: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 etc.
  20. --MAGICBLOOM_BLUR_PRECALCULATED:
  21. If set to 0 the gaussian blur will be calculated inside the shader.
  22. Otherwise, it uses a pre-calculated kernel (array).
  23. --MAGICBLOOM_NODIRT:
  24. If set to 1 all lens dirt related features are disabled.
  25. Beneficial for performance if you don't wish to use lens dirt.
  26. --MAGICBLOOM_NOADAPT:
  27. If set to 1 all adaptation related features are disabled.
  28. Beneficial for performance if you don't wish to use adaptation.
  29. MIT Licensed:
  30. Copyright (c) 2017 luluco250
  31. Permission is hereby granted, free of charge, to any person obtaining a copy
  32. of this software and associated documentation files (the "Software"), to deal
  33. in the Software without restriction, including without limitation the rights
  34. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  35. copies of the Software, and to permit persons to whom the Software is
  36. furnished to do so, subject to the following conditions:
  37. The above copyright notice and this permission notice shall be included in all
  38. copies or substantial portions of the Software.
  39. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  40. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  41. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  42. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  43. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  44. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  45. SOFTWARE.
  46. */
  47. #include "ReShade.fxh"
  48. //Statics
  49. #ifndef MAGICBLOOM_ADAPT_RESOLUTION
  50. #define MAGICBLOOM_ADAPT_RESOLUTION 64
  51. #endif
  52. #ifndef MAGICBLOOM_BLUR_PRECALCULATED
  53. #define MAGICBLOOM_BLUR_PRECALCULATED 1
  54. #endif
  55. #ifndef MAGICBLOOM_NODIRT
  56. #define MAGICBLOOM_NODIRT 1
  57. #endif
  58. #ifndef MAGICBLOOM_NOADAPT
  59. #define MAGICBLOOM_NOADAPT 0
  60. #endif
  61. static const int iBlurSamples = 3;
  62. static const int iAdaptResolution = MAGICBLOOM_ADAPT_RESOLUTION;
  63. #define CONST_LOG2(v) (((v) & 0xAAAAAAAA) != 0) | ((((v) & 0xFFFF0000) != 0) << 4) | ((((v) & 0xFF00FF00) != 0) << 3) | ((((v) & 0xF0F0F0F0) != 0) << 2) | ((((v) & 0xCCCCCCCC) != 0) << 1)
  64. static const float sigma = float(iBlurSamples) / 2.0;
  65. static const float double_pi = 6.283185307179586476925286766559;
  66. static const int lowest_mip = CONST_LOG2(iAdaptResolution) + 1;
  67. static const float3 luma_value = float3(0.2126, 0.7152, 0.0722);
  68. //Uniforms
  69. #include "ReShadeUI.fxh"
  70. uniform float fBloom_Intensity < __UNIFORM_SLIDER_FLOAT1
  71. ui_label = "Bloom Intensity";
  72. ui_tooltip = "Amount of bloom applied to the image.";
  73. ui_min = 0.0;
  74. ui_max = 10.0;
  75. ui_step = 0.001;
  76. > = 1.0;
  77. uniform float fBloom_Threshold <
  78. ui_label = "Bloom Threshold";
  79. ui_tooltip =
  80. "Thresholds (limits) dark pixels from being accounted for bloom.\n"
  81. "Essentially, it increases the contrast in bloom and blackens darker pixels.\n"
  82. "At 1.0 all pixels are used in bloom.\n"
  83. "This value is not normalized, it is exponential, therefore changes in lower values are more noticeable than at higher values.";
  84. ui_type = "drag";
  85. ui_min = 1.0;
  86. ui_max = 10.0;
  87. ui_step = 0.1;
  88. > = 2.0;
  89. #if !MAGICBLOOM_NODIRT
  90. uniform float fDirt_Intensity < __UNIFORM_SLIDER_FLOAT1
  91. ui_label = "Dirt Intensity";
  92. ui_tooltip =
  93. "Amount of lens dirt applied to bloom.\n"
  94. "Uses a texture called \"MagicBloom_Dirt.png\" from your textures directory(ies).";
  95. ui_min = 0.0;
  96. ui_max = 1.0;
  97. ui_step = 0.001;
  98. > = 0.0;
  99. #endif
  100. #if !MAGICBLOOM_NOADAPT
  101. uniform float fExposure < __UNIFORM_SLIDER_FLOAT1
  102. ui_label = "Exposure";
  103. ui_tooltip =
  104. "The target exposure that bloom adapts to.\n"
  105. "It is recommended to just leave it at 0.5, unless you wish for a brighter (1.0) or darker (0.0) image.";
  106. ui_min = 0.0;
  107. ui_max = 1.0;
  108. ui_step = 0.001;
  109. > = 0.5;
  110. uniform float fAdapt_Speed <
  111. ui_label = "Adaptation Speed";
  112. ui_tooltip =
  113. "How quick bloom adapts to changes in the image brightness.\n"
  114. "At 1.0, changes are instantaneous.\n"
  115. "It is recommended to use low values, between 0.01 and 0.1.\n"
  116. "0.1 will provide a quick but natural adaptation.\n"
  117. "0.01 will provide a slow form of adaptation.";
  118. ui_type = "drag";
  119. ui_min = 0.001;
  120. ui_max = 1.0;
  121. ui_step = 0.001;
  122. > = 0.1;
  123. uniform float fAdapt_Sensitivity < __UNIFORM_SLIDER_FLOAT1
  124. ui_label = "Adapt Sensitivity";
  125. ui_tooltip =
  126. "How sensitive adaptation is towards brightness.\n"
  127. "At higher values bloom can get darkened at the slightest amount of brightness.\n"
  128. "At lower values bloom will require a lot of image brightness before it's fully darkened."
  129. "1.0 will not modify the amount of brightness that is accounted for adaptation.\n"
  130. "0.5 is a good value, but may miss certain bright spots.";
  131. ui_min = 0.0;
  132. ui_max = 3.0;
  133. ui_step = 0.001;
  134. > = 1.0;
  135. uniform float2 f2Adapt_Clip < __UNIFORM_SLIDER_FLOAT2
  136. ui_label = "Adaptation Min/Max";
  137. ui_tooltip =
  138. "Determines the minimum and maximum values that adaptation can determine to ajust bloom.\n"
  139. "Reducing the maximum would cause bloom to be brighter (as it is less adapted).\n"
  140. "Increasing the minimum would cause bloom to be darker (as it is more adapted).\n"
  141. "Keep the maximum above or equal to the minium and vice-versa.";
  142. ui_min = 0.0;
  143. ui_max = 1.0;
  144. ui_step = 0.001;
  145. > = float2(0.0, 1.0);
  146. uniform int iAdapt_Precision < __UNIFORM_SLIDER_INT1
  147. ui_label = "Adaptation Precision";
  148. ui_tooltip =
  149. "Determins how accurately bloom adapts to the center of image.\n"
  150. "At 0 the adaptation is calculated from the average of the whole image.\n"
  151. "At the highest value (which may vary) adaptation focuses solely on the center pixel(s) of the screen.\n"
  152. "Values closer to 0 are recommended.";
  153. ui_min = 0;
  154. ui_max = lowest_mip;
  155. ui_step = 0.1;
  156. > = lowest_mip * 0.3;
  157. #endif
  158. //Textures
  159. texture tMagicBloom_1 < pooled = true; > { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RGBA16F; };
  160. texture tMagicBloom_2 < pooled = true; > { Width = BUFFER_WIDTH / 4; Height = BUFFER_HEIGHT / 4; Format = RGBA16F; };
  161. texture tMagicBloom_3 < pooled = true; > { Width = BUFFER_WIDTH / 8; Height = BUFFER_HEIGHT / 8; Format = RGBA16F; };
  162. texture tMagicBloom_4 < pooled = true; > { Width = BUFFER_WIDTH / 16; Height = BUFFER_HEIGHT / 16; Format = RGBA16F; };
  163. texture tMagicBloom_5 < pooled = true; > { Width = BUFFER_WIDTH / 32; Height = BUFFER_HEIGHT / 32; Format = RGBA16F; };
  164. texture tMagicBloom_6 < pooled = true; > { Width = BUFFER_WIDTH / 64; Height = BUFFER_HEIGHT / 64; Format = RGBA16F; };
  165. texture tMagicBloom_7 < pooled = true; > { Width = BUFFER_WIDTH / 128; Height = BUFFER_HEIGHT / 128; Format = RGBA16F; };
  166. texture tMagicBloom_8 < pooled = true; > { Width = BUFFER_WIDTH / 256; Height = BUFFER_HEIGHT / 256; Format = RGBA16F; };
  167. #if !MAGICBLOOM_NOADAPT
  168. texture tMagicBloom_Small { Width = iAdaptResolution; Height = iAdaptResolution; Format = R32F; MipLevels = lowest_mip; };
  169. texture tMagicBloom_Adapt { Format = R32F; };
  170. texture tMagicBloom_LastAdapt { Format = R32F; };
  171. #endif
  172. #if !MAGICBLOOM_NODIRT
  173. texture tMagicBloom_Dirt <source="MagicBloom_Dirt.png";> { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; };
  174. #endif
  175. //Samplers
  176. sampler sMagicBloom_1 { Texture = tMagicBloom_1; };
  177. sampler sMagicBloom_2 { Texture = tMagicBloom_2; };
  178. sampler sMagicBloom_3 { Texture = tMagicBloom_3; };
  179. sampler sMagicBloom_4 { Texture = tMagicBloom_4; };
  180. sampler sMagicBloom_5 { Texture = tMagicBloom_5; };
  181. sampler sMagicBloom_6 { Texture = tMagicBloom_6; };
  182. sampler sMagicBloom_7 { Texture = tMagicBloom_7; };
  183. sampler sMagicBloom_8 { Texture = tMagicBloom_8; };
  184. #if !MAGICBLOOM_NOADAPT
  185. sampler sMagicBloom_Small { Texture = tMagicBloom_Small; };
  186. sampler sMagicBloom_Adapt { Texture = tMagicBloom_Adapt; MinFilter = POINT; MagFilter = POINT; };
  187. sampler sMagicBloom_LastAdapt { Texture = tMagicBloom_LastAdapt; MinFilter = POINT; MagFilter = POINT; };
  188. #endif
  189. #if !MAGICBLOOM_NODIRT
  190. sampler sMagicBloom_Dirt { Texture = tMagicBloom_Dirt; };
  191. #endif
  192. //Functions
  193. #if !MAGICBLOOM_BLUR_PRECALCULATED
  194. float gaussian_function(float2 i) {
  195. static const float first_part = 1.0 / (double_pi * pow(sigma, 2.0));
  196. static const float second_part_a = 1.0 / (2.0 * pow(sigma, 2.0));
  197. float second_part_b = (pow(i.x, 2.0) + pow(i.y, 2.0)) * second_part_a;
  198. return first_part * exp(-second_part_b);
  199. }
  200. #endif
  201. //Why use a single-pass blur? To reduce the amount of textures used in half.
  202. //Scale should be the original resolution divided by target resolution.
  203. float3 blur(sampler sp, float2 uv, float scale) {
  204. float2 ps = BUFFER_PIXEL_SIZE * scale;
  205. #if MAGICBLOOM_BLUR_PRECALCULATED
  206. static const float kernel[9] = {
  207. 0.0269955, 0.0647588, 0.120985, 0.176033, 0.199471, 0.176033, 0.120985, 0.0647588, 0.0269955
  208. };
  209. static const float accum = 1.02352;
  210. #else
  211. float accum = 0.0;
  212. #endif
  213. float gaussian_weight = 0.0;
  214. float3 col = 0.0;
  215. [loop]
  216. for (int x = -iBlurSamples; x <= iBlurSamples; ++x) {
  217. for (int y = -iBlurSamples; y <= iBlurSamples; ++y) {
  218. #if MAGICBLOOM_BLUR_PRECALCULATED
  219. gaussian_weight = kernel[x + iBlurSamples] * kernel[y + iBlurSamples];
  220. #else
  221. gaussian_weight = gaussian_function(float2(x, y));
  222. accum += gaussian_weight;
  223. #endif
  224. col += tex2D(sp, uv + ps * float2(x, y)).rgb * gaussian_weight;
  225. }
  226. }
  227. #if MAGICBLOOM_BLUR_PRECALCULATED
  228. return col * accum;
  229. #else
  230. return col / accum;
  231. #endif
  232. }
  233. /*
  234. Uncharted 2 Tonemapper
  235. Thanks John Hable and Naughty Dog.
  236. */
  237. float3 tonemap(float3 col, float exposure) {
  238. static const float A = 0.15; //shoulder strength
  239. static const float B = 0.50; //linear strength
  240. static const float C = 0.10; //linear angle
  241. static const float D = 0.20; //toe strength
  242. static const float E = 0.02; //toe numerator
  243. static const float F = 0.30; //toe denominator
  244. static const float W = 11.2; //linear white point value
  245. col *= exposure;
  246. col = ((col * (A * col + C * B) + D * E) / (col * (A * col + B) + D * F)) - E / F;
  247. static const float white = 1.0 / (((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F);
  248. col *= white;
  249. return col;
  250. }
  251. float3 blend_screen(float3 a, float3 b) {
  252. return 1.0 - (1.0 - a) * (1.0 - b);
  253. }
  254. /*
  255. The function below is a leftover from debugging.
  256. It just draws a line on the screen, it's horizontal position being
  257. the value you specify (from 0.0-1.0, becoming left-right).
  258. No use now but might be useful later on so I just left it here.
  259. */
  260. /*void debug_value(inout float3 col, float2 uv, float value, float3 needle_color) {
  261. static const float2 ps = BUFFER_PIXEL_SIZE;
  262. col = (uv.x + ps.x > value && uv.x - ps.x < value) ? needle_color : col;
  263. }*/
  264. //Shaders
  265. /*
  266. Thresholding is performed on the first blur for two reasons:
  267. --Save an entire texture from being used to threshold.
  268. --Being the smallest blur it also results in the least amount of artifacts.
  269. */
  270. float4 PS_Blur1(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  271. float3 col = blur(ReShade::BackBuffer, uv, 2.0);
  272. col = pow(abs(col), fBloom_Threshold);
  273. col *= fBloom_Intensity;
  274. return float4(col, 1.0);
  275. }
  276. float4 PS_Blur2(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  277. return float4(blur(sMagicBloom_1, uv, 4.0), 1.0);
  278. }
  279. float4 PS_Blur3(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  280. return float4(blur(sMagicBloom_2, uv, 8.0), 1.0);
  281. }
  282. float4 PS_Blur4(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  283. return float4(blur(sMagicBloom_3, uv, 8.0), 1.0);
  284. }
  285. float4 PS_Blur5(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  286. return float4(blur(sMagicBloom_4, uv, 16.0), 1.0);
  287. }
  288. float4 PS_Blur6(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  289. return float4(blur(sMagicBloom_5, uv, 32.0), 1.0);
  290. }
  291. float4 PS_Blur7(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  292. return float4(blur(sMagicBloom_6, uv, 64.0), 1.0);
  293. }
  294. float4 PS_Blur8(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  295. return float4(blur(sMagicBloom_7, uv, 128.0), 1.0);
  296. }
  297. //Final blend shader
  298. float4 PS_Blend(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  299. float3 col = tex2D(ReShade::BackBuffer, uv).rgb;
  300. float3 bloom = tex2D(sMagicBloom_1, uv).rgb
  301. + tex2D(sMagicBloom_2, uv).rgb
  302. + tex2D(sMagicBloom_3, uv).rgb
  303. + tex2D(sMagicBloom_4, uv).rgb
  304. + tex2D(sMagicBloom_5, uv).rgb
  305. + tex2D(sMagicBloom_6, uv).rgb
  306. + tex2D(sMagicBloom_7, uv).rgb
  307. + tex2D(sMagicBloom_8, uv).rgb;
  308. //Dunno if making the division by 8 a static multiplication helps, but whatever.
  309. static const float bloom_accum = 1.0 / 8.0;
  310. bloom *= bloom_accum;
  311. #if !MAGICBLOOM_NOADAPT
  312. float exposure = fExposure / max(tex2D(sMagicBloom_Adapt, 0.0).x, 0.00001);
  313. bloom = tonemap(bloom, exposure);
  314. #else
  315. //Without adaptation it seems 100.0 exposure is needed for bloom to look bright enough.
  316. bloom = tonemap(bloom, 100.0);
  317. #endif
  318. #if !MAGICBLOOM_NODIRT
  319. float3 dirt = tex2D(sMagicBloom_Dirt, uv).rgb;
  320. dirt *= fDirt_Intensity;
  321. bloom = blend_screen(bloom, dirt * bloom);
  322. #endif
  323. col = blend_screen(col, bloom);
  324. return float4(col, 1.0);
  325. }
  326. #if !MAGICBLOOM_NOADAPT
  327. /*
  328. How adaptation works:
  329. --Calculate image luminance.
  330. --Save it to a smaller, mipmapped texture.
  331. --Mipmaps require a power of 2 texture.
  332. --Fetch a mipmap level according to a specfied amount of precision.
  333. --The lowest mipmap is simply an average of the entire image.
  334. */
  335. float PS_GetSmall(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  336. return dot(tex2D(ReShade::BackBuffer, uv).rgb, luma_value);
  337. }
  338. float PS_GetAdapt(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  339. float curr = tex2Dlod(sMagicBloom_Small, float4(0.5, 0.5, 0, lowest_mip - iAdapt_Precision)).x;
  340. curr *= fAdapt_Sensitivity;
  341. curr = clamp(curr, f2Adapt_Clip.x, f2Adapt_Clip.y);
  342. float last = tex2D(sMagicBloom_LastAdapt, 0.0).x;
  343. //Using the frametime/delta here would actually scale adaptation with the framerate.
  344. //We don't want that, so we don't even bother with it.
  345. return lerp(last, curr, fAdapt_Speed);
  346. }
  347. float PS_SaveAdapt(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
  348. return tex2D(sMagicBloom_Adapt, 0.0).x;
  349. }
  350. #endif
  351. technique MagicBloom {
  352. pass Blur1 {
  353. VertexShader = PostProcessVS;
  354. PixelShader = PS_Blur1;
  355. RenderTarget = tMagicBloom_1;
  356. }
  357. pass Blur2 {
  358. VertexShader = PostProcessVS;
  359. PixelShader = PS_Blur2;
  360. RenderTarget = tMagicBloom_2;
  361. }
  362. pass Blur3 {
  363. VertexShader = PostProcessVS;
  364. PixelShader = PS_Blur3;
  365. RenderTarget = tMagicBloom_3;
  366. }
  367. pass Blur4 {
  368. VertexShader = PostProcessVS;
  369. PixelShader = PS_Blur4;
  370. RenderTarget = tMagicBloom_4;
  371. }
  372. pass Blur5 {
  373. VertexShader = PostProcessVS;
  374. PixelShader = PS_Blur5;
  375. RenderTarget = tMagicBloom_5;
  376. }
  377. pass Blur6 {
  378. VertexShader = PostProcessVS;
  379. PixelShader = PS_Blur6;
  380. RenderTarget = tMagicBloom_6;
  381. }
  382. pass Blur7 {
  383. VertexShader = PostProcessVS;
  384. PixelShader = PS_Blur7;
  385. RenderTarget = tMagicBloom_7;
  386. }
  387. pass Blur8 {
  388. VertexShader = PostProcessVS;
  389. PixelShader = PS_Blur8;
  390. RenderTarget = tMagicBloom_8;
  391. }
  392. pass Blend {
  393. VertexShader = PostProcessVS;
  394. PixelShader = PS_Blend;
  395. }
  396. #if !MAGICBLOOM_NOADAPT
  397. pass GetSmall {
  398. VertexShader = PostProcessVS;
  399. PixelShader = PS_GetSmall;
  400. RenderTarget = tMagicBloom_Small;
  401. }
  402. pass GetAdapt {
  403. VertexShader = PostProcessVS;
  404. PixelShader = PS_GetAdapt;
  405. RenderTarget = tMagicBloom_Adapt;
  406. }
  407. pass SaveAdapt {
  408. VertexShader = PostProcessVS;
  409. PixelShader = PS_SaveAdapt;
  410. RenderTarget = tMagicBloom_LastAdapt;
  411. }
  412. #endif
  413. }