Experimental: High-Framerate Frame-Blending
- ShoterXX
- Topic Author
Simply put, my monitor only goes as far as 76Hz, but games like PSO2 go well over that.
This simply blends frames together, but unlike Motion Blur, it only blends the last frame with the current one.
So yeah, less frames wasted. Note that disabling "Blend All Frames" makes it so that only 1 every 2 frames updates the screen, meaning that if you have 120 FPS, you'll only get 60 FPS effectively. Enabling it makes things smoother, but blurrier.
Also, for obvious reasons, this doesn't work well with V-Sync. And no, this does not replace a 144Hz monitor, lol.
Might develop this a bit more, but not sure if it's worth it.
Old Versions:
v0.01:
- Initial release.
//#include "Utils.fx"
#if __RENDERER__ >= 0x14300
#error "OpenGL is not supported by this shader."
#define RENDERER_IS_OGL 1
#elif __RENDERER__ >= 0x09100 && __RENDERER__ <= 0x09300
#define RENDERER_IS_D3D9 1
#elif __RENDERER__ == 0x0A000 || __RENDERER__ == 0x0A100
#define RENDERER_IS_D3D10 1
#elif __RENDERER__ >= 0x0B000 && __RENDERER__ <= 0x0B200
#define RENDERER_IS_D3D11 1
#endif
#include "ReShade.fxh"
namespace ShoterXX
{
#ifndef RENDERER_IS_D3D9
#define SAFEINT uint
#else
#define SAFEINT int
#endif
uniform SAFEINT fCntr < source = "framecount"; >;
//MACRO FUNCTIONS
#define fetchMe() tex2D(ReShade::BackBuffer, xy)
#define CreateLQTexture(NAME, x, y)\
\
texture2D NAME##Tex\
{\
Width = ##x;\
Height = ##y;\
Format = RGBA8;\
};\
\
sampler2D NAME\
{\
Texture = NAME##Tex;\
};
//FUNCTIONS
float4 sqr(in float4 sqrMe) { return sqrMe * sqrMe; }
float sqr(in float sqrMe) { return sqrMe * sqrMe; }
}
namespace ShoterXX
{
CreateLQTexture(FBCopy, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(F0, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(F1, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(F0Copy, BUFFER_WIDTH, BUFFER_HEIGHT);
uniform SAFEINT blendAll <
ui_type = "combo";
ui_label = "Blend All Frames";
ui_items = "No\0Yes\0Yes + extra frame every 2nd frame (experimental)\0";
ui_tooltip = "Should the effect blend frames every frame, making the image smoother, but blurrier. \"No\" is effectively the same as running half-framerate.";
> = 2;
void FB_ReadFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frame1 : SV_Target1)
{
frame0 = tex2D(F0Copy, xy);
frame1 = fetchMe();
}
void FB_BlendFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
float4 frame0 = tex2D(F0, xy);
float4 frame1 = tex2D(F1, xy);
#ifndef RENDERER_IS_D3D9
bool hasFrame = (blendAll | 1) || (fCntr | 1);
#else
bool hasFrame = (blendAll % 2) || (fCntr % 2);
#endif
[branch]
if (hasFrame)
{
put = sqrt((sqr(frame0) + sqr(frame1))*.5);
}
else
{
float4 frameB = tex2D(FBCopy, xy);
put = blendAll? sqrt((sqr(frameB) + sqr(frame1))*.5) : frameB;
}
}
void FB_SaveFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F1, xy);
frameB = fetchMe();
}
}
technique FrameBlend
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_ReadFrames;
RenderTarget0 = ShoterXX::F0Tex;
RenderTarget1 = ShoterXX::F1Tex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_BlendFrames;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_SaveFrames;
RenderTarget0 = ShoterXX::F0CopyTex;
RenderTarget1 = ShoterXX::FBCopyTex;
}
}
v0.02:
- Fixed flipped bitwise operators for DX10+;
- Restructured code, reducing a single fetch.
//#include "Utils.fx"
#if __RENDERER__ >= 0x14300
#error "OpenGL is not supported by this shader."
#define RENDERER_IS_OGL 1
#elif __RENDERER__ >= 0x09100 && __RENDERER__ <= 0x09300
#define RENDERER_IS_D3D9 1
#elif __RENDERER__ == 0x0A000 || __RENDERER__ == 0x0A100
#define RENDERER_IS_D3D10 1
#elif __RENDERER__ >= 0x0B000 && __RENDERER__ <= 0x0B200
#define RENDERER_IS_D3D11 1
#endif
#include "ReShade.fxh"
namespace ShoterXX
{
#ifndef RENDERER_IS_D3D9
#define SAFEINT uint
#else
#define SAFEINT int
#endif
uniform SAFEINT fCntr < source = "framecount"; >;
//MACRO FUNCTIONS
#define fetchMe() tex2D(ReShade::BackBuffer, xy)
#define CreateLQTexture(NAME, x, y)\
\
texture2D NAME##Tex\
{\
Width = ##x;\
Height = ##y;\
Format = RGBA8;\
};\
\
sampler2D NAME\
{\
Texture = NAME##Tex;\
};
//FUNCTIONS
float4 sqr(in float4 sqrMe) { return sqrMe * sqrMe; }
float sqr(in float sqrMe) { return sqrMe * sqrMe; }
}
namespace ShoterXX
{
CreateLQTexture(F0, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(F0Copy, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FB, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FBCopy, BUFFER_WIDTH, BUFFER_HEIGHT);
uniform SAFEINT blendAll <
ui_type = "combo";
ui_label = "Blend All Frames";
ui_items = "No\0Yes\0Yes + extra frame every 2nd frame (experimental)\0";
ui_tooltip = "Should the effect blend frames every frame, making the image smoother, but blurrier. \"No\" is effectively the same as running half-framerate.";
> = 2;
void FB_CopyFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F0, xy);
frameB = tex2D(FB, xy);
}
void FB_BlendFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F0Copy, xy);
float4 frame1 = fetchMe();
#ifndef RENDERER_IS_D3D9
bool hasFrame = (blendAll & 1) | (fCntr & 1);
#else
bool hasFrame = (blendAll % 2) || (fCntr % 2);
#endif
[branch]
if (hasFrame)
{
frameB = sqrt((sqr(frame0) + sqr(frame1))*.5);
}
else
{
frameB = tex2D(FBCopy, xy);
frameB = blendAll? sqrt((sqr(frameB) + sqr(frame1))*.5) : frameB;
}
frame0 = frame1;
}
void FB_DisplayFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = tex2D(FBCopy, xy);
}
}
technique FrameBlend
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_CopyFrames;
RenderTarget0 = ShoterXX::F0CopyTex;
RenderTarget1 = ShoterXX::FBCopyTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_BlendFrames;
RenderTarget0 = ShoterXX::F0Tex;
RenderTarget1 = ShoterXX::FBTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_DisplayFrames;
}
}
v0.03:
- This shader should work with OpenGL, removed #error preprocess command;
- Added alternative version, "FrameBlur": it's significantly faster, but blurs every single frame together, effectively making it a regular motion blur.
//#include "Utils.fx"
#if __RENDERER__ >= 0x14300
#define RENDERER_IS_OGL 1
#elif __RENDERER__ >= 0x09100 && __RENDERER__ <= 0x09300
#define RENDERER_IS_D3D9 1
#elif __RENDERER__ == 0x0A000 || __RENDERER__ == 0x0A100
#define RENDERER_IS_D3D10 1
#elif __RENDERER__ >= 0x0B000 && __RENDERER__ <= 0x0B200
#define RENDERER_IS_D3D11 1
#endif
#include "ReShade.fxh"
namespace ShoterXX
{
#ifndef RENDERER_IS_D3D9
#define SAFEINT uint
#else
#define SAFEINT int
#endif
uniform SAFEINT fCntr < source = "framecount"; >;
//MACRO FUNCTIONS
#define fetchMe() tex2D(ReShade::BackBuffer, xy)
#define CreateLQTexture(NAME, x, y)\
\
texture2D NAME##Tex\
{\
Width = ##x;\
Height = ##y;\
Format = RGBA8;\
};\
\
sampler2D NAME\
{\
Texture = NAME##Tex;\
};
//FUNCTIONS
float4 sqr(in float4 sqrMe) { return sqrMe * sqrMe; }
float sqr(in float sqrMe) { return sqrMe * sqrMe; }
}
namespace ShoterXX
{
CreateLQTexture(F0, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(F0Copy, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FB, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FBCopy, BUFFER_WIDTH, BUFFER_HEIGHT);
uniform SAFEINT blendAll <
ui_type = "combo";
ui_label = "Blend All Frames";
ui_items = "No\0Yes\0Yes + extra frame every 2nd frame (experimental)\0";
ui_tooltip = "Should the effect blend frames every frame, making the image smoother, but blurrier. \"No\" is effectively the same as running half-framerate.";
> = 2;
void FB_CopyFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F0, xy);
frameB = tex2D(FB, xy);
}
void FB_BlendFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F0Copy, xy);
float4 frame1 = fetchMe();
#ifndef RENDERER_IS_D3D9
bool hasFrame = (blendAll & 1) | (fCntr & 1);
#else
bool hasFrame = (blendAll % 2) || (fCntr % 2);
#endif
[branch]
if (hasFrame)
{
frameB = sqrt((sqr(frame0) + sqr(frame1))*.5);
}
else
{
frameB = tex2D(FBCopy, xy);
frameB = blendAll? sqrt(sqr(frameB)*.666666666667 + sqr(frame1)*.333333333333) : frameB;
}
frame0 = frame1;
}
void FB_DisplayFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = tex2D(FBCopy, xy);
}
void FB_SaveFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = fetchMe();
}
void FB_BlurFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = sqrt(sqr(fetchMe())*.666666666667 + sqr(tex2D(FB, xy))*.333333333333);
}
}
technique FrameBlend
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_CopyFrames;
RenderTarget0 = ShoterXX::F0CopyTex;
RenderTarget1 = ShoterXX::FBCopyTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_BlendFrames;
RenderTarget0 = ShoterXX::F0Tex;
RenderTarget1 = ShoterXX::FBTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_DisplayFrames;
}
}
technique FrameBlur
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_BlurFrames;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_SaveFrames;
RenderTarget0 = ShoterXX::FBTex;
}
}
v0.04:
- Added "Target Framerate" field. When your framerate drops below this value, it disables the effect, preventing stuttering and awkward bluring;
- Added "Blur Strength" field, which determines how much it retains from the previous frames (only works with "FrameBlur");
- Something else, forgot what.
//#include "Utils.fx"
#if __RENDERER__ >= 0x14300
#define RENDERER_IS_OGL 1
#elif __RENDERER__ >= 0x09100 && __RENDERER__ <= 0x09300
#define RENDERER_IS_D3D9 1
#elif __RENDERER__ == 0x0A000 || __RENDERER__ == 0x0A100
#define RENDERER_IS_D3D10 1
#elif __RENDERER__ >= 0x0B000 && __RENDERER__ <= 0x0B200
#define RENDERER_IS_D3D11 1
#endif
#include "ReShade.fxh"
namespace ShoterXX
{
#ifndef RENDERER_IS_D3D9
#define SAFEINT uint
#else
#define SAFEINT int
#endif
uniform SAFEINT fCntr < source = "framecount"; >;
uniform float fTime < source = "frametime"; >;
//MACRO FUNCTIONS
#define fetchMe() tex2D(ReShade::BackBuffer, xy)
#define CreateLQTexture(NAME, x, y)\
\
texture2D NAME##Tex\
{\
Width = ##x;\
Height = ##y;\
Format = RGBA8;\
};\
\
sampler2D NAME\
{\
Texture = NAME##Tex;\
};
//FUNCTIONS
float4 sqr(in float4 sqrMe) { return sqrMe * sqrMe; }
float sqr(in float sqrMe) { return sqrMe * sqrMe; }
}
namespace ShoterXX
{
CreateLQTexture(F0, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(F0Copy, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FB, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FBCopy, BUFFER_WIDTH, BUFFER_HEIGHT);
uniform SAFEINT blendAll <
ui_type = "combo";
ui_label = "Blend All Frames";
ui_items = "No\0Yes\0Yes + extra frame every 2nd frame (experimental)\0";
ui_tooltip = "Should the effect blend frames every frame, making the image smoother, but blurrier. \"No\" is effectively the same as running half-framerate.";
> = 2;
uniform float tFRate <
ui_type = "drag";
ui_label = "Target Framerate";
ui_min = 0.0; ui_max = 300.0;
ui_tooltip = "Monitor's refresh rate. The effect shall compensate when framerate is too low to look proper.";
> = 75.;
uniform float blurStr <
ui_type = "drag";
ui_label = "Blur Strength (FrameBlur only)";
ui_min = 0.0; ui_max = 1.0;
ui_tooltip = "How much of the old frame persists.";
> = .666666666667;
void FB_CopyFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F0, xy);
frameB = tex2D(FB, xy);
}
void FB_BlendFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F0Copy, xy);
float4 frame1 = fetchMe();
#ifndef RENDERER_IS_D3D9
bool hasFrame = (blendAll & 1) | (fCntr & 1) ;
#else
bool hasFrame = (blendAll % 2) || (fCntr % 2);
#endif
float fEst = 1. - saturate(((1000./fTime)/tFRate) - 1.);
[branch]
if (hasFrame)
{
frameB = sqrt(lerp(sqr(frame0),sqr(frame1),.5 + .5 * fEst));
}
else
{
frameB = tex2D(FBCopy, xy);
frameB = blendAll? sqrt(lerp(sqr(frameB),sqr(frame1),.333333333333 + .666666666667 * fEst)) : fEst? frame1 : frameB;
}
frame0 = frame1;
}
void FB_DisplayFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = tex2D(FBCopy, xy);
}
void FB_SaveFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = fetchMe();
}
void FB_BlurFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
float fEst = 1. - saturate(((1000./fTime)/tFRate) - 1.);
put = sqrt(lerp(sqr(tex2D(FB, xy)),sqr(fetchMe()),(1.-blurStr) + blurStr * fEst));
}
}
technique FrameBlend
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_CopyFrames;
RenderTarget0 = ShoterXX::F0CopyTex;
RenderTarget1 = ShoterXX::FBCopyTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_BlendFrames;
RenderTarget0 = ShoterXX::F0Tex;
RenderTarget1 = ShoterXX::FBTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_DisplayFrames;
}
}
technique FrameBlur
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_BlurFrames;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_SaveFrames;
RenderTarget0 = ShoterXX::FBTex;
}
}
v0.05:
- Added a "Frame Drop Threshold", allowing for adjusting how much the framerate can dip without dropping a blurred frame. Default is 33% of target framerate;
- Frametime is now the average of 2 frames, reducing the number of needlessly dropped frames;
- Fixed issue where FrameBlend was showing a 1-frame-old image. Oops;
- Added debug mode that shows when the effect is dropping frames;
- Minor miscelaneous changes.
//#include "Utils.fx"
#if __RENDERER__ >= 0x14300
#define RENDERER_IS_OGL 1
#elif __RENDERER__ >= 0x09100 && __RENDERER__ <= 0x09300
#define RENDERER_IS_D3D9 1
#elif __RENDERER__ == 0x0A000 || __RENDERER__ == 0x0A100
#define RENDERER_IS_D3D10 1
#elif __RENDERER__ >= 0x0B000 && __RENDERER__ <= 0x0B200
#define RENDERER_IS_D3D11 1
#endif
#include "ReShade.fxh"
namespace ShoterXX
{
#ifndef RENDERER_IS_D3D9
#define SAFEINT uint
#else
#define SAFEINT int
#endif
uniform SAFEINT fCntr < source = "framecount"; >;
uniform float fTime < source = "frametime"; >;
//MACRO FUNCTIONS
#define fetchMe() tex2D(ReShade::BackBuffer, xy)
#define CreateLQTexture(NAME, x, y)\
\
texture2D NAME##Tex\
{\
Width = ##x;\
Height = ##y;\
Format = RGBA8;\
};\
\
sampler2D NAME\
{\
Texture = NAME##Tex;\
};
//FUNCTIONS
float4 sqr(in float4 sqrMe) { return sqrMe * sqrMe; }
float sqr(in float sqrMe) { return sqrMe * sqrMe; }
}
namespace ShoterXX
{
CreateLQTexture(F0, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(F0Copy, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FB, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FBCopy, BUFFER_WIDTH, BUFFER_HEIGHT);
uniform SAFEINT blendAll <
ui_type = "combo";
ui_label = "Blend All Frames";
ui_items = "No\0Yes\0Yes + extra frame every 2nd frame (experimental)\0";
ui_tooltip = "Should the effect blend frames every frame, making the image smoother, but blurrier. \"No\" is effectively the same as running half-framerate.";
> = 0;
uniform SAFEINT tFRate <
ui_type = "drag";
ui_label = "Target Framerate";
ui_min = 0.0; ui_max = 255.0;
ui_tooltip = "Monitor's refresh rate. The effect shall compensate when framerate is too low to look proper.";
> = 75.;
uniform float tFThres <
ui_type = "drag";
ui_label = "Frame Drop Threshold";
ui_min = 0.0; ui_max = 1.0;
ui_tooltip = "How much the effect is allowed to lax on framerate drops, providing a smoother overall. Too much will result on lack of framerate compensation, too little may cause uneccessary frames to drop.";
> = .333333333333;
uniform float blurStr <
ui_type = "drag";
ui_label = "Blur Strength (FrameBlur only)";
ui_min = 0.0; ui_max = 1.0;
ui_tooltip = "How much of the old frame persists.";
> = .666666666667;
uniform bool dbgON <
ui_label = "Show dropped frames";
ui_tooltip = "Show dropped frames. Grey is OK, Yellow means partially dropped, Red means fully dropped.";
ui_category = "\n\nDEBUG\0";
> = 0;
void FB_CopyFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F0, xy);
frameB = tex2D(FB, xy);
}
void FB_BlendFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
float4 frame1 = tex2D(F0Copy, xy);
frame0 = fetchMe();
#ifndef RENDERER_IS_D3D9
bool hasFrame = (blendAll & 1) | (fCntr & 1) ;
#else
bool hasFrame = (blendAll % 2) || (fCntr % 2);
#endif
float fEst = saturate((((1000./fTime) + frame0.a * 255.)/(4.*tFRate*(1. - tFThres))) - 1.);
[branch]
if (hasFrame)
{
frameB = sqrt(lerp(sqr(frame1),sqr(frame0), 1. - .5 * fEst));
}
else
{
frameB = tex2D(FBCopy, xy);
[branch]
if (blendAll)
{
frameB = sqrt(lerp(sqr(frame1),sqr(frame0), 1. - .666666666667 * fEst));
}
else
{
frameB = (1. - fEst)? frame0 : frameB;
}
}
frame0.a = (1000./fTime)/255.;
[branch]
if (dbgON)
if (xy.x > .99 || xy.y > .99 || xy.x < .01 || xy.y < .01)
{
if(1.-fEst)
{
if(fEst == 0)
{
frameB.rgb = float3(1,0,0);
}
else
{
frameB.rgb = float3(1,1,0);
}
}
else
{
frameB.rgb = float3(0.5,0.5,0.5);
}
}
}
void FB_DisplayFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = tex2D(FB, xy);
}
void FB_SaveFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = fetchMe();
put.a = (1000./fTime)/255.;
}
void FB_BlurFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = tex2D(FB, xy);
float fEst = saturate((((1000./fTime) + put.a * 255.)/(4.*tFRate*(1. - tFThres))) - 1.);
put = sqrt(lerp(sqr(put),sqr(fetchMe()), 1. - blurStr * fEst));
}
}
technique FrameBlend
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_CopyFrames;
RenderTarget0 = ShoterXX::F0CopyTex;
RenderTarget1 = ShoterXX::FBCopyTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_BlendFrames;
RenderTarget0 = ShoterXX::F0Tex;
RenderTarget1 = ShoterXX::FBTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_DisplayFrames;
}
}
technique FrameBlur
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_BlurFrames;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_SaveFrames;
RenderTarget0 = ShoterXX::FBTex;
}
}
v0.06:
- Only FrameBlend exists now, but it's a much more comprehensive effect, blending up to 255 previously unused frames for silk-smoothness (good luck reaching 60*255 = 15300 FPS), based on your set framerate;
- Fixed random black frames/severe stutter (report if you still find any);
- Removed debug mode because it's not really useful anymore;
- Minor changes;
#if __RENDERER__ >= 0x14300
#define RENDERER_IS_OGL 1
#elif __RENDERER__ >= 0x09100 && __RENDERER__ <= 0x09300
#define RENDERER_IS_D3D9 1
#elif __RENDERER__ == 0x0A000 || __RENDERER__ == 0x0A100
#define RENDERER_IS_D3D10 1
#elif __RENDERER__ >= 0x0B000 && __RENDERER__ <= 0x0B200
#define RENDERER_IS_D3D11 1
#endif
#include "ReShade.fxh"
namespace ShoterXX
{
#ifndef RENDERER_IS_D3D9
#define SAFEINT uint
#else
#define SAFEINT int
#endif
uniform SAFEINT fCntr < source = "framecount"; >;
uniform float fTime < source = "frametime"; >;
//MACRO FUNCTIONS
#define fetchMe() tex2D(ReShade::BackBuffer, xy)
#define CreateLQTexture(NAME, x, y)\
\
texture2D NAME##Tex\
{\
Width = ##x;\
Height = ##y;\
Format = RGBA8;\
};\
\
sampler2D NAME\
{\
Texture = NAME##Tex;\
};
#define CreateMQTexture(NAME, x, y)\
\
texture2D NAME##Tex\
{\
Width = ##x;\
Height = ##y;\
Format = RGBA16F;\
};\
\
sampler2D NAME\
{\
Texture = NAME##Tex;\
};
//FUNCTIONS
float4 sqr(in float4 sqrMe) { return sqrMe * sqrMe; }
float3 sqr(in float3 sqrMe) { return sqrMe * sqrMe; }
float sqr(in float sqrMe) { return sqrMe * sqrMe; }
}
namespace ShoterXX
{
CreateMQTexture(F0, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateMQTexture(F0Copy, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FB, BUFFER_WIDTH, BUFFER_HEIGHT);
CreateLQTexture(FBCopy, BUFFER_WIDTH, BUFFER_HEIGHT);
uniform SAFEINT tFRate <
ui_type = "drag";
ui_label = "Target Framerate";
ui_min = 1; ui_max = 255;
ui_tooltip = "Monitor's refresh rate. The effect shall compensate when framerate is too low to look proper.";
> = 60;
uniform float tFThres <
ui_type = "drag";
ui_label = "Frame Drop Threshold";
ui_min = 0.0; ui_max = 2.0;
ui_tooltip = "How much the effect is allowed to lax on frame drops, providing a smoother overall. Too much will result on lack of framerate compensation and cause visual stutter, too little may cause frames to drop uneccessarily early, and not be as smooth.";
> = 1.125;
uniform float blurStr <
ui_type = "drag";
ui_label = "Blur Strength";
ui_min = 0.0; ui_max = 1.0;
ui_tooltip = "Changes bias between the buffer and the most recent frame.";
> = 0.5;
void FB_CopyFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frame0 = tex2D(F0, xy);
frameB = tex2D(FB, xy);
}
void FB_DisplayFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 put : SV_Target0)
{
put = tex2D(FB, xy);
}
void FB_AddFrames(in float4 vpos : SV_Position, in float2 xy : TEXCOORD, out float4 frame0 : SV_Target0, out float4 frameB : SV_Target1)
{
frameB = tex2D(FBCopy, xy);
frame0 = tex2D(F0Copy, xy);
int curFrameN = frameB.a * 255;
int lastNFrame = ((1000./frame0.a)/tFRate * (1 + tFThres));
int nFrame = ((1000./fTime)/tFRate * (1 + tFThres));
nFrame = floor((nFrame + lastNFrame * lastNFrame)/(nFrame + lastNFrame));
[branch]
if (!curFrameN)
{
frame0 = fetchMe();
}
else
{
frame0 = sqrt((sqr(frame0) * curFrameN + sqr(fetchMe())*(1./blurStr))/(curFrameN+(1./blurStr)));
}
[branch]
if (curFrameN > (nFrame - 2))
{
frameB = float4(frame0.rgb,0.);
}
else
{
frameB.a = ((++curFrameN)/255.);
}
frame0.a = fTime;
}
technique FrameBlend
{
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_AddFrames;
RenderTarget0 = ShoterXX::F0Tex;
RenderTarget1 = ShoterXX::FBTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_CopyFrames;
RenderTarget0 = ShoterXX::F0CopyTex;
RenderTarget1 = ShoterXX::FBCopyTex;
}
pass
{
VertexShader = PostProcessVS;
PixelShader = ShoterXX::FB_DisplayFrames;
}
}
}
Some actual screenshots:
Please Log in or Create an account to join the conversation.
- Scorpio82CO
Please Log in or Create an account to join the conversation.
- ShoterXX
- Topic Author
Scorpio82CO wrote: I can´t made it work in OpenGL
Yeah, it's currently disabled, because OpenGL often requires extra work, and often goes unused, but I can take a look at it tomorrow.
Please Log in or Create an account to join the conversation.
- crosire
That should not be the case. If it is, then that is a bug and I would like to know about it.ShoterXX wrote: OpenGL often requires extra work, and often goes unused
Please Log in or Create an account to join the conversation.
- ShoterXX
- Topic Author
crosire wrote:
That should not be the case. If it is, then that is a bug and I would like to know about it.ShoterXX wrote: OpenGL often requires extra work, and often goes unused
For example, just now:
#ifndef RENDERER_IS_D3D9
bool hasFrame = (blendAll & 1) | (fCntr & 1);
#else
bool hasFrame = (blendAll % 2) || (fCntr % 2);
#endif
DX9 cannot use bitwise operations, so I use modulo. However, with OGL, it'll complain about implicit cast between unsigned integer and boolean. Fortunately, the way I had it was already OK, so all I need to do is remove the #error command. But IIRC, modulo doesn't work the same between OGL and DX either.
Then again, it's been a while since I last used OGL, so things most likely have changed,
Please Log in or Create an account to join the conversation.
- ShoterXX
- Topic Author
Please Log in or Create an account to join the conversation.
- Michael35699
Please Log in or Create an account to join the conversation.
- crosire
Please Log in or Create an account to join the conversation.
- ShoterXX
- Topic Author
And I'll probably need some help finding a bug causing some games to flicker like mad.
Please Log in or Create an account to join the conversation.
- Michael35699
Please Log in or Create an account to join the conversation.
- ShoterXX
- Topic Author
Please Log in or Create an account to join the conversation.
- AndreyRGW
Please Log in or Create an account to join the conversation.
- ShoterXX
- Topic Author
Please Log in or Create an account to join the conversation.
- ShoterXX
- Topic Author
Lesson learned, unsigned integers in logic can be dangerous.
Please Log in or Create an account to join the conversation.
- Sami 1999
Please Log in or Create an account to join the conversation.
- reter
Please Log in or Create an account to join the conversation.
- lowenz
More kind of decimation.reter wrote: Wait, is that similar to frame interpolation?
Please Log in or Create an account to join the conversation.
- DaInferno
Please Log in or Create an account to join the conversation.
- ShoterXX
- Topic Author
As for being worked on... not really. Performance-wise, I don't see a significant way to improve it. I tried messing with ways to blur in-between frames, but the performance hit and quality were not good enough.
Please Log in or Create an account to join the conversation.
- DaInferno
Please Log in or Create an account to join the conversation.