├── Textures ├── nose.png └── matcap.png ├── README.md └── Shaders ├── LightGunFrame.fx ├── VR_nose.fx ├── ScreenShake.fx ├── FilmicBloom.fx ├── FilmicGrade.fx ├── RimLight.fx ├── SimpleBloom.fx ├── BeforeAfterWithDepth.fx ├── RealGrain.fx ├── LocalContrast.fx ├── Reflection.fx ├── ThinFilm.fx └── VR.fx /Textures/nose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fubaxiusz/fubax-shaders-dev/HEAD/Textures/nose.png -------------------------------------------------------------------------------- /Textures/matcap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fubaxiusz/fubax-shaders-dev/HEAD/Textures/matcap.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Fubax ReShade FX shaders (in development) 2 | ========================================= 3 | 4 | This repository collects post-processing shaders written in the ReShade FX shader language, that are not really polished. 5 | 6 | To get ReShade visit https://reshade.me 7 | 8 | To see more polished shaders https://github.com/Fubaxiusz/fubax-shaders 9 | 10 | Installation 11 | ------------ 12 | 13 | 1. [Download](https://github.com/Fubaxiusz/fubax-shaders-dev/archive/master.zip) this repository 14 | 2. Extract the downloaded archive file somewhere 15 | 3. Start your game, open the ReShade in-game menu and switch to the "Settings" tab 16 | 4. Add the path to the extracted [Shaders](/Shaders) folder to "Effect Search Paths" 17 | 5. Add the path to the extracted [Textures](/Textures) folder to "Texture Search Paths" 18 | 6. Switch back to the "Home" tab and click on "Reload" to load the shaders 19 | 7. Make sure you have [ReShade standard effects](https://github.com/crosire/reshade-shaders/tree/slim) installed too. 20 | 21 | Syntax 22 | ------ 23 | 24 | Check out [the language reference document](https://github.com/crosire/reshade-shaders/blob/master/REFERENCE.md) to get started on how to write your own! 25 | 26 | 27 | Shaders (some of) 28 | ----------------- 29 | 30 | [Reflection (matcap)](/Shaders/Reflection.fx) 31 | --------------------- 32 | ![](https://i.imgur.com/DKsZBwE.jpg) 33 | 34 | [VR](/Shaders/VR.fx) 35 | ---- 36 | ![](https://i.imgur.com/GqOsqOT.jpg) 37 | 38 | [Contact me](mailto:jakub.m.fober@pm.me) 39 | -------------------------------------------------------------------------------- /Shaders/LightGunFrame.fx: -------------------------------------------------------------------------------- 1 | /* LightGun Frame PS, version 0.0.1 2 | by Jacob Max Fober 3 | */ 4 | 5 | #include "ReShade.fxh" 6 | #include "ReShadeUI.fxh" 7 | 8 | //////////// 9 | /// MENU /// 10 | //////////// 11 | 12 | uniform int threshold < __UNIFORM_SLIDER_INT1 13 | ui_label = "Border scale (%)"; 14 | ui_tooltip = "White borders scale in screen percentage"; 15 | ui_min = 0; ui_max = 20; 16 | > = 2; 17 | 18 | ///////////////// 19 | /// FUNCTIONS /// 20 | ///////////////// 21 | 22 | // Pixel-wide step function by JMF 23 | float linearstep(float edge, float x) 24 | { 25 | x -= edge; 26 | return clamp(x/fwidth(x), 0.0, 1.0); 27 | } 28 | 29 | ////////////// 30 | /// SHADER /// 31 | ////////////// 32 | 33 | void LightGunBorderPS(float4 pos : SV_Position, float2 texCoord : TEXCOORD, out float3 color : SV_Target) 34 | { 35 | // Panorama/portrait aspect ratio compensation 36 | float2 aspectRatio = float2(BUFFER_ASPECT_RATIO, 1.0/BUFFER_ASPECT_RATIO); 37 | aspectRatio = max(aspectRatio, 1.0); 38 | 39 | // Denominator for pixel linear step 40 | float2 pixelSize = BUFFER_PIXEL_SIZE*2.0; 41 | pixelSize *= aspectRatio; 42 | 43 | float2 border = 1.0-abs(texCoord*2.0-1.0); 44 | border *= aspectRatio; 45 | border -= threshold*0.01; // Convert percent to value 46 | 47 | // Perform pixel-wide step function 48 | border = clamp(border/pixelSize, 0.0, 1.0); 49 | 50 | color = lerp( 51 | 1.0, // White border 52 | tex2D(ReShade::BackBuffer, texCoord).rgb, // Background 53 | min(border.x, border.y) // Border mask 54 | ); 55 | } 56 | 57 | ////////////// 58 | /// OUTPUT /// 59 | ////////////// 60 | 61 | technique LightGunFrame 62 | < 63 | ui_label = "LightGun frame"; 64 | ui_tooltip = "Experimental"; 65 | > 66 | { 67 | pass 68 | { 69 | VertexShader = PostProcessVS; 70 | PixelShader = LightGunBorderPS; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Shaders/VR_nose.fx: -------------------------------------------------------------------------------- 1 | /* 2 | Nose PS (c) 2019 Jacob Maximilian Fober 3 | 4 | Anti-nausea shader for VR 5 | 6 | This work is licensed under the Creative Commons 7 | Attribution-ShareAlike 4.0 International License. 8 | To view a copy of this license, visit 9 | http://creativecommons.org/licenses/by-sa/4.0/. 10 | */ 11 | 12 | 13 | //////////// 14 | /// MENU /// 15 | //////////// 16 | 17 | #include "ReShadeUI.fxh" 18 | 19 | #ifndef nose 20 | #define nose 128 // Nose texture resolution 21 | #endif 22 | 23 | uniform float Brightness < 24 | ui_min = 0.0; ui_max = 1.0; ui_step = 0.002; 25 | ui_type = "drag"; 26 | ui_category = "Virtual nose adjustment"; 27 | > = 1.0; 28 | 29 | uniform float2 Scale < __UNIFORM_SLIDER_FLOAT2 30 | #if __RESHADE__ < 40000 31 | ui_step = 0.001; 32 | #endif 33 | ui_min = 0.1; ui_max = 1.0; 34 | ui_category = "Virtual nose adjustment"; 35 | > = float2(0.382, 0.618); 36 | 37 | 38 | ////////////// 39 | /// SHADER /// 40 | ////////////// 41 | 42 | texture NoseTex < source = "nose.png"; > {Width = nose; Height = nose;}; 43 | sampler NoseSampler { Texture = NoseTex; AddressU = CLAMP; AddressV = BORDER; }; 44 | 45 | // Convert RGB to YUV 46 | float3 yuv(float3 rgbImage) 47 | { 48 | // RGB to YUV709 matrix 49 | static const float3x3 YUV709 = 50 | float3x3( 51 | float3(0.2126, 0.7152, 0.0722), 52 | float3(-0.09991, -0.33609, 0.436), 53 | float3(0.615, -0.55861, -0.05639) 54 | ); 55 | return mul(YUV709, rgbImage); 56 | } 57 | 58 | // Overlay blending mode 59 | float Overlay(float LayerAB) 60 | { 61 | float MinAB = min(LayerAB, 0.5); 62 | float MaxAB = max(LayerAB, 0.5); 63 | return 2 * (MinAB*MinAB + 2*MaxAB - MaxAB*MaxAB) - 1.5; 64 | } 65 | 66 | #include "ReShade.fxh" 67 | 68 | float3 NosePS(float4 vois : SV_Position, float2 texcoord : TexCoord) : SV_Target 69 | { 70 | // Divide screen in two (mirrored) 71 | float2 StereoCoord = texcoord; 72 | StereoCoord.x = 1.0-abs(StereoCoord.x*2.0-1.0)/Scale.x; 73 | StereoCoord.y = 1.0-(1.0-StereoCoord.y)/Scale.y; 74 | 75 | // Sample display image 76 | float3 Display = tex2D(ReShade::BackBuffer, texcoord).rgb; 77 | // Sample nose texture 78 | float4 NoseTexture = tex2D(NoseSampler, StereoCoord); 79 | // Change skintone 80 | NoseTexture.rgb *= lerp(smoothstep(0.0, 1.0, yuv(NoseTexture.rgb).x), 1.0, Brightness); 81 | 82 | 83 | // Blend nose with display image 84 | return lerp(Display, NoseTexture.rgb, NoseTexture.a); 85 | } 86 | 87 | 88 | ////////////// 89 | /// OUTPUT /// 90 | ////////////// 91 | 92 | technique VR_nose < ui_label = "Virtual nose 4 VR"; ui_tooltip = "Virtual Reality:\n" 93 | "* can reduce nausea\n" 94 | "* highly recommended"; > 95 | { 96 | pass 97 | { 98 | VertexShader = PostProcessVS; 99 | PixelShader = NosePS; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Shaders/ScreenShake.fx: -------------------------------------------------------------------------------- 1 | /* Shoot Shake PS, version 1.0.1 2 | (c) 2019 Jakub Max Fober 3 | 4 | This work is licensed under a Creative Commons 5 | Attribution 4.0 International License. 6 | To view a copy of this license, visit 7 | http://creativecommons.org/licenses/by/4.0/ 8 | 9 | Please leave proper copyright somewhere appropriate. 10 | */ 11 | 12 | #include "ReShade.fxh" 13 | #include "ReShadeUI.fxh" 14 | 15 | texture texCamShakeFalloff { Width = 1; Height = 1; Format = R16F; }; 16 | sampler samperCamShakeFalloff 17 | { 18 | Texture = texCamShakeFalloff; 19 | SRGBTexture = false; 20 | MagFilter = POINT; 21 | MinFilter = POINT; 22 | MipFilter = POINT; 23 | }; 24 | uniform bool mouseClick < source = "mousebutton"; keycode = 0; mode = "press"; >; 25 | uniform float frameTime < source = "frametime"; >; 26 | 27 | ////////// 28 | // MENU // 29 | ////////// 30 | 31 | uniform float K0 < __UNIFORM_SLIDER_FLOAT1 32 | ui_label = "Shake scale"; 33 | ui_min = 0.01; ui_max = -0.25; 34 | > = -0.07; 35 | 36 | uniform float FalloffTime < __UNIFORM_SLIDER_FLOAT1 37 | ui_label = "Shake time"; 38 | ui_min = 0.07; ui_max = 0.1; 39 | > = 0.07; 40 | 41 | // uniform int Order < __UNIFORM_SLIDER_INT1 42 | // ui_label = "Shake radius"; 43 | // ui_min = 1; ui_max = 3; 44 | // > = 1; 45 | 46 | //////////// 47 | // SHADER // 48 | //////////// 49 | 50 | // Generate shake falloff 51 | float ClickFalloff (float4 pos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target 52 | { 53 | // Get current falloff value 54 | float falloff = tex2D(samperCamShakeFalloff, float2(0, 0)).r; 55 | 56 | // If mouse clicked, reset falloff 57 | if (mouseClick) falloff = 1.0; 58 | // Else apply falloff 59 | else falloff /= frameTime*FalloffTime; 60 | 61 | return falloff; 62 | } 63 | 64 | // Apply shake 65 | float3 ScreenShakePS (float4 pos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target 66 | { 67 | // Get diagonal length 68 | const float diagonal = length( float2(ReShade::AspectRatio, 1.0) ); 69 | // Center coordinates 70 | texcoord = texcoord*2.0-1.0; 71 | // Correct aspect ratio 72 | texcoord.x *= ReShade::AspectRatio; 73 | // Normalize diagonally 74 | texcoord /= diagonal; 75 | 76 | // Get falloff 77 | float falloff = tex2D(samperCamShakeFalloff, float2(0, 0)).r; 78 | // Apply Brown-Conrady distortion model 79 | // texcoord *= 1.0 + K0*pow(length(texcoord), Order)*falloff; 80 | texcoord *= 1.0 + K0*length(texcoord)*falloff; 81 | 82 | // Normalize vertically 83 | texcoord *= diagonal; 84 | // Aspect ratio back to square 85 | texcoord.x /= ReShade::AspectRatio; 86 | // Center back to corner 87 | texcoord = texcoord*0.5+0.5; 88 | 89 | return tex2D(ReShade::BackBuffer, texcoord).rgb; 90 | } 91 | 92 | //////////// 93 | // OUTPUT // 94 | //////////// 95 | 96 | technique ScreenShake 97 | < 98 | ui_label = "ScreenShake"; 99 | ui_tooltip = "Shakes the screen when mouse button pressed."; 100 | > 101 | { 102 | pass 103 | { 104 | VertexShader = PostProcessVS; 105 | PixelShader = ClickFalloff; 106 | RenderTarget = texCamShakeFalloff; 107 | ClearRenderTargets = false; 108 | BlendEnable = true; 109 | BlendOp = ADD; 110 | } 111 | pass 112 | { 113 | VertexShader = PostProcessVS; 114 | PixelShader = ScreenShakePS; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Shaders/FilmicBloom.fx: -------------------------------------------------------------------------------- 1 | /** Filmic Bloom PS, version 1.0.0 2 | All rights (c) 2020 Jakub Maksymilian Fober (the Author). 3 | 4 | The Author provides this shader (the Work) 5 | under the Creative Commons CC BY-ND 4.0 license available online at 6 | http://creatifloatommons.org/licenses/by-nd/4.0/. 7 | The Author further grants permission for reuse of screen-shots and game-play 8 | recordings derived from the Work, provided that the reuse is for the purpose of 9 | promoting and/or summarizing the Work or is a part of an online forum post or 10 | social media post and that any use is accompanied by a link to the Work and a 11 | credit to the Author. (crediting Author by pseudonym "Fubax" is acceptable) 12 | 13 | For inquiries please contact jakub.m.fober@pm.me 14 | For updates visit GitHub repository at 15 | https://github.com/Fubaxiusz/fubax-shaders/ 16 | */ 17 | 18 | 19 | //////////// 20 | /// MENU /// 21 | //////////// 22 | 23 | #include "ReShadeUI.fxh" 24 | 25 | uniform float BlurRadius < __UNIFORM_SLIDER_FLOAT1 26 | ui_label = "Blur radius"; 27 | ui_min = 0.0; ui_max = 32.0; ui_step = 0.2; 28 | > = 6.0; 29 | 30 | uniform int BlurSamples < __UNIFORM_SLIDER_INT1 31 | ui_label = "Blur samples"; 32 | ui_min = 2; ui_max = 32; 33 | > = 6; 34 | 35 | 36 | ///////////////// 37 | /// FUNCTIONS /// 38 | ///////////////// 39 | 40 | // Overlay filter by Fubax 41 | // Generates smooth falloff for blur 42 | // input is between 0-1 43 | float get_weight(float progress) 44 | { 45 | float bottom = min(progress, 0.5); 46 | float top = max(progress, 0.5); 47 | return 2.0 *(bottom*bottom +top +top -top*top) -1.5; 48 | } 49 | // 2D falloff 50 | float get_2D_weight(float2 position) 51 | { return get_weight( min(length(position), 1.0) ); } 52 | 53 | 54 | ////////////// 55 | /// SHADER /// 56 | ////////////// 57 | 58 | #include "ReShade.fxh" 59 | 60 | void FilmicBloomPS(float4 vpos : SV_Position, float2 tex_coord : TEXCOORD, out float3 softened : SV_Target) 61 | { 62 | softened = 0.0; 63 | 64 | float total_weight = 0.0; 65 | // Blur pixel 66 | for(int y=0; y 96 | { 97 | pass 98 | { 99 | VertexShader = PostProcessVS; 100 | PixelShader = FilmicBloomPS; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Shaders/FilmicGrade.fx: -------------------------------------------------------------------------------- 1 | /* 2 | FilmicGrade v1.1.1 (c) 2018 Jacob Maximilian Fober, 3 | 4 | This work is licensed under the Creative Commons 5 | Attribution-ShareAlike 4.0 International License. 6 | To view a copy of this license, visit 7 | http://creativecommons.org/licenses/by-sa/4.0/. 8 | */ 9 | 10 | 11 | //////////// 12 | /// MENU /// 13 | //////////// 14 | 15 | #include "ReShadeUI.fxh" 16 | 17 | uniform int Coefficients < __UNIFORM_RADIO_INT1 18 | ui_tooltip = "For digital video signal (HDMI, DVI, Display Port) use BT.709,\n" 19 | "for analog (like VGA, S-Video) use BT.601"; 20 | #if __RESHADE__ < 40000 21 | ui_label = "YUV coefficients"; 22 | ui_items = "BT.709 (digital signal)\0BT.601 (analog signal))\0"; 23 | #else 24 | ui_items = "BT.709 - digital\0BT.601 - analog\0"; 25 | #endif 26 | > = 0; 27 | 28 | uniform float2 LightControl < __UNIFORM_SLIDER_FLOAT2 29 | ui_label = "Shadow-Lights"; 30 | ui_tooltip = "Luma low - highs"; 31 | #if __RESHADE__ < 40000 32 | ui_step = 0.002; 33 | #endif 34 | ui_min = -1.0; ui_max = 1.0; 35 | > = float2(0.0, 0.0); 36 | 37 | 38 | ////////////// 39 | /// SHADER /// 40 | ////////////// 41 | 42 | #include "ReShade.fxh" 43 | 44 | // RGB to YUV709 45 | static const float3x3 ToYUV709 = 46 | float3x3( 47 | float3(0.2126, 0.7152, 0.0722), 48 | float3(-0.09991, -0.33609, 0.436), 49 | float3(0.615, -0.55861, -0.05639) 50 | ); 51 | // RGB to YUV601 52 | static const float3x3 ToYUV601 = 53 | float3x3( 54 | float3(0.299, 0.587, 0.114), 55 | float3(-0.14713, -0.28886, 0.436), 56 | float3(0.615, -0.51499, -0.10001) 57 | ); 58 | // YUV709 to RGB 59 | static const float3x3 ToRGB709 = 60 | float3x3( 61 | float3(1, 0, 1.28033), 62 | float3(1, -0.21482, -0.38059), 63 | float3(1, 2.12798, 0) 64 | ); 65 | // YUV601 to RGB 66 | static const float3x3 ToRGB601 = 67 | float3x3( 68 | float3(1, 0, 1.13983), 69 | float3(1, -0.39465, -0.58060), 70 | float3(1, 2.03211, 0) 71 | ); 72 | static const float2 MaxUV = float2(0.492, 0.877); 73 | 74 | // Overlay blending mode 75 | float Overlay(float LayerAB) 76 | { 77 | float MinAB = min(LayerAB, 0.5); 78 | float MaxAB = max(LayerAB, 0.5); 79 | return 2 * (MinAB * MinAB + MaxAB + MaxAB - MaxAB * MaxAB) - 1.5; 80 | } 81 | 82 | // Linear grading function 83 | float SuperGrade(float2 Controls, float Input) 84 | { 85 | // Color Grading 86 | float2 Grade = Overlay(Input); 87 | Grade.x = min(Grade.x, 0.5); 88 | Grade.y = max(Grade.y, 0.5) - 0.5; 89 | Grade.x = lerp(min(Input, 0.5), Grade.x, -Controls.x); 90 | Grade.y = lerp(max(0.5, Input) - 0.5, Grade.y * (0.5 - Grade.y), -Controls.y); 91 | 92 | return Grade.x + Grade.y; 93 | } 94 | 95 | // Shader 96 | void FilmicGradePS(float4 vois : SV_Position, float2 texcoord : TexCoord, out float3 Display : SV_Target) 97 | { 98 | if ( Coefficients==1 ) 99 | { 100 | // Sample display image and convert to YUV 101 | Display = mul(ToYUV709, tex2D(ReShade::BackBuffer, texcoord).rgb); 102 | // Color Grade Luma 103 | Display.x = SuperGrade(LightControl, Display.x); 104 | // Convert YUV to RGB 105 | Display = mul(ToRGB709, Display); 106 | } 107 | else 108 | { 109 | // Sample display image and convert to YUV 110 | Display = mul(ToYUV601, tex2D(ReShade::BackBuffer, texcoord).rgb); 111 | // Color Grade Luma 112 | Display.x = SuperGrade(LightControl, Display.x); 113 | // Convert YUV to RGB 114 | Display = mul(ToRGB601, Display); 115 | } 116 | } 117 | 118 | 119 | ////////////// 120 | /// OUTPUT /// 121 | ////////////// 122 | 123 | technique FilmicGrade < ui_label = "Filmic Grade"; > 124 | { 125 | pass 126 | { 127 | VertexShader = PostProcessVS; 128 | PixelShader = FilmicGradePS; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Shaders/RimLight.fx: -------------------------------------------------------------------------------- 1 | /* 2 | Rim Light PS (c) 2018 Jacob Maximilian Fober 3 | (based on DisplayDepth port (c) 2018 CeeJay) 4 | 5 | This work is licensed under the Creative Commons 6 | Attribution-ShareAlike 4.0 International License. 7 | To view a copy of this license, visit 8 | http://creativecommons.org/licenses/by-sa/4.0/. 9 | */ 10 | 11 | // Rim Light PS v0.1.6 a 12 | 13 | 14 | #include "Reshade.fxh" 15 | #include "ReShadeUI.fxh" 16 | 17 | 18 | //////////// 19 | /// MENU /// 20 | //////////// 21 | 22 | uniform float3 Color < __UNIFORM_COLOR_FLOAT3 23 | ui_label = "Rim Light Color"; 24 | ui_tooltip = "Adjust rim light tint"; 25 | > = float3(1, 1, 1); 26 | 27 | uniform bool Debug < 28 | ui_label = "Display Normal Map Pass"; 29 | ui_tooltip = "Surface vector angle color map"; 30 | ui_category = "Debug Tools"; 31 | ui_category_closed = true; 32 | > = false; 33 | 34 | uniform bool CustomFarPlane < 35 | ui_label = "Custom Far Plane"; 36 | ui_tooltip = "Enable custom far plane display outside debug view"; 37 | ui_category = "Debug Tools"; 38 | > = true; 39 | 40 | uniform float FarPlane < 41 | ui_label = "Depth Far Plane Preview"; 42 | ui_tooltip = "Adjust this option for proper normal map display\n" 43 | "and change preprocessor definitions, so that\n" 44 | "RESHADE_DEPTH_LINEARIZATION_FAR_PLANE = Your_Value"; 45 | ui_type = "drag"; 46 | ui_min = 0; ui_max = 1000; ui_step = 1; 47 | ui_category = "Debug Tools"; 48 | > = 1000.0; 49 | 50 | 51 | ///////////////// 52 | /// FUNCTIONS /// 53 | ///////////////// 54 | 55 | // Overlay blending mode 56 | float Overlay(float Layer) 57 | { 58 | float MinLayer = min(Layer, 0.5); 59 | float MaxLayer = max(Layer, 0.5); 60 | return 2 * (MinLayer * MinLayer + 2 * MaxLayer - MaxLayer * MaxLayer) - 1.5; 61 | } 62 | 63 | // Get depth pass function 64 | float GetDepth(float2 TexCoord) 65 | { 66 | float depth; 67 | if(Debug || CustomFarPlane) 68 | { 69 | #if RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN 70 | TexCoord.y = 1.0 - TexCoord.y; 71 | #endif 72 | 73 | depth = tex2Dlod(ReShade::DepthBuffer, float4(TexCoord, 0, 0)).x; 74 | 75 | #if RESHADE_DEPTH_INPUT_IS_LOGARITHMIC 76 | const float C = 0.01; 77 | depth = (exp(depth * log(C + 1.0)) - 1.0) / C; 78 | #endif 79 | #if RESHADE_DEPTH_INPUT_IS_REVERSED 80 | depth = 1.0 - depth; 81 | #endif 82 | 83 | depth /= FarPlane - depth * (FarPlane - 1.0); 84 | } 85 | else 86 | { 87 | depth = ReShade::GetLinearizedDepth(TexCoord); 88 | } 89 | return depth; 90 | } 91 | 92 | // Normal pass from depth function 93 | float3 NormalVector(float2 TexCoord) 94 | { 95 | float3 offset = float3(ReShade::PixelSize.xy, 0.0); 96 | float2 posCenter = TexCoord.xy; 97 | float2 posNorth = posCenter - offset.zy; 98 | float2 posEast = posCenter + offset.xz; 99 | 100 | float3 vertCenter = float3(posCenter - 0.5, 1) * GetDepth(posCenter); 101 | float3 vertNorth = float3(posNorth - 0.5, 1) * GetDepth(posNorth); 102 | float3 vertEast = float3(posEast - 0.5, 1) * GetDepth(posEast); 103 | 104 | return normalize(cross(vertCenter - vertNorth, vertCenter - vertEast)) * 0.5 + 0.5; 105 | } 106 | 107 | 108 | ////////////// 109 | /// SHADER /// 110 | ////////////// 111 | 112 | void RimLightPS(in float4 position : SV_Position, in float2 TexCoord : TEXCOORD, out float3 color : SV_Target) 113 | { 114 | float3 NormalPass = NormalVector(TexCoord); 115 | 116 | if(Debug) color = NormalPass; 117 | else 118 | { 119 | color = cross(NormalPass, float3(0.5, 0.5, 1.0)); 120 | float rim = max(max(color.x, color.y), color.z); 121 | color = tex2D(ReShade::BackBuffer, TexCoord).rgb; 122 | color += Color * Overlay(rim); 123 | } 124 | } 125 | 126 | 127 | ////////////// 128 | /// OUTPUT /// 129 | ////////////// 130 | 131 | technique RimLight < ui_label = "Rim Light"; > 132 | { 133 | pass 134 | { 135 | VertexShader = PostProcessVS; 136 | PixelShader = RimLightPS; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Shaders/SimpleBloom.fx: -------------------------------------------------------------------------------- 1 | /* 2 | Simple Bloom PS v0.2.3 (c) 2018 Jacob Maximilian Fober, 3 | 4 | This work is licensed under the Creative Commons 5 | Attribution-ShareAlike 4.0 International License. 6 | To view a copy of this license, visit 7 | http://creativecommons.org/licenses/by-sa/4.0/. 8 | */ 9 | 10 | 11 | //////////// 12 | /// MENU /// 13 | //////////// 14 | 15 | #include "ReShadeUI.fxh" 16 | 17 | uniform float BlurMultiplier < __UNIFORM_SLIDER_FLOAT1 18 | ui_label = "Radius"; 19 | ui_min = 1; ui_max = 16; ui_step = 0.01; 20 | > = 1.23; 21 | 22 | uniform float2 Blend < __UNIFORM_SLIDER_FLOAT2 23 | ui_min = 0; ui_max = 1; ui_step = 0.001; 24 | > = float2(0.0, 0.8); 25 | 26 | uniform bool Debug < 27 | ui_label = "Debug view"; 28 | > = false; 29 | 30 | 31 | #if !defined(ResolutionX) || !defined(ResolutionY) 32 | texture SimpleBloomTarget 33 | { 34 | // resolution 25% 35 | Width = BUFFER_WIDTH * 0.25; 36 | Height = BUFFER_HEIGHT * 0.25; 37 | Format = RGBA8; 38 | }; 39 | #else 40 | texture SimpleBloomTarget 41 | { 42 | // resolution 25% 43 | Width = ResolutionX * 0.25; 44 | Height = ResolutionY * 0.25; 45 | Format = RGBA8; 46 | }; 47 | #endif 48 | sampler SimpleBloomSampler { Texture = SimpleBloomTarget; }; 49 | 50 | 51 | ////////////// 52 | /// SHADER /// 53 | ////////////// 54 | 55 | #include "ReShade.fxh" 56 | 57 | void BloomHorizontalPass(float4 vpos : SV_Position, float2 UvCoord : TEXCOORD, 58 | out float3 Target : SV_Target) 59 | { 60 | const float Weight[11] = 61 | { 62 | 0.082607, 63 | 0.080977, 64 | 0.076276, 65 | 0.069041, 66 | 0.060049, 67 | 0.050187, 68 | 0.040306, 69 | 0.031105, 70 | 0.023066, 71 | 0.016436, 72 | 0.011254 73 | }; 74 | // Grab screen texture 75 | Target.rgb = tex2D(ReShade::BackBuffer, UvCoord).rgb; 76 | float UvOffset = ReShade::PixelSize.x * BlurMultiplier; 77 | Target.rgb *= Weight[0]; 78 | for (int i = 1; i < 11; i++) 79 | { 80 | float SampleOffset = i * UvOffset; 81 | Target.rgb += 82 | max( 83 | Target.rgb, 84 | max( 85 | tex2Dlod(ReShade::BackBuffer, float4(UvCoord.xy + float2(SampleOffset, 0), 0, 0)).rgb 86 | , tex2Dlod(ReShade::BackBuffer, float4(UvCoord.xy - float2(SampleOffset, 0), 0, 0)).rgb 87 | ) 88 | ) * Weight[i]; 89 | } 90 | } 91 | 92 | void SimpleBloomPS(float4 vpos : SV_Position, float2 UvCoord : TEXCOORD, 93 | out float3 Image : SV_Target) 94 | { 95 | const float Weight[11] = 96 | { 97 | 0.082607, 98 | 0.080977, 99 | 0.076276, 100 | 0.069041, 101 | 0.060049, 102 | 0.050187, 103 | 0.040306, 104 | 0.031105, 105 | 0.023066, 106 | 0.016436, 107 | 0.011254 108 | }; 109 | // Grab second pass screen texture 110 | float3 Target = tex2D(SimpleBloomSampler, UvCoord).rgb; 111 | // Vertical gaussian blur 112 | float UvOffset = ReShade::PixelSize.y * BlurMultiplier; 113 | Target.rgb *= Weight[0]; 114 | for (int i = 1; i < 11; i++) 115 | { 116 | float SampleOffset = i * UvOffset; 117 | Target.rgb += 118 | max( 119 | Target.rgb, 120 | max( 121 | tex2Dlod(SimpleBloomSampler, float4(UvCoord.xy + float2(0, SampleOffset), 0, 0)).rgb 122 | , tex2Dlod(SimpleBloomSampler, float4(UvCoord.xy - float2(0, SampleOffset), 0, 0)).rgb 123 | ) 124 | ) * Weight[i]; 125 | } 126 | 127 | Image = tex2D(ReShade::BackBuffer, UvCoord).rgb; 128 | 129 | // Threshold 130 | Target = max(Target - Image, Blend.x) - Blend.x; 131 | Target /= 1 - min(0.999, Blend.x); 132 | 133 | // Mix with image 134 | Target *= Blend.y; 135 | // Image += Target; // Add 136 | Image = 1 - (1 - Image) * (1 - Target); // Screen 137 | 138 | Image = Debug ? Target : Image; 139 | } 140 | 141 | 142 | ////////////// 143 | /// OUTPUT /// 144 | ////////////// 145 | 146 | technique SimpleBloom < ui_label = "Simple Bloom"; > 147 | { 148 | pass 149 | { 150 | VertexShader = PostProcessVS; 151 | PixelShader = BloomHorizontalPass; 152 | RenderTarget = SimpleBloomTarget; 153 | } 154 | pass 155 | { 156 | VertexShader = PostProcessVS; 157 | PixelShader = SimpleBloomPS; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Shaders/BeforeAfterWithDepth.fx: -------------------------------------------------------------------------------- 1 | /* 2 | Before-After with Depth PS v1.3.0 (c) 2018 Jacob Maximilian Fober, 3 | 4 | This work is licensed under the Creative Commons 5 | Attribution-ShareAlike 4.0 International License. 6 | To view a copy of this license, visit 7 | http://creativecommons.org/licenses/by-sa/4.0/. 8 | */ 9 | 10 | 11 | //////////// 12 | /// MENU /// 13 | //////////// 14 | 15 | #include "ReShadeUI.fxh" 16 | 17 | uniform bool DepthBased < 18 | ui_label = "Use Depth"; 19 | ui_category = "Separation settings"; 20 | > = false; 21 | 22 | uniform bool Line < 23 | ui_category = "Separation settings"; 24 | > = true; 25 | 26 | uniform float Offset < 27 | ui_type = "drag"; 28 | ui_min = -1.0; ui_max = 1.0; ui_step = 0.001; 29 | ui_category = "Separation settings"; 30 | > = 0.5; 31 | 32 | uniform float Blur < 33 | ui_label = "Edge Blur"; 34 | ui_type = "drag"; 35 | ui_min = 0.0; ui_max = 1.0; ui_step = 0.001; 36 | ui_category = "Separation settings"; 37 | > = 0.0; 38 | 39 | uniform float3 Color < __UNIFORM_COLOR_FLOAT3 40 | ui_label = "Line color"; 41 | ui_category = "Separation settings"; 42 | > = float3(0.337, 0, 0.118); 43 | 44 | uniform bool RadialX < 45 | ui_label = "Horizontally radial"; 46 | ui_category = "Radial depth adjustment"; 47 | > = false; 48 | 49 | uniform bool RadialY < 50 | ui_label = "Vertically radial"; 51 | ui_category = "Radial depth adjustment"; 52 | > = false; 53 | 54 | uniform int FOV < __UNIFORM_SLIDER_INT1 55 | ui_label = "FOV (horizontal)"; 56 | ui_tooltip = "Field of view in degrees"; 57 | #if __RESHADE__ < 40000 58 | ui_step = 1; 59 | #endif 60 | ui_min = 0; ui_max = 170; 61 | ui_category = "Radial depth adjustment"; 62 | > = 90; 63 | 64 | 65 | // First pass render target 66 | texture BeforeTarget { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; }; 67 | sampler BeforeSampler { Texture = BeforeTarget; }; 68 | 69 | 70 | ///////////////// 71 | /// FUNCTIONS /// 72 | ///////////////// 73 | 74 | // Overlay blending mode 75 | float Overlay(float LayerAB) 76 | { 77 | float MinAB = min(LayerAB, 0.5); 78 | float MaxAB = max(LayerAB, 0.5); 79 | return 2.0 * (MinAB * MinAB + MaxAB + MaxAB - MaxAB * MaxAB) - 1.5; 80 | } 81 | 82 | 83 | ////////////// 84 | /// SHADER /// 85 | ////////////// 86 | 87 | #include "ReShade.fxh" 88 | 89 | void BeforePS(float4 vpos : SV_Position, float2 UvCoord : TEXCOORD, out float3 Image : SV_Target) 90 | { 91 | // Grab screen texture 92 | Image = tex2D(ReShade::BackBuffer, UvCoord).rgb; 93 | } 94 | 95 | void AfterPS(float4 vpos : SV_Position, float2 UvCoord : TEXCOORD, out float3 Image : SV_Target) 96 | { 97 | bool Inverted = Offset < 0.0; 98 | 99 | // Separete Before/After 100 | float Coordinates = DepthBased ? 101 | ReShade::GetLinearizedDepth(UvCoord) : Inverted ? 1.0 - UvCoord.x : UvCoord.x; 102 | 103 | // Convert to radial depth 104 | if( DepthBased && (RadialX || RadialY) ) 105 | { 106 | float2 Size; 107 | Size.x = tan(radians(FOV*0.5)); 108 | Size.y = Size.x / ReShade::AspectRatio; 109 | if(RadialX) Coordinates *= length(float2((UvCoord.x-0.5)*Size.x, 1.0)); 110 | if(RadialY) Coordinates *= length(float2((UvCoord.y-0.5)*Size.y, 1.0)); 111 | } 112 | 113 | float AbsOffset = abs(Offset); 114 | 115 | if(Blur == 0) 116 | { 117 | bool WhichOne = Coordinates > AbsOffset; 118 | WhichOne = DepthBased && Inverted ? !WhichOne : WhichOne; 119 | Image = WhichOne ? tex2D(ReShade::BackBuffer, UvCoord).rgb : tex2D(BeforeSampler, UvCoord).rgb; 120 | if(Line) Image = Coordinates < AbsOffset - 0.002 || Coordinates > AbsOffset + 0.002 ? Image : Color; 121 | } 122 | else 123 | { 124 | // Mask 125 | float Mask = clamp((Coordinates - AbsOffset + 0.5 * Blur) / Blur, 0.0, 1.0); 126 | Mask = DepthBased && Inverted ? 1.0 - Mask : Mask; 127 | Image = lerp(tex2D(BeforeSampler, UvCoord).rgb, tex2D(ReShade::BackBuffer, UvCoord).rgb, Overlay(Mask)); 128 | } 129 | } 130 | 131 | 132 | ////////////// 133 | /// OUTPUT /// 134 | ////////////// 135 | 136 | technique Before < ui_tooltip = "Place this technique before effects you want compare.\nThen move technique 'After'"; > 137 | { 138 | pass 139 | { 140 | VertexShader = PostProcessVS; 141 | PixelShader = BeforePS; 142 | RenderTarget = BeforeTarget; 143 | } 144 | } 145 | technique After < ui_tooltip = "Place this technique after effects you want compare.\nThen move technique 'Before'"; > 146 | { 147 | pass 148 | { 149 | VertexShader = PostProcessVS; 150 | PixelShader = AfterPS; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Shaders/RealGrain.fx: -------------------------------------------------------------------------------- 1 | /* 2 | Real Grain PS v0.3.1 (c) 2018 Jacob Maximilian Fober 3 | 4 | This work is licensed under the Creative Commons 5 | Attribution-ShareAlike 4.0 International License. 6 | To view a copy of this license, visit 7 | http://creativecommons.org/licenses/by-sa/4.0/. 8 | */ 9 | 10 | 11 | //////////// 12 | /// MENU /// 13 | //////////// 14 | 15 | #include "ReShadeUI.fxh" 16 | 17 | uniform float2 Intensity < __UNIFORM_SLIDER_FLOAT2 18 | ui_label = "Noise intensity"; 19 | ui_tooltip = "First Value - Macro Noise\n" 20 | "Second Value - Micro Noise"; 21 | ui_min = 0.0; ui_max = 1.0; ui_step = 0.002; 22 | > = float2(0.316, 0.08); 23 | 24 | uniform int2 Framerate < 25 | ui_label = "Noise framerate"; 26 | ui_tooltip = "Zero will match in-game framerate\n" 27 | "\n" 28 | "First Value - Macro Noise\n" 29 | "Second Value - Micro Noise"; 30 | ui_type = "drag"; 31 | ui_min = 0; ui_max = 120; ui_step = 1; 32 | > = int2(6, 12); 33 | 34 | uniform int Coefficient < __UNIFORM_RADIO_INT1 35 | ui_tooltip = "For digital video signal (HDMI, DVI, Display Port) use BT.709,\n" 36 | "for analog (like VGA, S-Video) use BT.601"; 37 | #if __RESHADE__ < 40000 38 | ui_label = "YUV coefficients"; 39 | ui_items = "BT.709 (digital signal)\0BT.601 (analog signal))\0"; 40 | #else 41 | ui_items = "BT.709 - digital\0BT.601 - analog\0"; 42 | #endif 43 | > = 0; 44 | 45 | 46 | ////////////// 47 | /// SHADER /// 48 | ////////////// 49 | 50 | uniform float Timer < source = "timer"; >; 51 | uniform int FrameCount < source = "framecount"; >; 52 | 53 | 54 | // Overlay blending mode 55 | float Overlay(float LayerA, float LayerB) 56 | { 57 | float MinA = min(LayerA, 0.5); 58 | float MinB = min(LayerB, 0.5); 59 | float MaxA = max(LayerA, 0.5); 60 | float MaxB = max(LayerB, 0.5); 61 | return 2 * (MinA * MinB + MaxA + MaxB - MaxA * MaxB) - 1.5; 62 | } 63 | 64 | // Noise generator 65 | float SimpleNoise(float p) 66 | { 67 | return frac(sin(dot(p, float2(12.9898, 78.233))) * 43758.5453); 68 | } 69 | 70 | #include "ReShade.fxh" 71 | 72 | #if !defined(__RESHADE__) || __RESHADE__ < 40000 73 | static const float GoldenAB = sqrt(5) * 0.5 + 0.5; 74 | static const float GoldenABh = sqrt(5) * 0.25 + 0.25; 75 | #else 76 | static const float GoldenAB = 1.6180339887498948482045868343656; 77 | static const float GoldenABh = 0.80901699437494742410229341718282; 78 | #endif 79 | 80 | #if !defined(ResolutionX) || !defined(ResolutionY) 81 | texture FilmGrainTex { Width = BUFFER_WIDTH * 0.7; Height = BUFFER_HEIGHT * 0.7; Format = R8; }; 82 | #else 83 | texture FilmGrainTex { Width = ResolutionX * 0.7; Height = ResolutionY * 0.7; Format = R8; }; 84 | #endif 85 | sampler FilmGrain { Texture = FilmGrainTex; }; 86 | 87 | float Seed(float2 Coordinates, float Frames) 88 | { 89 | // Calculate seed change 90 | float Seed = Frames == 0 ? FrameCount : floor(Timer * 0.001 * Frames); 91 | // Protect from enormous numbers 92 | Seed = frac(Seed * 0.0001) * 10000; 93 | return Seed * Coordinates.x * Coordinates.y; 94 | } 95 | 96 | void GrainLowRes(float4 vois : SV_Position, float2 TexCoord : TEXCOORD, out float Grain : SV_Target) 97 | { 98 | Grain = saturate(SimpleNoise(Seed(TexCoord, Framerate.x)) * GoldenABh); 99 | Grain = lerp(0.5, Grain, 0.5); 100 | } 101 | 102 | // Shader pass 103 | void RealGrainPS(float4 vois : SV_Position, float2 TexCoord : TEXCOORD, out float3 Image : SV_Target) 104 | { 105 | // Choose luma coefficient, if True BT.709 Luma, else BT.601 Luma 106 | const float3 LumaCoefficient = (Coefficient == 0) ? 107 | float3( 0.2126, 0.7152, 0.0722) 108 | : float3( 0.299, 0.587, 0.114); 109 | // Sample image 110 | Image = tex2D(ReShade::BackBuffer, TexCoord).rgb; 111 | // Mask out bright pixels gamma: (sqrt(5)+1)/2 112 | float Mask = pow(abs(1.0 - dot(Image.rgb, LumaCoefficient)), GoldenAB); 113 | // Generate noise * (sqrt(5) + 1) / 4 (to remain brightness) 114 | float MicroNoise = SimpleNoise(Seed(TexCoord, Framerate.y)); 115 | float MacroNoise = tex2D(FilmGrain, TexCoord).r; 116 | 117 | MicroNoise = 10 * (max(MicroNoise, 0.908 + 0.044) + min(MicroNoise, 1 - 0.908) - 1 - 0.044); 118 | MicroNoise = saturate(MicroNoise + 0.5); 119 | 120 | MacroNoise = lerp(0.5, MacroNoise, Intensity.x); 121 | 122 | float Noise = Overlay(MicroNoise, MacroNoise); 123 | Noise = lerp(0.5, Noise, Intensity.y); 124 | 125 | Image = tex2D(ReShade::BackBuffer, TexCoord).rgb; 126 | 127 | // Blend noise with image 128 | Image.rgb = float3( 129 | Overlay(Image.r, Noise), 130 | Overlay(Image.g, Noise), 131 | Overlay(Image.b, Noise) 132 | ); 133 | } 134 | 135 | 136 | ////////////// 137 | /// OUTPUT /// 138 | ////////////// 139 | 140 | technique RealGrain < ui_label = "Real Grain"; > 141 | { 142 | pass 143 | { 144 | VertexShader = PostProcessVS; 145 | PixelShader = GrainLowRes; 146 | RenderTarget = FilmGrainTex; 147 | } 148 | pass 149 | { 150 | VertexShader = PostProcessVS; 151 | PixelShader = RealGrainPS; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Shaders/LocalContrast.fx: -------------------------------------------------------------------------------- 1 | /** Local Contrast PS, version 0.2.1 2 | All rights (c) 2020 Jakub Maksymilian Fober (the Author). 3 | 4 | The Author provides this shader (the Work) 5 | under the Creative Commons CC BY-SA 3.0 license 6 | available online at 7 | http://creativecommons.org/licenses/by-sa/3.0/ 8 | 9 | For inquiries please contact jakub.m.fober@pm.me 10 | */ 11 | 12 | 13 | #include "ReShadeUI.fxh" 14 | #include "ReShade.fxh" 15 | 16 | 17 | //////////// 18 | /// MENU /// 19 | //////////// 20 | 21 | #if !defined(LC_BLOCK) 22 | #define LC_BLOCK 8 23 | #endif 24 | 25 | uniform bool CorrectGamma < 26 | ui_label = "Perform gamma correction"; 27 | ui_tootlip = "Applies S-curve to the corrected luma channel"; 28 | > = true; 29 | 30 | uniform int ContrastLimit < __UNIFORM_SLIDER_INT1 31 | ui_label = "Contrast limit"; 32 | ui_min = 0; ui_max = 63; 33 | > = 40; 34 | 35 | 36 | /////////////// 37 | /// TEXTURE /// 38 | /////////////// 39 | 40 | // Local histogram values map 41 | texture LocalContrastMapBuffer 42 | { 43 | Width = BUFFER_WIDTH/LC_BLOCK; 44 | Height = BUFFER_HEIGHT/LC_BLOCK; 45 | Format = RG8; 46 | // R = histogram luma min 47 | // G = histogram luma max 48 | }; 49 | sampler LocalContrastMap { Texture = LocalContrastMapBuffer; }; 50 | 51 | // Point mapping back-buffer sampler 52 | sampler BackBuffer { Texture = ReShade::BackBufferTex; }; 53 | 54 | ///////////////// 55 | /// FUNCTIONS /// 56 | ///////////////// 57 | 58 | // RGB to YUV709 59 | static const float3x3 ToYUV709 = 60 | float3x3( 61 | float3(0.2126, 0.7152, 0.0722), 62 | float3(-0.09991, -0.33609, 0.436), 63 | float3(0.615, -0.55861, -0.05639) 64 | ); 65 | // RGB to YUV601 66 | static const float3x3 ToYUV601 = 67 | float3x3( 68 | float3(0.299, 0.587, 0.114), 69 | float3(-0.14713, -0.28886, 0.436), 70 | float3(0.615, -0.51499, -0.10001) 71 | ); 72 | // YUV709 to RGB 73 | static const float3x3 ToRGB709 = 74 | float3x3( 75 | float3(1, 0, 1.28033), 76 | float3(1, -0.21482, -0.38059), 77 | float3(1, 2.12798, 0) 78 | ); 79 | // YUV601 to RGB 80 | static const float3x3 ToRGB601 = 81 | float3x3( 82 | float3(1, 0, 1.13983), 83 | float3(1, -0.39465, -0.58060), 84 | float3(1, 2.03211, 0) 85 | ); 86 | 87 | // Overlay filter by Fubax 88 | // Generates smooth s-curve 89 | // input is between 0-1 90 | float weight(float gradient) 91 | { 92 | float bottom = min(gradient, 0.5); 93 | float top = max(gradient, 0.5); 94 | return 2.0 *(bottom*bottom +top +top -top*top) -1.5; 95 | } 96 | // Convert map slider range 97 | float getContrastLimit() 98 | { return 1.0-ContrastLimit/127.0; } 99 | 100 | 101 | ////////////// 102 | /// SHADER /// 103 | ////////////// 104 | 105 | // Analyze histogram per image block 106 | void GetLocalHistogramPS( 107 | float4 pos : SV_Position, 108 | float2 texcoord : TEXCOORD, 109 | out float2 histogramStats : SV_Target // Contrast-limited adaptive histogram 110 | ){ 111 | // Initial values of the histogram 112 | histogramStats.s = 1.0; // Min 113 | histogramStats.t = 0.0; // Max 114 | float histogramMean = 0.0; // Average 115 | // Loop through image block and get histogram min, max and average 116 | const int halfBlock = LC_BLOCK/2; 117 | for (int y=-halfBlock; y 172 | { 173 | pass Local_Histogram 174 | { 175 | VertexShader = PostProcessVS; 176 | PixelShader = GetLocalHistogramPS; 177 | RenderTarget = LocalContrastMapBuffer; 178 | } 179 | pass Historgram_Normalization 180 | { 181 | VertexShader = PostProcessVS; 182 | PixelShader = LocalConstrastPS; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Shaders/Reflection.fx: -------------------------------------------------------------------------------- 1 | /* 2 | Reflection PS (c) 2019 Jacob Maximilian Fober 3 | 4 | This work is licensed under the Creative Commons 5 | Attribution-NonCommercial-NoDerivatives 4.0 International License. 6 | To view a copy of this license, visit 7 | http://creativecommons.org/licenses/by-nc-nd/4.0/ 8 | 9 | For inquiries please contact jakub.m.fober@pm.me 10 | */ 11 | 12 | /* 13 | This shader maps reflection vector of a normal surface 14 | from a camera onto reflection texture in equisolid 15 | 360 degrees projection (mirror ball) 16 | 17 | If you want to create reflection texture from 18 | equirectangular 360 panorama, visit following: 19 | https://github.com/Fubaxiusz/shadron-shaders/blob/master/Shaders/Equirect2Equisolid.shadron 20 | 21 | It's a shader script for Shadron image-editing software, 22 | avaiable here: 23 | https://www.arteryengine.com/shadron/ 24 | */ 25 | 26 | /* 27 | Depth Map sampler is from ReShade.fxh by Crosire. 28 | Normal Map generator is from DisplayDepth.fx by CeeJay. 29 | Soft light blending mode is from pegtop.net 30 | */ 31 | 32 | // version 1.2.2 33 | 34 | 35 | //////////// 36 | /// MENU /// 37 | //////////// 38 | 39 | #include "ReShadeUI.fxh" 40 | 41 | #ifndef ReflectionRes 42 | #define ReflectionRes 512 43 | #endif 44 | #ifndef ReflectionImage 45 | #define ReflectionImage "matcap.png" 46 | #endif 47 | 48 | uniform int FOV < __UNIFORM_SLIDER_INT1 49 | ui_label = "Field of View (horizontal)"; 50 | ui_min = 1; ui_max = 170; 51 | ui_category = "Depth settings"; 52 | > = 90; 53 | 54 | uniform float FarPlane < 55 | ui_label = "Far Plane adjustment"; 56 | ui_tooltip = "Adjust Normal Map strength"; 57 | ui_type = "drag"; 58 | ui_min = 0.0; ui_max = 1000.0; ui_step = 0.2; 59 | ui_category = "Depth settings"; 60 | > = 1000.0; 61 | 62 | uniform bool Skip4Background < 63 | ui_label = "Show background"; 64 | ui_tooltip = "Mask reflection for:\nDEPTH = 1.0"; 65 | ui_category = "Depth settings"; 66 | > = true; 67 | 68 | uniform int BlendingMode < 69 | ui_label = "Mix mode"; 70 | ui_type = "combo"; 71 | ui_items = "Normal mode\0Multiply\0Screen\0Overlay\0Soft Light\0"; 72 | ui_category = "Texture settings"; 73 | > = 0; 74 | 75 | uniform float BlendingAmount < __UNIFORM_SLIDER_FLOAT1 76 | ui_label = "Blending"; 77 | ui_tooltip = "Blend with background"; 78 | ui_min = 0.0; ui_max = 1.0; 79 | ui_category = "Texture settings"; 80 | > = 1.0; 81 | 82 | uniform bool AlphaBlending < 83 | ui_label = "Use alpha transparency"; 84 | ui_tooltip = "Use texture transparency channel (Alpha)"; 85 | ui_category = "Texture settings"; 86 | > = false; 87 | 88 | #include "ReShade.fxh" 89 | 90 | texture ReflectionTex < source = ReflectionImage; > 91 | { 92 | Width = ReflectionRes; 93 | Height = ReflectionRes; 94 | Format = RGBA8; 95 | }; 96 | sampler ReflectionSampler 97 | { 98 | Texture = ReflectionTex; 99 | AddressU = BORDER; 100 | AddressV = BORDER; 101 | }; 102 | 103 | 104 | ////////////////////// 105 | /// BLENDING MODES /// 106 | ////////////////////// 107 | 108 | float3 Multiply(float3 A, float3 B){ return A*B; } 109 | 110 | float3 Screen(float3 A, float3 B){ return A+B-A*B; } // By JMF 111 | 112 | float3 Overlay(float3 A, float3 B) 113 | { 114 | // Algorithm by JMF 115 | float3 HL[4] = { max(A, 0.5), max(B, 0.5), min(A, 0.5), min(B, 0.5) }; 116 | return ( HL[0] + HL[1] - HL[0]*HL[1] + HL[2]*HL[3] )*2.0 - 1.5; 117 | } 118 | 119 | float3 SoftLight(float3 A, float3 B) 120 | { 121 | float3 A2 = pow(A, 2.0); 122 | return 2.0*A*B+A2-2.0*A2*B; 123 | } 124 | 125 | 126 | ////////////// 127 | /// SHADER /// 128 | ////////////// 129 | 130 | // Get depth function from ReShade.fxh with custom Far Plane 131 | float GetDepth(float2 TexCoord) 132 | { 133 | #if RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN 134 | TexCoord.y = 1.0 - TexCoord.y; 135 | #endif 136 | float Depth = tex2Dlod( ReShade::DepthBuffer, float4(TexCoord, 0, 0) ).x; 137 | 138 | #if RESHADE_DEPTH_INPUT_IS_LOGARITHMIC 139 | const float C = 0.01; 140 | Depth = (exp(Depth * log(C + 1.0)) - 1.0) / C; 141 | #endif 142 | #if RESHADE_DEPTH_INPUT_IS_REVERSED 143 | Depth = 1.0 - Depth; 144 | #endif 145 | 146 | const float N = 1.0; 147 | Depth /= FarPlane - Depth * (FarPlane - N); 148 | 149 | return Depth; 150 | } 151 | 152 | // Normal map (OpenGL oriented) generator from DisplayDepth.fx 153 | float3 NormalVector(float2 texcoord) 154 | { 155 | float3 offset = float3(ReShade::PixelSize.xy, 0.0); 156 | float2 posCenter = texcoord.xy; 157 | float2 posNorth = posCenter - offset.zy; 158 | float2 posEast = posCenter + offset.xz; 159 | 160 | float3 vertCenter = float3(posCenter - 0.5, 1.0) * GetDepth(posCenter); 161 | float3 vertNorth = float3(posNorth - 0.5, 1.0) * GetDepth(posNorth); 162 | float3 vertEast = float3(posEast - 0.5, 1.0) * GetDepth(posEast); 163 | 164 | return normalize(cross(vertCenter - vertNorth, vertCenter - vertEast)) * 0.5 + 0.5; 165 | } 166 | 167 | // Sample Matcap texture 168 | float4 GetReflection(float2 TexCoord) 169 | { 170 | // Get aspect ratio 171 | float Aspect = ReShade::AspectRatio; 172 | 173 | // Sample normal pass 174 | float3 Normal = NormalVector(TexCoord); 175 | Normal.xy = Normal.xy * 2.0 - 1.0; 176 | 177 | // Get ray vector from camera to the visible geometry 178 | float3 CameraRay; 179 | CameraRay.xy = TexCoord * 2.0 - 1.0; 180 | CameraRay.y /= Aspect; // Correct aspect ratio 181 | CameraRay.z = 1.0 / tan(radians(FOV*0.5)); // Scale frustum Z position from FOV 182 | 183 | // Get reflection vector from camera to geometry surface 184 | float3 OnSphereCoord = normalize( reflect(CameraRay, Normal) ); 185 | 186 | // Convert cartesian coordinates to equisolid polar 187 | float2 EquisolidPolar, EquisolidCoord; 188 | EquisolidPolar.x = length( OnSphereCoord + float3(0.0, 0.0, 1.0) ) * 0.5; 189 | EquisolidPolar.y = atan2(OnSphereCoord.y, OnSphereCoord.x); 190 | // Convert polar to UV coordinates 191 | EquisolidCoord.x = EquisolidPolar.x * cos(EquisolidPolar.y); 192 | EquisolidCoord.y = EquisolidPolar.x * sin(EquisolidPolar.y); 193 | EquisolidCoord = EquisolidCoord * 0.5 + 0.5; 194 | 195 | // Sample matcap texture 196 | float4 ReflectionTexture = tex2D(ReflectionSampler, EquisolidCoord); 197 | 198 | return ReflectionTexture; 199 | } 200 | 201 | float3 ReflectionPS(float4 vois : SV_Position, float2 texcoord : TexCoord) : SV_Target 202 | { 203 | if(Skip4Background) if( GetDepth(texcoord)==1.0 ) return tex2D(ReShade::BackBuffer, texcoord).rgb; 204 | if( !bool(BlendingMode) && !AlphaBlending && BlendingAmount == 1.0) return GetReflection(texcoord).rgb; 205 | 206 | float3 Display = tex2D(ReShade::BackBuffer, texcoord).rgb; 207 | float4 Reflection = GetReflection(texcoord); 208 | 209 | switch(BlendingMode) 210 | { 211 | case 1:{ Reflection.rgb = Multiply(Reflection.rgb, Display); break; } // Multiply 212 | case 2:{ Reflection.rgb = Screen(Reflection.rgb, Display); break; } // Screen 213 | case 3:{ Reflection.rgb = Overlay(Reflection.rgb, Display); break; } // Overlay 214 | case 4:{ Reflection.rgb = SoftLight(Reflection.rgb, Display); break; } // Soft Light 215 | } 216 | 217 | if( !AlphaBlending && BlendingAmount == 1.0) return Reflection.rgb; 218 | return lerp(Display, Reflection.rgb, Reflection.a * BlendingAmount); 219 | } 220 | 221 | 222 | ////////////// 223 | /// OUTPUT /// 224 | ////////////// 225 | 226 | technique Reflection < ui_label = "Reflection (matcap)"; 227 | ui_tooltip = "Apply reflection texture to a geometry...\n" 228 | "To change used texture, define in global preprocessor definitions:\n\n" 229 | " ReflectionImage 'YourImage.png'\n\n" 230 | "To change texture resolution, define\n\n" 231 | " ReflectionRes 32"; > 232 | { 233 | pass 234 | { 235 | VertexShader = PostProcessVS; 236 | PixelShader = ReflectionPS; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Shaders/ThinFilm.fx: -------------------------------------------------------------------------------- 1 | /* 2 | ThinFilm PS (c) 2019 Jacob Maximilian Fober 3 | 4 | This work is licensed under the Creative Commons 5 | Attribution 4.0 International License. 6 | To view a copy of this license, visit 7 | http://creativecommons.org/licenses/by/4.0/ 8 | 9 | For inquiries please contact jakub.m.fober@pm.me 10 | */ 11 | 12 | /* 13 | This shader generates thin film interference based from view and normal angle. 14 | The irridecensce formula was sourced from: 15 | http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/thin-film-interference-for-computer-graphics-r2962 16 | by Bacterius 17 | */ 18 | 19 | /* 20 | Depth Map sampler is from ReShade.fxh by Crosire. 21 | Normal Map generator is from DisplayDepth.fx by CeeJay. 22 | Soft light blending mode is from pegtop.net 23 | */ 24 | 25 | // version 1.0.3 26 | 27 | 28 | //////////// 29 | /// MENU /// 30 | //////////// 31 | 32 | #include "ReShadeUI.fxh" 33 | 34 | uniform int FOV < __UNIFORM_SLIDER_INT1 35 | ui_label = "Field of View (horizontal)"; 36 | ui_min = 1; ui_max = 170; 37 | ui_category = "Depth settings"; 38 | ui_category_closed = true; 39 | > = 90; 40 | 41 | uniform float FarPlane < 42 | ui_label = "Far Plane adjustment"; 43 | ui_tooltip = "Adjust Normal Map strength"; 44 | ui_type = "drag"; 45 | ui_min = 0.0; ui_max = 1000.0; ui_step = 0.2; 46 | ui_category = "Depth settings"; 47 | > = 1000.0; 48 | 49 | uniform bool Skip4Background < 50 | ui_label = "Show background"; 51 | ui_tooltip = "Mask reflection for:\nDEPTH = 1.0"; 52 | ui_category = "Depth settings"; 53 | > = true; 54 | 55 | // If not using thin film noise texture 56 | uniform int thick < 57 | ui_label = "Film thickness"; 58 | ui_tooltip = "Thickness of the film, in nm"; 59 | ui_type = "drag"; 60 | ui_min = 0; ui_max = 1000; ui_step = 1; 61 | ui_category = "Irridescence settings"; 62 | > = 350; 63 | /* 64 | uniform int thicknessMin < 65 | ui_label = "Film thickness min"; 66 | ui_tooltip = "Minimum thickness of the film, in nm"; 67 | ui_type = "drag"; 68 | ui_min = 0; ui_max = 1000; ui_step = 1; 69 | ui_category = "Irridescence settings"; 70 | > = 250; 71 | 72 | uniform int thicknessMax < 73 | ui_label = "Film thickness max"; 74 | ui_tooltip = "Minimum thickness of the film, in nm"; 75 | ui_type = "drag"; 76 | ui_min = 0; ui_max = 1000; ui_step = 1; 77 | ui_category = "Irridescence settings"; 78 | > = 400; 79 | 80 | uniform float thickness < __UNIFORM_SLIDER_FLOAT1 81 | ui_label = "Thickness noise"; 82 | ui_tooltip = "Noise that determines the thickness of the film as fraction between the min and max"; 83 | ui_min = 0.0; ui_max = 1.0; 84 | ui_category = "Irridescence settings"; 85 | > = 0.5; 86 | */ 87 | 88 | uniform float nmedium < __UNIFORM_SLIDER_FLOAT1 89 | ui_label = "Refractive index of air"; 90 | ui_tooltip = "Refractive index of the outer medium (typically air)"; 91 | ui_min = 1.0; ui_max = 5.0; 92 | ui_category = "Irridescence settings"; 93 | > = 1.0; 94 | 95 | uniform float nfilm < __UNIFORM_SLIDER_FLOAT1 96 | ui_label = "Refractive index of thin film layer"; 97 | ui_tooltip = "Refractive index of the thin film itself"; 98 | ui_min = 1.0; ui_max = 5.0; 99 | ui_category = "Irridescence settings"; 100 | > = 1.5; 101 | 102 | uniform float ninternal < __UNIFORM_SLIDER_FLOAT1 103 | ui_label = "Refractive index of the lower material"; 104 | ui_tooltip = "Refractive index of the material below the film"; 105 | ui_min = 1.0; ui_max = 5.0; 106 | ui_category = "Irridescence settings"; 107 | > = 3.5; 108 | 109 | uniform int BlendingMode < 110 | ui_label = "Mix mode"; 111 | ui_type = "combo"; 112 | ui_items = "Normal mode\0Multiply\0Screen\0Overlay\0Soft Light\0"; 113 | ui_category = "Blending options"; 114 | > = 2; 115 | 116 | uniform bool LumaBlending < 117 | ui_label = "Luminance based mixing"; 118 | ui_category = "Blending options"; 119 | > = true; 120 | 121 | uniform float BlendingAmount < __UNIFORM_SLIDER_FLOAT1 122 | ui_label = "Blend"; 123 | ui_tooltip = "Blend with background"; 124 | ui_min = 0.0; ui_max = 1.0; 125 | ui_category = "Blending options"; 126 | > = 1.0; 127 | 128 | #include "ReShade.fxh" 129 | 130 | 131 | ////////////////////// 132 | /// BLENDING MODES /// 133 | ////////////////////// 134 | 135 | float3 Multiply(float3 A, float3 B){ return A*B; } 136 | 137 | float3 Screen(float3 A, float3 B){ return A+B-A*B; } // By JMF 138 | 139 | float3 Overlay(float3 A, float3 B) 140 | { 141 | // Algorithm by JMF 142 | float3 HL[4] = { max(A, 0.5), max(B, 0.5), min(A, 0.5), min(B, 0.5) }; 143 | return ( HL[0] + HL[1] - HL[0]*HL[1] + HL[2]*HL[3] )*2.0 - 1.5; 144 | } 145 | 146 | float3 SoftLight(float3 A, float3 B) 147 | { 148 | float3 A2 = pow(A, 2.0); 149 | return 2.0*A*B+A2-2.0*A2*B; 150 | } 151 | 152 | // RGB to YUV709 luma 153 | float LumaMask(float3 Color) 154 | { 155 | static const float3 Luma709 = float3(0.2126, 0.7152, 0.0722); 156 | return dot(Luma709, Color); 157 | } 158 | 159 | 160 | ////////////// 161 | /// SHADER /// 162 | ////////////// 163 | 164 | // Get depth function from ReShade.fxh with custom Far Plane 165 | float GetDepth(float2 TexCoord) 166 | { 167 | #if RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN 168 | TexCoord.y = 1.0 - TexCoord.y; 169 | #endif 170 | float Depth = tex2Dlod( ReShade::DepthBuffer, float4(TexCoord, 0, 0) ).x; 171 | 172 | #if RESHADE_DEPTH_INPUT_IS_LOGARITHMIC 173 | const float C = 0.01; 174 | Depth = (exp(Depth * log(C + 1.0)) - 1.0) / C; 175 | #endif 176 | #if RESHADE_DEPTH_INPUT_IS_REVERSED 177 | Depth = 1.0 - Depth; 178 | #endif 179 | 180 | const float N = 1.0; 181 | Depth /= FarPlane - Depth * (FarPlane - N); 182 | 183 | return Depth; 184 | } 185 | 186 | // Normal map (OpenGL oriented) generator from DisplayDepth.fx 187 | float3 NormalVector(float2 texcoord) 188 | { 189 | float3 offset = float3(ReShade::PixelSize.xy, 0.0); 190 | float2 posCenter = texcoord.xy; 191 | float2 posNorth = posCenter - offset.zy; 192 | float2 posEast = posCenter + offset.xz; 193 | 194 | float3 vertCenter = float3(posCenter - 0.5, 1.0) * GetDepth(posCenter); 195 | float3 vertNorth = float3(posNorth - 0.5, 1.0) * GetDepth(posNorth); 196 | float3 vertEast = float3(posEast - 0.5, 1.0) * GetDepth(posEast); 197 | 198 | return normalize(cross(vertCenter - vertNorth, vertCenter - vertEast)) * 0.5 + 0.5; 199 | } 200 | 201 | /* Amplitude reflection coefficient (s-polarized) */ 202 | float rs(float n1, float n2, float cosI, float cosT) 203 | { return (n1 * cosI - n2 * cosT) / (n1 * cosI + n2 * cosT); } 204 | 205 | /* Amplitude reflection coefficient (p-polarized) */ 206 | float rp(float n1, float n2, float cosI, float cosT) 207 | { return (n2 * cosI - n1 * cosT) / (n1 * cosT + n2 * cosI); } 208 | 209 | /* Amplitude transmission coefficient (s-polarized) */ 210 | float ts(float n1, float n2, float cosI, float cosT) 211 | { return 2.0 * n1 * cosI / (n1 * cosI + n2 * cosT); } 212 | 213 | /* Amplitude transmission coefficient (p-polarized) */ 214 | float tp(float n1, float n2, float cosI, float cosT) 215 | { return 2.0 * n1 * cosI / (n1 * cosT + n2 * cosI); } 216 | 217 | // cosI is the cosine of the incident angle, that is, cos0 = dot(view angle, normal) 218 | // lambda is the wavelength of the incident light (e.g. lambda = 510 for green, 650 for red, 475 for blue) 219 | // From http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/thin-film-interference-for-computer-graphics-r2962 220 | float thinFilmReflectance(float cos0, float lambda, float thickness, float n0, float n1, float n2) { 221 | const float PI = radians(180.0); 222 | 223 | // compute the phase change term (constant) 224 | float d10 = (n1 > n0) ? 0.0 : PI; 225 | float d12 = (n1 > n2) ? 0.0 : PI; 226 | float delta = d10 + d12; 227 | 228 | // now, compute cos1, the cosine of the reflected angle 229 | float sin1 = pow(n0 / n1, 2.0) * (1.0 - pow(cos0, 2.0)); 230 | if(sin1 > 1.0) return 1.0; // total internal reflection 231 | float cos1 = sqrt(1.0 - sin1); 232 | 233 | // compute cos2, the cosine of the final transmitted angle, i.e. cos(theta_2) 234 | // we need this angle for the Fresnel terms at the bottom interface 235 | float sin2 = pow(n0 / n2, 2.0) * (1.0 - pow(cos0, 2.0)); 236 | if(sin2 > 1.0) return 1.0; // total internal reflection 237 | float cos2 = sqrt(1.0 - sin2); 238 | 239 | // get the reflection transmission amplitude Fresnel coefficients 240 | float alpha_s = rs(n1, n0, cos1, cos0) * rs(n1, n2, cos1, cos2); // rho_10 * rho_12 (s-polarized) 241 | float alpha_p = rp(n1, n0, cos1, cos0) * rp(n1, n2, cos1, cos2); // rho_10 * rho_12 (p-polarized) 242 | 243 | float beta_s = ts(n0, n1, cos0, cos1) * ts(n1, n2, cos1, cos2); // tau_01 * tau_12 (s-polarized) 244 | float beta_p = tp(n0, n1, cos0, cos1) * tp(n1, n2, cos1, cos2); // tau_01 * tau_12 (p-polarized) 245 | 246 | // compute the phase term (phi) 247 | float phi = (2.0 * PI / lambda) * (2.0 * n1 * thickness * cos1) + delta; 248 | 249 | // finally, evaluate the transmitted intensity for the two possible polarizations 250 | float ts = pow(beta_s, 2.0) / (pow(alpha_s, 2.0) - 2.0 * alpha_s * cos(phi) + 1.0); 251 | float tp = pow(beta_p, 2.0) / (pow(alpha_p, 2.0) - 2.0 * alpha_p * cos(phi) + 1.0); 252 | 253 | // we need to take into account conservation of energy for transmission 254 | float beamRatio = (n2 * cos2) / (n0 * cos0); 255 | 256 | // calculate the average transmitted intensity (if you know the polarization distribution of your 257 | // light source, you should specify it here. if you don't, a 50%/50% average is generally used) 258 | float t = beamRatio * (ts + tp) * 0.5; 259 | 260 | // and finally, derive the reflected intensity 261 | return 1.0 - t; 262 | } 263 | 264 | // Calculate dot product of view angle and normal 265 | float GetReflectionCosine(float2 TexCoord) 266 | { 267 | // Get aspect ratio 268 | float Aspect = ReShade::AspectRatio; 269 | 270 | // Sample normal pass 271 | float3 Normal = NormalVector(TexCoord); 272 | // Center normal coordinates 273 | Normal.xy = Normal.xy * 2.0 - 1.0; 274 | 275 | // Get ray vector from camera to the visible geometry 276 | float3 CameraRay; 277 | CameraRay.xy = TexCoord * 2.0 - 1.0; 278 | CameraRay.y /= Aspect; // Correct aspect ratio 279 | CameraRay.z = 1.0 / tan(radians(FOV*0.5)); // Scale frustum Z position from FOV 280 | 281 | // Get dot product of view angle and normal 282 | return abs( dot(CameraRay, Normal) ); 283 | } 284 | 285 | float3 ThinFilmPS(float4 vois : SV_Position, float2 texcoord : TexCoord) : SV_Target 286 | { 287 | if(Skip4Background) if( GetDepth(texcoord)==1.0 ) return tex2D(ReShade::BackBuffer, texcoord).rgb; 288 | 289 | float3 Display = tex2D(ReShade::BackBuffer, texcoord).rgb; 290 | float3 Irridescence; 291 | 292 | float cos0 = GetReflectionCosine(texcoord); 293 | // float thick = thicknessMin*(1.0-thickness) + thicknessMax*thickness; 294 | 295 | Irridescence.r = thinFilmReflectance(cos0, 650.0, thick, nmedium, nfilm, ninternal); // Red wavelength 296 | Irridescence.g = thinFilmReflectance(cos0, 510.0, thick, nmedium, nfilm, ninternal); // Green wavelength 297 | Irridescence.b = thinFilmReflectance(cos0, 475.0, thick, nmedium, nfilm, ninternal); // Blue wavelength 298 | 299 | Irridescence = saturate(Irridescence); 300 | 301 | if(BlendingAmount == 1.0 && !bool(BlendingMode) && !LumaBlending) return Irridescence; 302 | 303 | switch(BlendingMode) 304 | { 305 | case 1:{ Irridescence = Multiply(Irridescence, Display); break; } // Multiply 306 | case 2:{ Irridescence = Screen(Irridescence, Display); break; } // Screen 307 | case 3:{ Irridescence = Overlay(Irridescence, Display); break; } // Overlay 308 | case 4:{ Irridescence = SoftLight(Irridescence, Display); break; } // Soft Light 309 | } 310 | 311 | if(BlendingAmount == 1.0 && !LumaBlending) return Irridescence; 312 | else if(LumaBlending) return lerp(Display, Irridescence, LumaMask(Display) * BlendingAmount); 313 | return lerp(Display, Irridescence, BlendingAmount); 314 | } 315 | 316 | 317 | ////////////// 318 | /// OUTPUT /// 319 | ////////////// 320 | 321 | technique ThinFilm < ui_label = "Thin-film (iridescence)"; 322 | ui_tooltip = "Apply thin-film interference (iridescence) 'holographic' effect..."; > 323 | { 324 | pass 325 | { 326 | VertexShader = PostProcessVS; 327 | PixelShader = ThinFilmPS; 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /Shaders/VR.fx: -------------------------------------------------------------------------------- 1 | /* 2 | /// VR shader /// 3 | 4 | Make any game VR and any screen with lenses a VR headset. 5 | Thanks to this shader you'll be able to correct distortions of any lens types 6 | (DIY, experimental) and chromatic aberration. 7 | Also if a game outputs depth pass you can have a stereo-3D vision thanks to 8 | the parallax mapping (which needs some further improvement). 9 | 10 | Copyright (c) 2019 Jacob Max Fober 11 | 12 | This work is licensed under the Creative Commons 13 | Attribution-NonCommercial-ShareAlike 4.0 International License. 14 | To view a copy of this license, visit 15 | http://creativecommons.org/licenses/by-nc-sa/4.0/ 16 | 17 | If you want to use it commercially, contact me at jakub.m.fober@pm.me 18 | If you have questions, visit https://reshade.me/forum/shader-discussion/ 19 | 20 | I'm author of most of equations present here, 21 | beside Brown-Conrady distortion correction model and 22 | Parallax Steep and Occlusion mapping which 23 | I changed and adopted from various sources. 24 | 25 | Version 0.4.2 alpha 26 | */ 27 | 28 | 29 | 30 | #include "ReShade.fxh" 31 | #include "ReShadeUI.fxh" 32 | 33 | //////////// 34 | /// MENU /// 35 | //////////// 36 | 37 | #ifndef MaximumParallaxSteps 38 | #define MaximumParallaxSteps 1024 // Defefine max steps to make loop finite 39 | #endif 40 | // Grid settings 41 | #ifndef BoxAmount 42 | #define BoxAmount 31 // Number of boxes horizontally (choose odd number) 43 | #endif 44 | #ifndef thicknessA 45 | #define thicknessA 0.25 // White grid thickness 46 | #endif 47 | #ifndef thicknessB 48 | #define thicknessB 0.125 // Yellow cross thickness 49 | #endif 50 | #ifndef crossColor 51 | #define crossColor float3(1.0, 1.0, 0.0) // Center cross color (yellow) 52 | #endif 53 | 54 | 55 | uniform int SoloLines < __UNIFORM_RADIO_INT1 56 | #if __RESHADE__ < 40000 57 | ui_label = "Solo lines"; 58 | #endif 59 | ui_items = "All lines visible\0Solo horizontal lines\0Solo vertical lines\0Switch to radial pattern\0"; 60 | ui_tooltip = "for chromatic aberration switch radial pattern"; 61 | ui_category = "Calibration grid"; 62 | > = 0; 63 | 64 | uniform bool TestGrid < 65 | ui_label = "Display calibration grid"; 66 | ui_tooltip = "Toggle test grid for lens calibration"; 67 | ui_category = "Calibration grid"; 68 | > = true; 69 | 70 | uniform float IPD < 71 | ui_label = "IPD (interpupillary distance)"; 72 | ui_tooltip = "Adjust lens center relative to the screen size"; 73 | ui_type = "drag"; 74 | ui_min = 0.0; ui_max = 0.75; ui_step = 0.001; 75 | ui_category = "Stereo-vision adjustment"; 76 | ui_category_closed = true; 77 | > = 0.477; 78 | 79 | uniform bool StereoSwitch < 80 | ui_label = "Stereoscopic view enabled"; 81 | ui_tooltip = "Toggle stereo vision"; 82 | ui_category = "Stereo-vision adjustment"; 83 | > = true; 84 | 85 | uniform float ParallaxOffset < __UNIFORM_SLIDER_FLOAT1 86 | ui_label = "Parallax horizontal offset (disparity)"; 87 | ui_tooltip = "Adjust 3D effect power\n(disparity in screen percent)"; 88 | #if __RESHADE__ < 40000 89 | ui_step = 0.001; 90 | #endif 91 | ui_min = 0.0; ui_max = 1.0; 92 | ui_category = "Parallax 3D effect"; 93 | ui_category_closed = true; 94 | > = 0.355; 95 | 96 | uniform float ParallaxCenter < __UNIFORM_SLIDER_FLOAT1 97 | ui_label = "Parallax rotation center (ZPD)"; 98 | ui_tooltip = "Adjust 3D effect middle point\n(zero parallax distance)"; 99 | #if __RESHADE__ < 40000 100 | ui_step = 0.001; 101 | #endif 102 | ui_min = 0.0; ui_max = 1.0; 103 | ui_category = "Parallax 3D effect"; 104 | > = 1.0; 105 | 106 | uniform int ParallaxSteps < 107 | ui_label = "Parallax sampling steps"; 108 | ui_tooltip = "Adjust 3D effect quality\n(higher numbers may decrease performance)"; 109 | ui_type = "drag"; 110 | ui_min = 1; ui_max = 128; ui_step = 0.2; 111 | ui_category = "Parallax 3D effect"; 112 | > = 16; 113 | 114 | uniform int ParallaxMaskScalar < __UNIFORM_SLIDER_INT1 115 | ui_label = "Parallax gaps compensation"; 116 | ui_tooltip = "Adjust gaps from parallax offset\n(some glithes may occur due to lack of\nanti-aliasing in the depth pass)"; 117 | ui_min = 0; ui_max = 32; ui_step = 0.2; 118 | ui_category = "Parallax 3D effect"; 119 | > = 10; 120 | 121 | uniform bool ParallaxSwitch < 122 | ui_label = "Parallax enabled"; 123 | ui_tooltip = "Toggle parallax 3D effect\n(disable MSAA if parallax does not work)"; 124 | ui_category = "Parallax 3D effect"; 125 | > = true; 126 | 127 | uniform int FOV < __UNIFORM_SLIDER_INT1 128 | ui_label = "Lens distortion power"; 129 | ui_tooltip = "Adjust lens distortion main profile (vertical FOV)"; 130 | #if __RESHADE__ < 40000 131 | ui_step = 0.1; 132 | #endif 133 | ui_min = 0; ui_max = 170; 134 | ui_category = "Perspective distortion"; 135 | ui_category_closed = true; 136 | > = 96; 137 | 138 | uniform int LensType < 139 | ui_label = "Type of lens distortion"; 140 | ui_tooltip = "Adjust lens distortion profile type"; 141 | ui_type = "combo"; 142 | ui_items = "Orthographic\0Equisolid\0Equidistant\0Stereographic\0"; 143 | ui_category = "Perspective distortion"; 144 | > = 0; 145 | 146 | uniform float4 K < 147 | ui_label = "Radial correction"; 148 | ui_tooltip = "Adjust lens radial distortion K\n(Brown-Conrady model)\n[K1,K2,K3,K4]"; 149 | ui_type = "drag"; 150 | ui_step = 0.01; 151 | ui_category = "Perspective distortion"; 152 | > = float4(0.0, 0.0, 0.0, 0.0); 153 | 154 | uniform float3 P < 155 | ui_label = "Tangental correction"; 156 | ui_tooltip = "Adjust lens tangental distortion P\n(Brown-Conrady model)\n[P1,P2,P3]"; 157 | ui_type = "drag"; 158 | ui_step = 0.001; 159 | ui_category = "Perspective distortion"; 160 | > = float3(0.0, 0.0, 0.0); 161 | 162 | uniform float ImageScale < __UNIFORM_SLIDER_FLOAT1 163 | ui_label = "Image scale"; 164 | ui_tooltip = "Adjust image borders (does not affect distortion geometry)"; 165 | ui_min = 0.25; ui_max = 1.0; 166 | ui_category = "Perspective distortion"; 167 | > = 1.0; 168 | 169 | uniform bool PerspectiveSwitch < 170 | ui_label = "Lens correction enabled"; 171 | ui_tooltip = "Toggle lens distortion correction"; 172 | ui_category = "Perspective distortion"; 173 | > = true; 174 | 175 | uniform float4 ChGreenK < 176 | ui_label = "Chromatic green correction"; 177 | ui_tooltip = "Adjust lens color fringing K\nfor green channel\n(Brown-Conrady model)\n[Zoom,K1,K2,K3]"; 178 | ui_type = "drag"; 179 | ui_step = 0.001; 180 | ui_category = "Chromatic radial correction"; 181 | ui_category_closed = true; 182 | > = float4(0.0, 0.0, 0.0, 0.0); 183 | 184 | uniform bool SoloGreen < 185 | ui_label = "Green channel adjustment solo"; 186 | ui_tooltip = "Visible only on calibration grid preview"; 187 | ui_category = "Chromatic radial correction"; 188 | > = false; 189 | 190 | uniform float4 ChBlueK < 191 | ui_label = "Chromatic blue correction"; 192 | ui_tooltip = "Adjust lens color fringing K\nfor blue channel\n(Brown-Conrady model)\n[Zoom,K1,K2,K3]"; 193 | ui_type = "drag"; 194 | ui_step = 0.001; 195 | ui_category = "Chromatic radial correction"; 196 | > = float4(0.0, 0.0, 0.0, 0.0); 197 | 198 | uniform bool SoloBlue < 199 | ui_label = "Blue channel adjustment solo"; 200 | ui_tooltip = "Visible only on calibration grid preview"; 201 | ui_category = "Chromatic radial correction"; 202 | > = false; 203 | 204 | uniform bool ChromaticAbrSwitch < 205 | ui_label = "Chromatic correction enabled"; 206 | ui_tooltip = "Toggle color fringing correction"; 207 | ui_category = "Chromatic radial correction"; 208 | > = true; 209 | 210 | uniform float2 ChGreenOffsetL < 211 | ui_label = "Left green center offset"; 212 | ui_tooltip = "Adjust lens center for chromatic aberration"; 213 | ui_type = "drag"; 214 | ui_min = -0.2; ui_max = 0.2; ui_step = 0.001; 215 | ui_category = "Chromatic left-eye center"; 216 | ui_category_closed = true; 217 | > = float2(0.0, 0.0); 218 | 219 | uniform float2 ChBlueOffsetL < 220 | ui_label = "Left blue center offset"; 221 | ui_tooltip = "Adjust lens center for chromatic aberration"; 222 | ui_type = "drag"; 223 | ui_min = -0.2; ui_max = 0.2; ui_step = 0.001; 224 | ui_category = "Chromatic left-eye center"; 225 | > = float2(0.0, 0.0); 226 | 227 | uniform float2 ChGreenOffsetR < 228 | ui_label = "Right green center offset"; 229 | ui_tooltip = "Adjust lens center for chromatic aberration"; 230 | ui_type = "drag"; 231 | ui_min = -0.2; ui_max = 0.2; ui_step = 0.001; 232 | ui_category = "Chromatic right-eye center"; 233 | ui_category_closed = true; 234 | > = float2(0.0, 0.0); 235 | 236 | uniform float2 ChBlueOffsetR < 237 | ui_label = "Right blue center offset"; 238 | ui_tooltip = "Adjust lens center for chromatic aberration"; 239 | ui_type = "drag"; 240 | ui_min = -0.2; ui_max = 0.2; ui_step = 0.001; 241 | ui_category = "Chromatic right-eye center"; 242 | > = float2(0.0, 0.0); 243 | 244 | uniform float Strength < 245 | ui_label = "Sharpen strength"; 246 | ui_type = "drag"; 247 | ui_min = 0.0; ui_max = 100.0; ui_step = 0.01; 248 | ui_category = "Sharpen image"; 249 | ui_category_closed = true; 250 | > = 25.0; 251 | 252 | uniform float Clamp < 253 | ui_label = "Sharpen clamping"; 254 | ui_type = "drag"; 255 | ui_min = 0.5; ui_max = 1.0; ui_step = 0.001; 256 | ui_category = "Sharpen image"; 257 | > = 0.65; 258 | 259 | uniform float Offset < 260 | ui_label = "High-pass offset"; 261 | ui_tooltip = "High-pass cross offset in pixels"; 262 | ui_type = "drag"; 263 | ui_min = 0.01; ui_max = 2; ui_step = 0.002; 264 | ui_category = "Sharpen image"; 265 | > = 0.1; 266 | 267 | uniform bool Preview < 268 | ui_label = "Preview sharpen layer"; 269 | ui_category = "Sharpen image"; 270 | > = false; 271 | 272 | uniform bool Sharpen < 273 | ui_label = "Enable sharpening"; 274 | ui_category = "Sharpen image"; 275 | > = true; 276 | 277 | 278 | ///////////////// 279 | /// FUNCTIONS /// 280 | ///////////////// 281 | 282 | // Adjust to limited RGB 283 | float3 tv(float3 Input) 284 | { return Input*((235.0-16.0)/255.0)+16.0/255.0; } 285 | 286 | // Generate test grid 287 | float3 Grid(float2 Coordinates, float AspectRatio) 288 | { 289 | // Grid settings 290 | #ifndef BoxAmount 291 | #define BoxAmount 31 // Number of boxes horizontally (choose odd number) 292 | #endif 293 | #ifndef thicknessA 294 | #define thicknessA 0.25 // White grid thickness 295 | #endif 296 | #ifndef thicknessB 297 | #define thicknessB 0.125 // Yellow cross thickness 298 | #endif 299 | #ifndef crossColor 300 | #define crossColor float3(1.0, 1.0, 0.0) // Center cross color (yellow) 301 | #endif 302 | 303 | bool RadialPattern = SoloLines==3; 304 | 305 | float2 GridCoord = Coordinates; 306 | // Correct aspect ratio 307 | GridCoord.y -= 0.5; // Center coordinates vertically 308 | GridCoord.y /= AspectRatio; // Correct aspect 309 | GridCoord.y += 0.5; // Center at middle 310 | 311 | float2 CrossUV = GridCoord; // Store center cross coordinates 312 | 313 | float2 PixelSize; float3 gridColor; 314 | // Generate grid pattern 315 | GridCoord = RadialPattern ? length(GridCoord-0.5)*1.618 : GridCoord; // Switch to radial pattern 316 | GridCoord = abs(frac(GridCoord*BoxAmount)*2.0-1.0)*(thicknessA+1.0); 317 | // Anti-aliased grid 318 | PixelSize = fwidth(GridCoord); 319 | GridCoord = smoothstep(1.0-PixelSize, 1.0+PixelSize, GridCoord); 320 | 321 | // Combine/solo vertical and horizontal lines 322 | switch(SoloLines) 323 | { 324 | case 1: 325 | { gridColor = GridCoord.y; break; } 326 | case 2: 327 | { gridColor = GridCoord.x; break; } 328 | default: 329 | { gridColor = max(GridCoord.x, GridCoord.y); break; } 330 | }; 331 | 332 | // Generate center cross 333 | CrossUV = 1.0-abs(CrossUV*2.0-1.0); 334 | CrossUV = CrossUV*(thicknessB/BoxAmount+1.0); 335 | // Anti-aliased cross 336 | PixelSize = fwidth(CrossUV); 337 | CrossUV = smoothstep(1.0-PixelSize, 1.0+PixelSize, CrossUV); 338 | // Combine vertical and horizontal line 339 | float CrossMask = max(CrossUV.y, CrossUV.x); 340 | 341 | // Blend grid and center cross 342 | gridColor = lerp(gridColor, (RadialPattern ? 1.0 : crossColor), CrossMask); 343 | 344 | // Solo colors 345 | if(SoloGreen) gridColor.b = 0.0; 346 | if(SoloBlue) gridColor.g = 0.0; 347 | 348 | // Reduce grid brightness 349 | return tv(gridColor); 350 | }; 351 | 352 | 353 | // Divide screen into two halfs 354 | float2 StereoVision(float2 Coordinates, float Center) 355 | { 356 | float2 StereoCoord = Coordinates; 357 | StereoCoord.x = 0.25 + abs( StereoCoord.x*2.0-1.0 ) * 0.5; // Divide screen in two 358 | StereoCoord.x -= lerp(-0.25, 0.25, Center); // Change center for interpupillary distance (IPD) 359 | // Mirror left half 360 | float ScreenSide = step(0.5, Coordinates.x); 361 | StereoCoord.x *= ScreenSide*2.0-1.0; 362 | StereoCoord.x += 1.0 - ScreenSide; 363 | return StereoCoord; 364 | }; 365 | // Convert stereo coordinates to mono 366 | float2 InvStereoVision(float2 Coordinates, int ScreenMask, float Center) 367 | { 368 | float2 stereoCoord = Coordinates; 369 | stereoCoord.x += Center*0.5 * ScreenMask; 370 | return stereoCoord; 371 | }; 372 | 373 | 374 | // Generate border mask with anti-aliasing from UV coordinates 375 | float BorderMaskAA(float2 Coordinates) 376 | { 377 | float2 RaidalCoord = abs(Coordinates*2.0-1.0); 378 | // Get pixel size in transformed coordinates (for anti-aliasing) 379 | float2 PixelSize = fwidth(RaidalCoord); 380 | 381 | // Create borders mask (with anti-aliasing) 382 | float2 Borders = smoothstep(1.0-PixelSize, 1.0+PixelSize, RaidalCoord); 383 | 384 | // Combine side and top borders 385 | return max(Borders.x, Borders.y); 386 | }; 387 | 388 | float GetDepth(float2 texcoord) 389 | { 390 | return ReShade::GetLinearizedDepth(texcoord); 391 | } 392 | 393 | // Horizontal parallax offset effect 394 | float2 Parallax(float2 Coordinates, float Offset, float Center, int GapOffset, int Steps) 395 | { 396 | // Limit amount of loop steps to make it finite 397 | #ifndef MaximumParallaxSteps 398 | #def MaximumParallaxSteps 64 399 | #endif 400 | 401 | // Offset per step progress 402 | float LayerDepth = 1.0 / min(MaximumParallaxSteps, Steps); 403 | 404 | // Netto layer offset change 405 | float deltaCoordinates = Offset * LayerDepth; 406 | 407 | float2 ParallaxCoord = Coordinates; 408 | // Offset image horizontally so that parallax is in the depth appointed center 409 | ParallaxCoord.x += Offset * Center; 410 | float CurrentDepthMapValue = GetDepth(ParallaxCoord); // Replace function 411 | 412 | // Steep parallax mapping 413 | float CurrentLayerDepth = 0.0; 414 | [loop] 415 | while(CurrentLayerDepth < CurrentDepthMapValue) 416 | { 417 | // Shift coordinates horizontally in linear fasion 418 | ParallaxCoord.x -= deltaCoordinates; 419 | // Get depth value at current coordinates 420 | CurrentDepthMapValue = GetDepth(ParallaxCoord); // Replace function 421 | // Get depth of next layer 422 | CurrentLayerDepth += LayerDepth; 423 | continue; 424 | } 425 | 426 | // Parallax Occlusion Mapping 427 | float2 PrevParallaxCoord = ParallaxCoord; 428 | PrevParallaxCoord.x += deltaCoordinates; 429 | float afterDepthValue = CurrentDepthMapValue - CurrentLayerDepth; 430 | float beforeDepthValue = GetDepth(PrevParallaxCoord); // Replace function 431 | // Store depth read difference for masking 432 | float DepthDifference = beforeDepthValue - CurrentDepthMapValue; 433 | 434 | beforeDepthValue += LayerDepth - CurrentLayerDepth; 435 | // Interpolate coordinates 436 | float weight = afterDepthValue / (afterDepthValue - beforeDepthValue); 437 | ParallaxCoord = PrevParallaxCoord * weight + ParallaxCoord * (1.0 - weight); 438 | 439 | // Apply gap masking (by JMF) 440 | DepthDifference *= GapOffset * Offset * 100.0; 441 | DepthDifference *= ReShade::PixelSize.x; // Replace function 442 | ParallaxCoord.x += DepthDifference; 443 | 444 | return ParallaxCoord; 445 | }; 446 | 447 | 448 | // Lens projection model (algorithm by JMF) 449 | float Orthographic(float rFOV, float R){ return tan(asin(sin(rFOV*0.5)*R))/(tan(rFOV*0.5)*R); } 450 | float Equisolid(float rFOV, float R){ return tan(asin(sin(rFOV*0.25)*R)*2.0)/(tan(rFOV*0.5)*R); } 451 | float Equidistant(float rFOV, float R){ return tan(R*rFOV*0.5)/(tan(rFOV*0.5)*R); } 452 | float Stereographic(float rFOV, float R){ return tan(atan(tan(rFOV*0.25)*R)*2.0)/(tan(rFOV*0.5)*R); } 453 | 454 | // Brown-Conrady radial distortion model (multiply by coordinates) 455 | float kRadial(float R2, float K1, float K2, float K3, float K4) 456 | { return 1.0 + K1*R2 + K2*pow(R2,2) + K3*pow(R2,4) + K4*pow(R2,6); }; 457 | 458 | // Brown-Conrady tangental distortion model (add to coordinates) 459 | float2 pTangental(float2 Coord, float R2, float P1, float P2, float P3, float P4) 460 | { 461 | return float2( 462 | (P1*(R2+pow(Coord.x,2)*2.0)+2.0*P2*Coord.x*Coord.y)*(1.0+P3*R2+P4*pow(R2,2)), 463 | (P2*(R2+pow(Coord.y,2)*2.0)+2.0*P1*Coord.x*Coord.y)*(1.0+P3*R2+P4*pow(R2,2)) 464 | ); 465 | }; 466 | 467 | // RGB to YUV709.luma 468 | float Luma(float3 Input) 469 | { 470 | static const float3 Luma709 = float3(0.2126, 0.7152, 0.0722); 471 | return dot(Input, Luma709); 472 | } 473 | 474 | // Overlay blending mode 475 | float Overlay(float LayerA, float LayerB) 476 | { 477 | float MinA = min(LayerA, 0.5); 478 | float MinB = min(LayerB, 0.5); 479 | float MaxA = max(LayerA, 0.5); 480 | float MaxB = max(LayerB, 0.5); 481 | return 2 * (MinA * MinB + MaxA + MaxB - MaxA * MaxB) - 1.5; 482 | } 483 | 484 | 485 | ////////////// 486 | /// SHADER /// 487 | ////////////// 488 | 489 | float3 VR_ps(float4 vois : SV_Position, float2 texcoord : TexCoord) : SV_Target 490 | { 491 | // Get display aspect ratio (horizontal/vertical resolution) 492 | static const float rAspect = ReShade::AspectRatio; 493 | 494 | // Divide screen in two 495 | float2 UvCoord = StereoSwitch? StereoVision(texcoord, IPD) : texcoord; 496 | 497 | // Generate negative-positive stereo mask 498 | float StereoMask = step(0.5, texcoord.x)*2.0-1.0; 499 | 500 | // Correct lens distortion 501 | if(PerspectiveSwitch) 502 | { 503 | // Center coordinates 504 | UvCoord = UvCoord*2.0-1.0; 505 | UvCoord.x *= rAspect; 506 | float2 StereoCoord = UvCoord; // Save coordinates for Brown-Conrady correction 507 | 508 | // Base distortion correction 509 | if(bool(FOV)) // If FOV is not equal 0 510 | { 511 | float radFOV = radians(FOV); 512 | // Calculate radius 513 | float Radius = length(UvCoord); 514 | // Apply base lens correction 515 | switch(LensType) 516 | { 517 | case 0: 518 | { UvCoord *= Orthographic(radFOV, Radius); break; } 519 | case 1: 520 | { UvCoord *= Equisolid(radFOV, Radius); break; } 521 | case 2: 522 | { UvCoord *= Equidistant(radFOV, Radius); break; } 523 | case 3: 524 | { UvCoord *= Stereographic(radFOV, Radius); break; } 525 | } 526 | }; 527 | 528 | // Lens geometric aberration correction (Brown-Conrady model) 529 | float Diagonal = rAspect; Diagonal *= StereoSwitch ? 0.5 : 1.0; 530 | Diagonal = length(float2(Diagonal, 1.0)); 531 | float InvDiagonal2 = 1.0 / pow(Diagonal, 2); 532 | 533 | StereoCoord /= Diagonal; // Normalize diagonally 534 | float Radius2 = dot(StereoCoord, StereoCoord); // Squared radius 535 | float correctionK = kRadial(Radius2, K.x, K.y, K.z, K.w); 536 | // Apply negative-positive stereo mask for tangental distortion (flip side) 537 | float SideScreenSwitch = (StereoSwitch) ? StereoMask : 1.0; 538 | 539 | float2 correctionP = pTangental( 540 | StereoCoord, 541 | Radius2, 542 | P.x * SideScreenSwitch, 543 | P.y, 544 | P.z, 545 | 0.0 546 | ); 547 | // Expand background to vertical border (but not for test grid for ease of calibration) 548 | UvCoord /= TestGrid ? 1.0 : kRadial(InvDiagonal2, K.x, K.y, K.z, K.w); 549 | UvCoord = UvCoord * correctionK + correctionP; // Apply lens correction 550 | 551 | // Scale image 552 | UvCoord /= TestGrid ? 1.0 : ImageScale; 553 | 554 | // Revert aspect ratio to square 555 | UvCoord.x /= rAspect; 556 | // Move origin back to left top corner 557 | UvCoord = UvCoord*0.5 + 0.5; 558 | } 559 | 560 | // Display test grid 561 | if(TestGrid) return Grid(UvCoord, rAspect); 562 | 563 | // Create parallax effect 564 | if(ParallaxSwitch) 565 | { 566 | float ParallaxDirection = ParallaxOffset*0.01; 567 | // For stereo-vison flip direction on one side 568 | ParallaxDirection *= StereoSwitch ? StereoMask : 1.0; 569 | // Apply parallax effect 570 | UvCoord = Parallax( 571 | UvCoord, 572 | ParallaxDirection, 573 | ParallaxCenter, 574 | ParallaxMaskScalar, 575 | ParallaxSteps 576 | ); 577 | }; 578 | 579 | // Sample image with black borders to display 580 | float3 Image = lerp( 581 | tex2D(ReShade::BackBuffer, UvCoord).rgb, // Display image 582 | 0.0, // Black borders 583 | BorderMaskAA(UvCoord) // Anti-aliased border mask 584 | ); 585 | 586 | // Display image 587 | return Image; 588 | }; 589 | 590 | float3 Chromatic_ps(float4 vois : SV_Position, float2 texcoord : TexCoord) : SV_Target 591 | { 592 | // Bypass chromatic aberration switch 593 | if(!ChromaticAbrSwitch){ return tex2D(ReShade::BackBuffer, texcoord).rgb; } 594 | 595 | // Get display aspect ratio (horizontal/vertical resolution) 596 | float rAspect = ReShade::AspectRatio; 597 | 598 | // Generate negative-positive stereo mask 599 | float SideScreenSwitch = step(0.5, texcoord.x)*2.0-1.0; 600 | 601 | // Divide screen in two if stereo vision mode enabled 602 | float2 CenterCoord = StereoSwitch? StereoVision(texcoord, IPD) : texcoord; 603 | 604 | CenterCoord = CenterCoord*2.0-1.0; // Center coordinates 605 | CenterCoord.x *= rAspect; // Correct aspect ratio 606 | 607 | float Diagonal = rAspect; Diagonal *= StereoSwitch ? 0.5 : 1.0; 608 | Diagonal = length(float2(Diagonal, 1.0)); 609 | 610 | CenterCoord /= Diagonal; // Normalize diagonally 611 | 612 | // Left/right eye mask 613 | float L = step(0.5, 1.0-texcoord.x), R = step(0.5, texcoord.x); 614 | 615 | // Offset center green 616 | float2 CoordGreen = ChGreenOffsetL * L + ChGreenOffsetR * R; 617 | CoordGreen.x *= -1.0; 618 | CoordGreen = 0.01 * CoordGreen + CenterCoord; 619 | // Offset center blue 620 | float2 CoordBlue = ChBlueOffsetL * L + ChBlueOffsetR * R; 621 | CoordBlue.x *= -1.0; 622 | CoordBlue = 0.01 * CoordBlue + CenterCoord; 623 | 624 | // float RadiusGreen = dot(CoordGreen, CoordGreen); // Radius squared (techically accurate) 625 | // float RadiusBlue = dot(CoordBlue, CoordBlue); // Radius squared (techically accurate) 626 | float RadiusGreen = length(CoordGreen); // Radius 627 | float RadiusBlue = length(CoordBlue); // Radius 628 | 629 | // Calculate radial distortion K 630 | float correctionGreenK = (1.0+ChGreenK.x)*kRadial(RadiusGreen, ChGreenK.y, ChGreenK.z, ChGreenK.w, 0.0); 631 | float correctionBlueK = (1.0+ChBlueK.x)*kRadial(RadiusBlue, ChBlueK.y, ChBlueK.z, ChBlueK.w, 0.0); 632 | 633 | // Apply chromatic aberration correction 634 | CoordGreen = CoordGreen * correctionGreenK; 635 | CoordBlue = CoordBlue * correctionBlueK; 636 | 637 | CoordGreen *= Diagonal; CoordBlue *= Diagonal; // Back to vertical normalization 638 | CoordGreen.x /= rAspect; CoordBlue.x /= rAspect; // Back to square 639 | 640 | // Move origin to left top corner 641 | CoordGreen = CoordGreen * 0.5 + 0.5; CoordBlue = CoordBlue * 0.5 + 0.5; 642 | 643 | // Generate border mask for green and blue channel 644 | float MaskBlue, MaskGreen; if(StereoSwitch) 645 | { 646 | // Mask compensation for center cut 647 | float CenterCut = 0.5+(0.5-IPD)*SideScreenSwitch; 648 | 649 | // Mask sides and center cut for blue channel 650 | float2 MaskCoordBlue; 651 | MaskCoordBlue.x = CoordBlue.x*2.0 - CenterCut; // Compensate for 2 views 652 | MaskCoordBlue.y = CoordBlue.y; 653 | MaskBlue = BorderMaskAA(MaskCoordBlue); 654 | 655 | // Mask sides and center cut for green channel 656 | float2 MaskCoordGreen; 657 | MaskCoordGreen.x = CoordGreen.x*2.0 - CenterCut; // Compensate for 2 views 658 | MaskCoordGreen.y = CoordGreen.y; 659 | MaskGreen = BorderMaskAA(MaskCoordGreen); 660 | 661 | // Reverse stereo coordinates to single view 662 | CoordGreen = InvStereoVision(CoordGreen, SideScreenSwitch, IPD); 663 | CoordBlue = InvStereoVision(CoordBlue, SideScreenSwitch, IPD); 664 | } 665 | else 666 | { 667 | MaskBlue = BorderMaskAA(CoordBlue); 668 | MaskGreen = BorderMaskAA(CoordGreen); 669 | }; 670 | 671 | float3 Image; 672 | // Sample image red 673 | Image.r = tex2D(ReShade::BackBuffer, texcoord).r; 674 | // Sample image green 675 | Image.g = lerp( 676 | tex2D(ReShade::BackBuffer, CoordGreen).g, 677 | 0.0, // Black borders 678 | MaskGreen // Anti-aliased border mask 679 | ); 680 | // Sample image blue 681 | Image.b = lerp( 682 | tex2D(ReShade::BackBuffer, CoordBlue).b, 683 | 0.0, // Black borders 684 | MaskBlue // Anti-aliased border mask 685 | ); 686 | 687 | // Display chromatic aberration 688 | return Image; 689 | }; 690 | 691 | 692 | // Sharpen pass 693 | float3 FilmicSharpenPS(float4 vois : SV_Position, float2 UvCoord : TexCoord) : SV_Target 694 | { 695 | // Bypass sharpening 696 | if(!Sharpen) return tex2D(ReShade::BackBuffer, UvCoord).rgb; 697 | 698 | float2 Pixel = ReShade::PixelSize * Offset; 699 | // Sample display image 700 | float3 Source = tex2D(ReShade::BackBuffer, UvCoord).rgb; 701 | 702 | float2 NorSouWesEst[4] = { 703 | float2(UvCoord.x, UvCoord.y + Pixel.y), 704 | float2(UvCoord.x, UvCoord.y - Pixel.y), 705 | float2(UvCoord.x + Pixel.x, UvCoord.y), 706 | float2(UvCoord.x - Pixel.x, UvCoord.y) 707 | }; 708 | 709 | // Luma high-pass 710 | float HighPass = 0; 711 | for(int i=0; i<4; i++) HighPass += Luma(tex2D(ReShade::BackBuffer, NorSouWesEst[i]).rgb); 712 | HighPass = 0.5 - 0.5 * (HighPass * 0.25 - Luma(Source)); 713 | 714 | // Sharpen strength 715 | HighPass = lerp(0.5, HighPass, Strength); 716 | 717 | // Clamping sharpen 718 | HighPass = (Clamp != 1) ? max(min(HighPass, Clamp), 1 - Clamp) : HighPass; 719 | 720 | float3 Sharpen = float3( 721 | Overlay(Source.r, HighPass), 722 | Overlay(Source.g, HighPass), 723 | Overlay(Source.b, HighPass) 724 | ); 725 | 726 | return (Preview) ? HighPass : Sharpen; 727 | } 728 | 729 | 730 | ////////////// 731 | /// OUTPUT /// 732 | ////////////// 733 | 734 | technique VR < ui_label = "Virtual Reality"; ui_tooltip = "Virtual Reality:\n" 735 | "* apply lens distortion correction\n" 736 | "* correct chromatic aberration\n" 737 | "* generate stereo-3D effect"; > 738 | { 739 | pass 740 | { 741 | VertexShader = PostProcessVS; 742 | PixelShader = VR_ps; 743 | } 744 | pass 745 | { 746 | VertexShader = PostProcessVS; 747 | PixelShader = FilmicSharpenPS; 748 | } 749 | pass{ 750 | VertexShader = PostProcessVS; 751 | PixelShader = Chromatic_ps; 752 | } 753 | }; --------------------------------------------------------------------------------