Skip to content

Instantly share code, notes, and snippets.

@mrsteyk
Created October 27, 2025 15:28
Show Gist options
  • Select an option

  • Save mrsteyk/13d37d3e4ab988f71ebdf33496b51e1a to your computer and use it in GitHub Desktop.

Select an option

Save mrsteyk/13d37d3e4ab988f71ebdf33496b51e1a to your computer and use it in GitHub Desktop.
LottesDBM ReShade
#define BUFFER_PIXEL_SIZE float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT)
#define BUFFER_SCREEN_SIZE float2(BUFFER_WIDTH, BUFFER_HEIGHT)
#define BUFFER_ASPECT_RATIO (BUFFER_WIDTH * BUFFER_RCP_HEIGHT)
void PostProcessVS(in uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD)
{
texcoord.x = (id == 2) ? 2.0 : 0.0;
texcoord.y = (id == 1) ? 2.0 : 0.0;
position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
texture texColorBuffer : COLOR;
sampler colorSamplerLinear { Texture = texColorBuffer; SRGBTexture = true; };
//sampler colorSamplerLinear { Texture = texColorBuffer; };
// workaround for gamescope zero-alloc bug!
uniform float4 UniformSingleValue = float4(0.0f, 0.0f, 0.0f, 0.0f);
// ReShade config jank
#define half float
#define half2 float2
#define half3 float3
#define half4 float4
// CONFIGURATION OPTIONS
// =====================
// Grille direction (0=horz, 1=vert)
#define DBM_V 0
// Grille (0=monochome, 1=chromatic)
#define DBM_C 1
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JUNKY DBM INTERFACE
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Used for emulation of sRGB->linear reads and for store
float SelF1(float a,float b,bool p){return p?b:a;}
bool GtF1(float a,float b){return a>b;}
// To sRGB
float SrgbEncF1(float c){float3 j=float3(0.0031308*12.92,12.92,1.0/2.4);
float2 k=float2(1.055,-0.055);
return clamp(j.x,c*j.y,pow(c,j.z)*k.x+k.y);}
// To linear
float SrgbDecF1(float c){float3 j=float3(0.04045,1.0/12.92,2.4);
float2 k=float2(1.0/1.055,0.055/1.055);
return SelF1(c*j.y,pow(c*k.x+k.y,j.z),GtF1(c,j.x));}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define DBM_HLSL 1
// This must return linear data (not perceptual) else algorithm won't work
half3 DbmTex(float2 p){half4 c;
//c=textureLod(iChannel0,p,0.0).rgb;
//c=tex2Dlod(ReShade::BackBuffer, float4(p, 0, 0));
c=tex2Dlod(colorSamplerLinear, float4(p, 0, 0));
// if you use non srgb sampler - uncomment this
//c.x=SrgbDecF1(c.x);c.y=SrgbDecF1(c.y);c.z=SrgbDecF1(c.z);
return c.rgb;}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
//. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
//|||||||||||||||||||||||||||||||||||||::|||||||||||||||||||||||||||||||||||||||
//|||||||||||||||||||||||||||||||||||||::|||||||||||||||||||||||||||||||||||||||
//=====================================||=======================================
//
// .. ..
// ........:: ::........ ::..::..::
// OO OO OO OO OO OO OO
// OOooooooOO OOooooooOO OO OO OO
//
// ___ by
// |imothy Lottes
//
//-------------------------------------::---------------------------------------
// MIT NO ATTRIBUTION
// ==================
// Copyright 2024 Timothy Lottes
// ------------------
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so.
// ------------------
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//=====================================||=======================================
//|||||||||||||||||||||||||||||||||||||::|||||||||||||||||||||||||||||||||||||||
//=====================================||=======================================
// DBM VERSION 1.0
//-------------------------------------::---------------------------------------
// The 'DBM' is short for '[D]ou[B]ling [M]onitor'
// CRT-stylized image doubler (2x on each axis, 4x area)
//-------------------------------------::---------------------------------------
// THE PURPOSE
// ===========
// This is an alternative to simply doing 1:2x2 integer scaling
// Hids square pixel artifacts while retaining perceptual sharpness
// It works just like a CRT which is pushed to slight horizontal undersampling
// This does a 1:2 scale on axis, but uses a 3:1 pixel period grille
//-------------------------------------::---------------------------------------
// FLAT-PANEL PROBLEMS
// ===================
// Fixed near {even,odd} patterns can cause panel artifacts
// Artifacts are caused by the 'dot inversion' in panel's pixel control circuit
// Where the polarity of pixels alternates to reduce flicker and cross-talk
// So flat signals get destructive (canceling) polarity inversion
// But signals that match the polarity inversion can catastrophically overload
// -------------------
// This uses a 3-pixel period pattern for the grille to avoid polarity problems
// Also this does a subtractive grille
// It pushes energy out of 1-pixel instead of towards 1-pixel
// This tends to be a lot more stable on flat panels
//-------------------------------------::---------------------------------------
// SCALING ALIGNMENT
// =================
// +---+ | |
// | E | | T | ... Texel is centered on even scanlines
// +---+ | | For sharpest output
// | o | +-------+ ... Odd scanlines get a fixed 50% blend
// +---+ | | The axis this is applied to depends on DBM_V
// | E | | T |
// +---+ | |
// | o | +-------+
// +---+ | |
//-------------------------------------::---------------------------------------
// MIMIMAL EXPOSURE CHANGE
// =======================
// Simply applying a mask by multiply would darken the whole image
// This instead linearly redistributes intensity away from masked areas
// And applies a very minor multiply mask so white retains some hint of masking
//=====================================||=======================================
//|||||||||||||||||||||||||||||||||||||::|||||||||||||||||||||||||||||||||||||||
//=====================================||=======================================
// CONFIGURATIONS
//-------------------------------------::---------------------------------------
// KNOBS
// =====
// DBM_B
// Final brightness
// Going too small and the output gets too dark
// Going closer to 1 would remove too much mask on white output
// =====
// DBM_C
// Set {1=color, 0=monochrome} grille pattern
// =====
// DBM_M
// Maximum energy distribution as a function of neighbor pixels
// Range {0=off, to 1.0=maximum}
// Using too much in 'color' cases can result in panel output problems
// =====
// DBM_V
// Set {1=vertical, 0=horizontal} grille pattern
//=====================================||=======================================
// These are not designed to be changed for spatial scaling
// But one might want to adjust the DBM_B when mixed with rolling BFI
#ifndef DBM_B
#define DBM_B (1.0-1.0/16.0)
#endif
#define DBM_M 0.75
//-------------------------------------::---------------------------------------
// These are configurable
#ifndef DBM_C
#define DBM_C 1
#endif
#ifndef DBM_V
#define DBM_V 1
#endif
//|||||||||||||||||||||||||||||||||||||::|||||||||||||||||||||||||||||||||||||||
//=====================================||=======================================
// PORTABILITY
//-------------------------------------::---------------------------------------
// The callback
// DbmH3 DbmTex(DbmF2 c) - Returns (half if possible) RGB texture lookup
// Fixed to LOD=0
// THIS MUST RETURN LINEAR DATA!!!!!!!!!!!!!!!!!
// Else none of the DBM logic will work right
//-------------------------------------::---------------------------------------
// Sorry only time to do the SHADERTOY interface, but regular GLSL/HLSL is easy
//=====================================||=======================================
#ifdef DBM_GLSL
#endif
//=====================================||=======================================
#ifdef DBM_HLSL
#define DbmF1 float
#define DbmF2 float2
#define DbmF4 float4
#define DbmI1 uint
#define DbmI2 uint2
#define DbmP1 bool
//
#define DbmH1 half
#define DbmH3 half3
#define DbmH4 half4
//
#define DbmAddH3(a,b) ((a)+(b))
#define DbmAddI1(a,b) ((a)+(b))
#define DbmAndI1(a,b) ((a)&(b))
#define DbmCvtF2I2(a) DbmF2(a)
#define DbmFmaF2(a,b,c) ((a)*(b)+(c))
#define DbmFmaF4(a,b,c) ((a)*(b)+(c))
#define DbmFmaH3(a,b,c) ((a)*(b)+(c))
#define DbmFractF2(a) frac(a)
#define DmbGeP1F1(a,b) ((a)>=(b))
#define DmbLeP1F1(a,b) ((a)<=(b))
#define DbmMaxH3(a,b) max(a,b)
#define DbmMulH3(a,b) ((a)*(b))
#define DbmSatH3(a) clamp(a,DbmH3(0.0, 0.0, 0.0),DbmH3(1.0, 1.0, 1.0))
#define DbmSelH1(a,b,c) ((c)?(b):(a))
#endif
//=====================================||=======================================
#ifdef DBM_SHADERTOY
// This is a junky GLSL interface (no half support, etc)
#define DbmF1 float
#define DbmF2 vec2
#define DbmF4 vec4
#define DbmI1 uint
#define DbmI2 uvec2
#define DbmP1 bool
//-------------------------------------::---------------------------------------
// No 16-bit support so just override using float
#define DbmH1 float
#define DbmH3 vec3
#define DbmH4 vec4
//-------------------------------------::---------------------------------------
// General portable instruction intrinsics
#define DbmAddH3(a,b) ((a)+(b))
#define DbmAddI1(a,b) ((a)+(b))
#define DbmAndI1(a,b) ((a)&(b))
#define DbmCvtF2I2(a) DbmF2(a)
#define DbmFmaF2(a,b,c) ((a)*(b)+(c))
#define DbmFmaF4(a,b,c) ((a)*(b)+(c))
#define DbmFmaH3(a,b,c) ((a)*(b)+(c))
#define DbmFractF2(a) fract(a)
#define DmbGeP1F1(a,b) ((a)>=(b))
#define DmbLeP1F1(a,b) ((a)<=(b))
#define DbmMaxH3(a,b) max(a,b)
#define DbmMulH3(a,b) ((a)*(b))
#define DbmSatH3(a) clamp(a,DbmH3(0.0),DbmH3(1.0))
#define DbmSelH1(a,b,c) ((c)?(b):(a))
#endif //DBM_SHADERTOY
//|||||||||||||||||||||||||||||||||||||::|||||||||||||||||||||||||||||||||||||||
//=====================================||=======================================
// SHADER ENTRY
//-------------------------------------::---------------------------------------
// This returns the linear color space output
// This could be faster perhaps in the 'DBM_C=0' case
// if one wants to rewrite everything in CS and output 3 pixels/lane
//=====================================||=======================================
DbmH3 Dbm(
DbmI2 g, // Global integer pixel coordinate for output
DbmF2 kRcp2Img, // 1/(2*inputImageSizeInPixels)
DbmF2 kHalfRcpImg, // 0.5/inputImageSizeInPixels
DbmF2 kQutrRcpImg){ // 0.25/inputImageSizeInPixels
//-------------------------------------::---------------------------------------
// SAMPLING
// ========
// Even output pixels are on texel centers (when using kHalfRcpImg)
// :
// |--e--|--o--|--e--|--o--|
// : : : :
// ---t-----|-----t-----|---
// :
// Odd output pixels are 50% between two texels
// --------
// This does 5 bilinear fetches in a horizontal span {a,b,c,d,e}
// For the nearest 5 output pixels centered around 'c' (the target output)
//-------------------------------------::---------------------------------------
DbmF2 gg=DbmCvtF2I2(g);
#if DBM_V==0
// Using standard bilinear on Y
// Because it removes the hot pixel in the grille problem
// Using fixed bilinear (sharper) on X
DbmF2 p=DbmFmaF2(gg,kRcp2Img,DbmF2(kHalfRcpImg.x,kQutrRcpImg.y));
#else
DbmF2 p=DbmFmaF2(gg,kRcp2Img,DbmF2(kQutrRcpImg.x,kHalfRcpImg.y));
#endif
DbmH3 a,b,c,d,e;
#if DBM_V
// Want the values for nearby outputs
DbmF4 pABDE=DbmFmaF4(DbmF4(-2.0,-1.0,1.0,2.0),kRcp2Img.xxxx,p.xxxx);
// Should match up to NSA form on later AMD HW
a=DbmTex(DbmF2(pABDE.x,p.y));
b=DbmTex(DbmF2(pABDE.y,p.y));
c=DbmTex(p);
d=DbmTex(DbmF2(pABDE.z,p.y));
e=DbmTex(DbmF2(pABDE.w,p.y));
#else
DbmF4 pABDE=DbmFmaF4(DbmF4(-2.0,-1.0,1.0,2.0),kRcp2Img.yyyy,p.yyyy);
a=DbmTex(DbmF2(p.x,pABDE.x));
b=DbmTex(DbmF2(p.x,pABDE.y));
c=DbmTex(p);
d=DbmTex(DbmF2(p.x,pABDE.z));
e=DbmTex(DbmF2(p.x,pABDE.w));
#endif
//-------------------------------------::---------------------------------------
// REORDERING
// ==========
// This needs spans of 3 filtered texels around the primary grille column
// In order to do energy redistribution correctly
// Naming the 3x1's as 'UVW' where 'V' is the primary grille column
// ----------
// A B C D E
// - - - - -
// . r R r . <-- centered on R
// . . g G g
// b B b . .
// - - - - -
// r R r . .
// . g G g . <-- centered on G
// . . b B b
// - - - - -
// . . r R r
// g G g . .
// . b B b . <-- centered on B
//-------------------------------------::---------------------------------------
// Using integer mod here would be slow
// R--|--G--|--B--|--R
// 0 : 1 : 2 : 3
// 0 : 1/3 : 2/3 : 3/3
// 1/6 3/6 5/6 7/6
// |--R--|--G--|--B--|
gg=DbmFractF2(DbmFmaF2(DbmF2(1.0/3.0, 1.0/3.0),gg,DbmF2(1.0/6.0, 1.0/6.0)));
#if DBM_V
DbmP1 isR=DmbLeP1F1(gg.x,DbmF1(1.0/3.0));
DbmP1 isB=DmbGeP1F1(gg.x,DbmF1(2.0/3.0));
#else
DbmP1 isR=DmbLeP1F1(gg.y,DbmF1(1.0/3.0));
DbmP1 isB=DmbGeP1F1(gg.y,DbmF1(2.0/3.0));
#endif
DbmH3 u;
DbmH3 v;
DbmH3 w;
#if DBM_C
// Do {isG,isR} case first
u.r=DbmSelH1(a.r,b.r,isR);
u.g=DbmSelH1(b.g,c.g,isR);
u.b=DbmSelH1(c.b,a.b,isR);
v.r=DbmSelH1(b.r,c.r,isR);
v.g=DbmSelH1(c.g,d.g,isR);
v.b=DbmSelH1(d.b,b.b,isR);
w.r=DbmSelH1(c.r,d.r,isR);
w.g=DbmSelH1(d.g,e.g,isR);
w.b=DbmSelH1(e.b,c.b,isR);
// Do {isR|G,isB} case
u.r=DbmSelH1(u.r,c.r,isB);
u.g=DbmSelH1(u.g,a.g,isB);
u.b=DbmSelH1(u.b,b.b,isB);
v.r=DbmSelH1(v.r,d.r,isB);
v.g=DbmSelH1(v.g,b.g,isB);
v.b=DbmSelH1(v.b,c.b,isB);
w.r=DbmSelH1(w.r,e.r,isB);
w.g=DbmSelH1(w.g,c.g,isB);
w.b=DbmSelH1(w.b,d.b,isB);
#else
// All channels just grab whatever the G locations are
u.r=DbmSelH1(b.r,c.r,isR);
u.g=DbmSelH1(b.g,c.g,isR);
u.b=DbmSelH1(b.b,c.b,isR);
v.r=DbmSelH1(c.r,d.r,isR);
v.g=DbmSelH1(c.g,d.g,isR);
v.b=DbmSelH1(c.b,d.b,isR);
w.r=DbmSelH1(d.r,e.r,isR);
w.g=DbmSelH1(d.g,e.g,isR);
w.b=DbmSelH1(d.b,e.b,isR);
//
u.r=DbmSelH1(u.r,a.r,isB);
u.g=DbmSelH1(u.g,a.g,isB);
u.b=DbmSelH1(u.b,a.b,isB);
v.r=DbmSelH1(v.r,b.r,isB);
v.g=DbmSelH1(v.g,b.g,isB);
v.b=DbmSelH1(v.b,b.b,isB);
w.r=DbmSelH1(w.r,c.r,isB);
w.g=DbmSelH1(w.g,c.g,isB);
w.b=DbmSelH1(w.b,c.b,isB);
#endif
//-------------------------------------::---------------------------------------
// ENERGY REDISTRIBUTION
//-------------------------------------::---------------------------------------
// Redistrbute energy from 'v' into both 'uw'
// Drop brightness first
u=DbmMulH3(u,DbmH3(DBM_B, DBM_B, DBM_B));
v=DbmMulH3(v,DbmH3(DBM_B, DBM_B, DBM_B));
w=DbmMulH3(w,DbmH3(DBM_B, DBM_B, DBM_B));
// Limit by whatever pixel would clip first
DbmH3 uw=DbmMaxH3(u,w);
// Allowed maximum is at most half of 'v' because its going to two pixels
DbmH3 m=DbmSatH3(DbmFmaH3(v,DbmH3(DBM_M*0.5, DBM_M*0.5, DBM_M*0.5),uw));
// Change for {u,w}
m=DbmAddH3(m,-uw);
u=DbmAddH3(m,u);
w=DbmAddH3(m,w);
v=DbmFmaH3(DbmH3(-2.0, -2.0, -2.0),m,v);
//-------------------------------------::---------------------------------------
// COLLECT FINAL OUTPUT
// ====================
// Each of the {r,R,r} and so on maps to {u,v,w}
// Need the 'C' centers
// --------------------
// C
// - - - - -
// . r R r . <-- centered on R
// . . g G g
// b B b . .
// - - - - -
// r R r . .
// . g G g . <-- centered on G
// . . b B b
// - - - - -
// . . r R r
// g G g . .
// . b B b . <-- centered on B
//-------------------------------------::---------------------------------------
DbmH3 o;
#if DBM_C
// Do {isG,isR} case first
o.r=DbmSelH1(w.r,v.r,isR);
o.g=DbmSelH1(v.g,u.g,isR);
o.b=DbmSelH1(u.b,w.b,isR);
// Do {isR|G,isB} case
o.r=DbmSelH1(o.r,u.r,isB);
o.g=DbmSelH1(o.g,w.g,isB);
o.b=DbmSelH1(o.b,v.b,isB);
#else
// No color separation case
o.r=DbmSelH1(v.r,u.r,isR);
o.g=DbmSelH1(v.g,u.g,isR);
o.b=DbmSelH1(v.b,u.b,isR);
//
o.r=DbmSelH1(o.r,w.r,isB);
o.g=DbmSelH1(o.g,w.g,isB);
o.b=DbmSelH1(o.b,w.b,isB);
#endif
return o;}
//-------------------------------------------------------------------------------
// RESHADE ENTRY
void PS_Upscale(in float4 pos : SV_Position, in float2 texcoord : TEXCOORD, out float4 fragColor : SV_Target)
{
DbmI2 uv = DbmI2(texcoord * BUFFER_SCREEN_SIZE);
// Constants for DBM
DbmF2 kRcp2Img =float2(1.0 , 1.0 )/(float2(2.0, 2.0)*BUFFER_SCREEN_SIZE * 0.5);
DbmF2 kQutrRcpImg=float2(0.25, 0.25)/(float2(1.0, 2.0)*BUFFER_SCREEN_SIZE * 0.5);
DbmF2 kHalfRcpImg=float2(0.5 , 0.5 )/(float2(1.0, 1.0)*BUFFER_SCREEN_SIZE * 0.5);
DbmH4 c = DbmH4(0.0, 0.0, 0.0, 0.0);
c.rgb = Dbm(uv.xy, kRcp2Img,kHalfRcpImg,kQutrRcpImg);
//c.r = 1; c.g = 0; c.b = 0;
// if you are using srgb sampler uncomment this
c.r=SrgbEncF1(c.r);c.g=SrgbEncF1(c.g);c.b=SrgbEncF1(c.b);
fragColor = c.rgb;
}
technique LottesDBM {
pass {
VertexShader = PostProcessVS;
PixelShader = PS_Upscale;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment