Your shader looks okay to me at first glance, though I’m not really on top of the GLSL format. Does it give you anything in your log?
Related: after a number of failed attempts over the past 2 weeks, I finally got CRT-geom-interlaced converted to Cg:
/* COMPATIBILITY
- HLSL compilers
- Cg compilers
*/
// This is a good place for any #defines you might have
#define FIX(c) max(abs(c), 1e-5);
// Comment the next line to disable interpolation in linear gamma (and gain speed).
//#define LINEAR_PROCESSING
// Enable screen curvature.
#define CURVATURE
// Enable 3x oversampling of the beam profile
#define OVERSAMPLE
// Use the older, purely gaussian beam profile
//#define USEGAUSSIAN
#define distortion 0.1
// Macros.
#define FIX(c) max(abs(c), 1e-5);
#define PI 3.141592653589
#ifdef LINEAR_PROCESSING
# define TEX2D(c) pow(tex2D(IN.texture, (c)), float4(CRTgamma))
#else
# define TEX2D(c) tex2D(IN.texture, (c))
#endif
// gamma of simulated CRT
#define CRTgamma 2.4
// gamma of display monitor (typically 2.2 is correct)
#define monitorgamma 2.2
// overscan (e.g. 1.02 for 2% overscan)
#define overscan float2(1.00,1.00)
// aspect ratio ; needs 'transform' function
#define aspect float2(1.0, 0.75)
// size of curved corners
#define cornersize 0.02
// border smoothness parameter
// decrease if borders are too aliased
#define cornersmooth 80.0
// lengths are measured in units of (approximately) the width of the monitor
// simulated distance from viewer to monitor ; needs 'transform' function
#define d 2.0
// radius of curvature ; needs 'transform' function. use 'distortion' instead
#define R 1.5
// tilt angle in radians
// (behavior might be a bit wrong if both components are nonzero) ; needs 'transform' function
#define angle float2(0.0,-0.15)
struct input // These are things that can attach to 'IN' to achieve similar function to ruby* stuff in existing GLSL shaders. NOTE: the ruby* prefix is deprecated but still works in GLSL for legacy compatibility purposes.
{
float2 video_size;
float2 texCoord_size;
float2 output_size;
float frame_count;
float frame_direction;
float frame_rotation;
float texture_size;
sampler2D texture : TEXUNIT0;
};
struct tex_coords
{
float2 texCoord : TEXCOORD0;
float mod_factor : TEXCOORD1;
float2 one : TEXCOORD2;
};
// VERTEX SHADER //
void main_vertex
(
float4 position : POSITION,
out float4 oPosition : POSITION,
uniform float4x4 modelViewProj,
float4 color : COLOR,
out float4 oColor : COLOR,
float2 tex : TEXCOORD,
uniform input IN,
out tex_coords coords
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
coords.texCoord = tex + float2(0.0, 0.0);
coords.mod_factor = tex.x * IN.output_size.x * IN.texture_size.x / IN.video_size.x;
coords.one = 1.0 / IN.texture_size;
}
// Precalculate a bunch of useful values we'll need in the fragment
// shader.
float2 sinangle = sin(angle);
float2 cosangle = cos(angle);
float3 stretch = maxscale();
float intersect(float2 xy)
{
float A = dot(xy,xy)+d*d;
float B = 2.0*(R*(dot(xy,sinangle)-d*cosangle.x*cosangle.y)-d*d);
float C = d*d + 2.0*R*d*cosangle.x*cosangle.y;
return (-B-sqrt(B*B-4.0*A*C))/(2.0*A);
}
float2 bkwtrans(float2 xy)
{
float c = intersect(xy);
float2 point = float2(c)*xy;
point -= float2(-R)*sinangle;
point /= float2(R);
float2 tang = sinangle/cosangle;
float2 poc = point/cosangle;
float A = dot(tang,tang)+1.0;
float B = -2.0*dot(poc,tang);
float C = dot(poc,poc)-1.0;
float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A);
float2 uv = (point-a*sinangle)/cosangle;
float r = R*acos(a);
return uv*r/sin(r/R);
}
float2 fwtrans(float2 uv)
{
float r = FIX(sqrt(dot(uv,uv)));
uv *= sin(r/R)/r;
float x = 1.0-cos(r/R);
float D = d/R + x*cosangle.x*cosangle.y+dot(uv,sinangle);
return d*(uv*cosangle-x*sinangle)/D;
}
float3 maxscale()
{
float2 c = bkwtrans(-R * sinangle / (1.0 + R/d*cosangle.x*cosangle.y));
float2 a = float2(0.5,0.5)*aspect;
float2 lo = float2(fwtrans(float2(-a.x,c.y)).x,
fwtrans(float2(c.x,-a.y)).y)/aspect;
float2 hi = float2(fwtrans(float2(+a.x,c.y)).x,
fwtrans(float2(c.x,+a.y)).y)/aspect;
return float3((hi+lo)*aspect*0.5,max(hi.x-lo.x,hi.y-lo.y));
}
// Nonworking but left here for reference; reverted to 'radialDistortion' function instead
float2 transform(float2 coord, uniform input IN)
{
coord *= IN.texture_size / IN.video_size;
coord = (coord-float2(0.5))*aspect*stretch.z+stretch.xy;
return (bkwtrans(coord)/overscan/aspect+float2(0.5)) * IN.video_size / IN.texture_size;
}
// Apply radial distortion to the given coordinate.
float2 radialDistortion(float2 coord, uniform input IN) {
coord *= IN.texture_size.x / IN.video_size.x;
float2 cc = coord - 0.5;
float dist = dot(cc, cc) * distortion;
return (coord + cc * (1.0 + dist) * dist) * IN.video_size.x / IN.texture_size.x;
}
float corner(float2 coord, uniform input IN)
{
coord *= IN.texture_size / IN.video_size;
coord = (coord - float2(0.5)) * overscan + float2(0.5);
coord = min(coord, float2(1.0)-coord) * aspect;
float2 cdist = float2(cornersize);
coord = (cdist - min(coord,cdist));
float dist = sqrt(dot(coord,coord));
return clamp((cdist.x-dist)*cornersmooth,0.0, 1.0);
}
// Calculate the influence of a scanline on the current pixel.
//
// 'distance' is the distance in texture coordinates from the current
// pixel to the scanline in question.
// 'color' is the colour of the scanline at the horizontal location of
// the current pixel.
float4 scanlineWeights(float distance, float4 color)
{
// "wid" controls the width of the scanline beam, for each RGB channel
// The "weights" lines basically specify the formula that gives
// you the profile of the beam, i.e. the intensity as
// a function of distance from the vertical center of the
// scanline. In this case, it is gaussian if width=2, and
// becomes nongaussian for larger widths. Ideally this should
// be normalized so that the integral across the beam is
// independent of its width. That is, for a narrower beam
// "weights" should have a higher peak at the center of the
// scanline than for a wider beam.
#ifdef USEGAUSSIAN
float4 wid = 0.3 + 0.1 * pow(color, float4(3.0));
float4 weights = float4(distance / wid);
return 0.4 * exp(-weights * weights) / wid;
#else
float4 wid = 2.0 + 2.0 * pow(color, float4(4.0));
float4 weights = float4(distance / 0.3);
return 1.4 * exp(-pow(weights * inversesqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid);
#endif
}
// FRAGMENT SHADER //
float4 main_fragment(in tex_coords co, uniform input IN, uniform sampler2D s_p : TEXUNIT0) : COLOR
{
// Paste your fragment code--the part that is inside the GLSL shader's "void main(){ ... }"--here:
// Here's a helpful diagram to keep in mind while trying to
// understand the code:
//
// | | | | |
// -------------------------------
// | | | | |
// | 01 | 11 | 21 | 31 | <-- current scanline
// | | @ | | |
// -------------------------------
// | | | | |
// | 02 | 12 | 22 | 32 | <-- next scanline
// | | | | |
// -------------------------------
// | | | | |
//
// Each character-cell represents a pixel on the output
// surface, "@" represents the current pixel (always somewhere
// in the bottom half of the current scan-line, or the top-half
// of the next scanline). The grid of lines represents the
// edges of the texels of the underlying texture.
// Texture coordinates of the texel containing the active pixel.
#ifdef CURVATURE
float2 xy = radialDistortion(co.texCoord, IN); //transform(co.texCoord, IN);
#else
float2 xy = co.texCoord;
#endif
float cval = corner(xy, IN);
float2 ilfac = float2(1.0,floor(IN.video_size.y/200.0));
// The size of one texel, in texture-coordinates.
float2 one = ilfac / IN.texture_size;
// Of all the pixels that are mapped onto the texel we are
// currently rendering, which pixel are we currently rendering?
float2 ilvec = float2(0.0,ilfac.y > 1.5 ? mod(float(IN.frame_count),2.0) : 0.0);
float2 ratio_scale = (xy * IN.texture_size - float2(0.5) + ilvec)/ilfac;
#ifdef OVERSAMPLE
float filter = (IN.video_size / (IN.output_size * IN.texture_size)) *(ratio_scale.y);
#endif
float2 uv_ratio = fract(ratio_scale);
// Snap to the center of the underlying texel.
xy = (floor(ratio_scale)*ilfac + float2(0.5) - ilvec) / IN.texture_size;
// Calculate Lanczos scaling coefficients describing the effect
// of various neighbour texels in a scanline on the current
// pixel.
float4 coeffs = PI * float4(1.0 + uv_ratio.x, uv_ratio.x, 1.0 - uv_ratio.x, 2.0 - uv_ratio.x);
// Prevent division by zero.
coeffs = FIX(coeffs);
// Lanczos2 kernel.
coeffs = 2.0 * sin(coeffs) * sin(coeffs / 2.0) / (coeffs * coeffs);
// Normalize.
coeffs /= dot(coeffs, float4(1.0));
// Calculate the effective colour of the current and next
// scanlines at the horizontal location of the current pixel,
// using the Lanczos coefficients above.
float4 col = clamp(mul(coeffs, float4x4(
TEX2D(xy + float2(-co.one.x, 0.0)),
TEX2D(xy),
TEX2D(xy + float2(co.one.x, 0.0)),
TEX2D(xy + float2(2.0 * co.one.x, 0.0)))),
0.0, 1.0);
float4 col2 = clamp(mul(coeffs, float4x4(
TEX2D(xy + float2(-co.one.x, co.one.y)),
TEX2D(xy + float2(0.0, co.one.y)),
TEX2D(xy + co.one),
TEX2D(xy + float2(2.0 * co.one.x, co.one.y)))),
0.0, 1.0);
#ifndef LINEAR_PROCESSING
col = pow(col , float4(CRTgamma));
col2 = pow(col2, float4(CRTgamma));
#endif
// Calculate the influence of the current and next scanlines on
// the current pixel.
float4 weights = scanlineWeights(uv_ratio.y, col);
float4 weights2 = scanlineWeights(1.0 - uv_ratio.y, col2);
#ifdef OVERSAMPLE
uv_ratio.y =uv_ratio.y+1.0/3.0*filter;
weights = (weights+scanlineWeights(uv_ratio.y, col))/3.0;
weights2=(weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2))/3.0;
uv_ratio.y =uv_ratio.y-2.0/3.0*filter;
weights=weights+scanlineWeights(abs(uv_ratio.y), col)/3.0;
weights2=weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2)/3.0;
#endif
float3 mul_res = (col * weights + col2 * weights2).rgb * float3(cval);
// dot-mask emulation:
// Output pixels are alternately tinted green and magenta.
float3 dotMaskWeights = lerp(
float3(1.0, 0.7, 1.0),
float3(0.7, 1.0, 0.7),
floor(mod(co.mod_factor, 2.0))
);
mul_res *= dotMaskWeights;
// Convert the image gamma for display on our output device.
mul_res = pow(mul_res, float3(1.0 / monitorgamma));
return float4(mul_res, 1.0);
}
I had to downgrade the curvature effect to use the older ‘radialDistortion’ function instead of the ‘transform’ function, so doing the variable aspect and tilting doesn’t work, but the corner effects still work just fine.