11
Copyright (C) 2010-2012 cgwg, Themaister and DOLLS
13
This program is free software; you can redistribute it and/or modify it
14
under the terms of the GNU General Public License as published by the Free
15
Software Foundation; either version 2 of the License, or (at your option)
18
(cgwg gave their consent to have the original version of this shader
19
distributed under the GPL in this message:
21
http://board.byuu.org/viewtopic.php?p=26075#p26075
23
"Feel free to distribute my shaders under the GPL. After all, the
24
barrel distortion code was taken from the Curvature shader, which is
27
This shader variant is pre-configured with screen curvature
30
uniform float texture_sizeX <
33
ui_max = BUFFER_WIDTH;
34
ui_label = "Screen Width [CRT-Geom]";
37
uniform float texture_sizeY <
40
ui_max = BUFFER_HEIGHT;
41
ui_label = "Screen Height [CRT-Geom]";
44
uniform float video_sizeX <
47
ui_max = BUFFER_WIDTH;
48
ui_label = "Frame Width [CRT-Geom]";
49
ui_tooltip = "This should be sized according to the video data in the texture (If you're using emulators, set this to the Emu video frame size, otherwise, keep it the same as Screen Width/Height or Fullscreen res.)";
52
uniform float video_sizeY <
55
ui_max = BUFFER_HEIGHT;
56
ui_label = "Frame Height [CRT-Geom]";
57
ui_tooltip = "This should be sized according to the video data in the texture (If you're using emulators, set this to the Emu video frame size, otherwise, keep it the same as Screen Width/Height or Fullscreen res.)";
60
uniform float CRTgamma <
65
ui_label = "Target Gamma [CRT-Geom]";
68
uniform float monitorgamma <
73
ui_label = "Monitor Gamma [CRT-Geom]";
81
ui_label = "Distance [CRT-Geom]";
84
uniform float CURVATURE <
89
ui_label = "Curvature Toggle [CRT-Geom]";
97
ui_label = "Curvature Radius [CRT-Geom]";
100
uniform float cornersize <
105
ui_label = "Corner Size [CRT-Geom]";
108
uniform float cornersmooth <
113
ui_label = "Corner Smoothness [CRT-Geom]";
116
uniform float x_tilt <
121
ui_label = "Horizontal Tilt [CRT-Geom]";
124
uniform float y_tilt <
129
ui_label = "Vertical Tilt [CRT-Geom]";
132
uniform float overscan_x <
137
ui_label = "Horiz. Overscan % [CRT-Geom]";
140
uniform float overscan_y <
145
ui_label = "Vert. Overscan % [CRT-Geom]";
148
uniform float DOTMASK <
153
ui_label = "Dot Mask Toggle [CRT-Geom]";
156
uniform float SHARPER <
161
ui_label = "Sharpness [CRT-Geom]";
164
uniform float scanline_weight <
169
ui_label = "Scanline Weight [CRT-Geom]";
177
ui_label = "Luminance Boost [CRT-Geom]";
180
uniform float interlace_toggle <
185
ui_label = "Interlacing [CRT-Geom]";
188
uniform bool OVERSAMPLE <
189
ui_tooltip = "Enable 3x oversampling of the beam profile; improves moire effect caused by scanlines+curvature";
192
uniform bool INTERLACED <
193
ui_tooltip = "Use interlacing detection; may interfere with other shaders if combined";
196
#define FIX(c) max(abs(c), 1e-5);
197
#define PI 3.141592653589
199
#define TEX2D(c) pow(tex2D(ReShade::BackBuffer, (c)), float4(CRTgamma,CRTgamma,CRTgamma,CRTgamma))
200
#define texture_size float2(texture_sizeX, texture_sizeY)
201
#define video_size float2(video_sizeX, video_sizeY)
203
static const float2 aspect = float2(1.0, 0.75);
205
uniform int framecount < source = "framecount"; >;
207
float fmod(float a, float b)
209
float c = frac(abs(a/b))*abs(b);
210
return (a < 0) ? -c : c; /* if ( a < 0 ) c = 0-c */
213
float intersect(float2 xy, float2 sinangle, float2 cosangle)
215
float A = dot(xy,xy)+d*d;
216
float B = 2.0*(R*(dot(xy,sinangle)-d*cosangle.x*cosangle.y)-d*d);
217
float C = d*d + 2.0*R*d*cosangle.x*cosangle.y;
218
return (-B-sqrt(B*B-4.0*A*C))/(2.0*A);
221
float2 bkwtrans(float2 xy, float2 sinangle, float2 cosangle)
223
float c = intersect(xy, sinangle, cosangle);
224
float2 pnt = float2(c,c)*xy;
225
pnt -= float2(-R,-R)*sinangle;
227
float2 tang = sinangle/cosangle;
228
float2 poc = pnt/cosangle;
229
float A = dot(tang,tang)+1.0;
230
float B = -2.0*dot(poc,tang);
231
float C = dot(poc,poc)-1.0;
232
float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A);
233
float2 uv = (pnt-a*sinangle)/cosangle;
234
float r = FIX(R*acos(a));
235
return uv*r/sin(r/R);
238
float2 fwtrans(float2 uv, float2 sinangle, float2 cosangle)
240
float r = FIX(sqrt(dot(uv,uv)));
242
float x = 1.0-cos(r/R);
243
float D = d/R + x*cosangle.x*cosangle.y+dot(uv,sinangle);
244
return d*(uv*cosangle-x*sinangle)/D;
247
float3 maxscale(float2 sinangle, float2 cosangle)
249
float2 c = bkwtrans(-R * sinangle / (1.0 + R/d*cosangle.x*cosangle.y), sinangle, cosangle);
250
float2 a = float2(0.5,0.5)*aspect;
251
float2 lo = float2(fwtrans(float2(-a.x,c.y), sinangle, cosangle).x,
252
fwtrans(float2(c.x,-a.y), sinangle, cosangle).y)/aspect;
253
float2 hi = float2(fwtrans(float2(+a.x,c.y), sinangle, cosangle).x,
254
fwtrans(float2(c.x,+a.y), sinangle, cosangle).y)/aspect;
255
return float3((hi+lo)*aspect*0.5,max(hi.x-lo.x,hi.y-lo.y));
258
float4 scanlineWeights(float distance, float4 color)
260
// "wid" controls the width of the scanline beam, for each RGB
261
// channel The "weights" lines basically specify the formula
262
// that gives you the profile of the beam, i.e. the intensity as
263
// a function of distance from the vertical center of the
264
// scanline. In this case, it is gaussian if width=2, and
265
// becomes nongaussian for larger widths. Ideally this should
266
// be normalized so that the integral across the beam is
267
// independent of its width. That is, for a narrower beam
268
// "weights" should have a higher peak at the center of the
269
// scanline than for a wider beam.
270
float4 wid = 2.0 + 2.0 * pow(color, float4(4.0,4.0,4.0,4.0));
271
float weights = float(distance / scanline_weight);
272
return (lum + 1.4) * exp(-pow(weights * rsqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid);
275
float4 PS_CRTGeom(float4 vpos : SV_Position, float2 uv : TexCoord) : SV_Target
278
float2 TextureSize = float2(SHARPER * texture_size.x, texture_size.y);
279
float mod_factor = uv.x * texture_size.x * ReShade::ScreenSize.x / video_size.x;
280
float2 ilfac = float2(1.0,clamp(floor(video_size.y/(200.0 * interlace_toggle)),1.0,2.0));
281
float2 sinangle = sin(float2(x_tilt, y_tilt));
282
float2 cosangle = cos(float2(x_tilt, y_tilt));
283
float3 stretch = maxscale(sinangle, cosangle);
284
float2 one = ilfac / TextureSize;
286
// Here's a helpful diagram to keep in mind while trying to
287
// understand the code:
290
// -------------------------------
292
// | 01 | 11 | 21 | 31 | <-- current scanline
294
// -------------------------------
296
// | 02 | 12 | 22 | 32 | <-- next scanline
298
// -------------------------------
301
// Each character-cell represents a pixel on the output
302
// surface, "@" represents the current pixel (always somewhere
303
// in the bottom half of the current scan-line, or the top-half
304
// of the next scanline). The grid of lines represents the
305
// edges of the texels of the underlying texture.
307
// Texture coordinates of the texel containing the active pixel.
309
if (CURVATURE > 0.5){
311
cd *= texture_size / video_size;
312
cd = (cd-float2(0.5,0.5))*aspect*stretch.z+stretch.xy;
313
xy = (bkwtrans(cd, sinangle, cosangle)/float2(overscan_x / 100.0, overscan_y / 100.0)/aspect+float2(0.5,0.5)) * video_size / texture_size;
319
cd2 *= texture_size / video_size;
320
cd2 = (cd2 - float2(0.5,0.5)) * float2(overscan_x / 100.0, overscan_y / 100.0) + float2(0.5,0.5);
321
cd2 = min(cd2, float2(1.0,1.0)-cd2) * aspect;
322
float2 cdist = float2(cornersize,cornersize);
323
cd2 = (cdist - min(cd2,cdist));
324
float dist = sqrt(dot(cd2,cd2));
325
float cval = clamp((cdist.x-dist)*cornersmooth,0.0, 1.0);
327
float2 xy2 = ((xy*TextureSize/video_size-float2(0.5,0.5))*float2(1.0,1.0)+float2(0.5,0.5))*video_size/TextureSize;
328
// Of all the pixels that are mapped onto the texel we are
329
// currently rendering, which pixel are we currently rendering?
330
float2 ilfloat = float2(0.0,ilfac.y > 1.5 ? fmod(float(framecount),2.0) : 0.0);
332
float2 ratio_scale = (xy * TextureSize - float2(0.5,0.5) + ilfloat)/ilfac;
334
float filter = video_size.y / ReShade::ScreenSize.y;
335
float2 uv_ratio = frac(ratio_scale);
337
// Snap to the center of the underlying texel.
339
xy = (floor(ratio_scale)*ilfac + float2(0.5,0.5) - ilfloat) / TextureSize;
341
// Calculate Lanczos scaling coefficients describing the effect
342
// of various neighbour texels in a scanline on the current
344
float4 coeffs = PI * float4(1.0 + uv_ratio.x, uv_ratio.x, 1.0 - uv_ratio.x, 2.0 - uv_ratio.x);
346
// Prevent division by zero.
347
coeffs = FIX(coeffs);
350
coeffs = 2.0 * sin(coeffs) * sin(coeffs / 2.0) / (coeffs * coeffs);
353
coeffs /= dot(coeffs, float4(1.0,1.0,1.0,1.0));
355
// Calculate the effective colour of the current and next
356
// scanlines at the horizontal location of the current pixel,
357
// using the Lanczos coefficients above.
358
float4 col = clamp(mul(coeffs, float4x4(
359
TEX2D(xy + float2(-one.x, 0.0)),
361
TEX2D(xy + float2(one.x, 0.0)),
362
TEX2D(xy + float2(2.0 * one.x, 0.0)))),
365
float4 col2 = clamp(mul(coeffs, float4x4(
366
TEX2D(xy + float2(-one.x, one.y)),
367
TEX2D(xy + float2(0.0, one.y)),
369
TEX2D(xy + float2(2.0 * one.x, one.y)))),
372
// Calculate the influence of the current and next scanlines on
373
// the current pixel.
374
float4 weights = scanlineWeights(uv_ratio.y, col);
375
float4 weights2 = scanlineWeights(1.0 - uv_ratio.y, col2);
378
uv_ratio.y =uv_ratio.y+1.0/3.0*filter;
379
weights = (weights+scanlineWeights(uv_ratio.y, col))/3.0;
380
weights2=(weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2))/3.0;
381
uv_ratio.y =uv_ratio.y-2.0/3.0*filter;
382
weights=weights+scanlineWeights(abs(uv_ratio.y), col)/3.0;
383
weights2=weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2)/3.0;
385
float3 mul_res = (col * weights + col2 * weights2).rgb;
386
mul_res *= float3(cval,cval,cval);
388
// dot-mask emulation:
389
// Output pixels are alternately tinted green and magenta.
390
float3 dotMaskWeights = lerp(
391
float3(1.0, 1.0 - DOTMASK, 1.0),
392
float3(1.0 - DOTMASK, 1.0, 1.0 - DOTMASK),
393
floor(fmod(mod_factor, 2.0))
395
mul_res *= dotMaskWeights;
398
// Convert the image gamma for display on our output device.
399
mul_res = pow(mul_res, float3(1.0 / monitorgamma,1.0 / monitorgamma,1.0 / monitorgamma));
402
return float4(mul_res, 1.0);
407
VertexShader=PostProcessVS;
408
PixelShader=PS_CRTGeom;