Rotate shader

I’m wondering if there’s any rotate helper shader, the goal is to turn it 90º for vertical games. I guess this could be done also directly in code, hope it’s not too difficult.

It’s actually a bit of a hassle to do in shaders because the rotation matrix happens around the origin, which is in one of the corners, so you then have to move the image, but how much to move it varies with the resolution (see: the cocktail cabinet shader, which had the same issue). It’s manageable now with the runtime parameter tweaking, but still has to be adjusted on a per-resolution basis.

Is there a reason why the settings > video options > rotation option is insufficient?

Yes, I was expecting that question, hehe. If you rotate a vertical game by 90º then it’s no longer a vertical game. I only wanted to rotate shader because when you stretch a shader horizontally (vertical games needs to do so) it (the shader) would look wrong. But stretched vertically never (or rarely) is an issue. This is 5:7 ratio and this 7:12 (1:1 par)

edit: well another reason to rotate shader would be that these vertical games run on rotated TV’s if I’m not wrong, but I wouldn’t put my hand on it.

Ok, try this one, then:

#pragma parameter height "Rotated Image Height" -1.145 -3.0 3.0 0.01
#pragma parameter width "Rotated Image Width" 2.0 1.0 5.0 0.05
#pragma parameter location_x "Rotated Image Location X" 0.75 -2.0 2.0 0.005
#pragma parameter location_y "Rotated Image Location Y" -0.5 -2.0 2.0 0.005

#ifdef PARAMETER_UNIFORM
uniform float height;
uniform float width;
uniform float location_x;
uniform float location_y;
#else
#define height -1.145
#define width 2.0
#define location_x 0.75
#define location_y -0.5
#endif

/* COMPATIBILITY 
   - HLSL compilers
   - Cg   compilers
*/

struct input
{
  float2 video_size;
  float2 texture_size;
  float2 output_size;
  float  frame_count;
  float  frame_direction;
  float frame_rotation;
  sampler2D texture;
};

void main_vertex
(
   float4 position   : POSITION,
   float4 color   : COLOR,
   float2 texCoord1 : TEXCOORD0,
   float2 texCoord2 : TEXCOORD1,

    uniform float4x4 modelViewProj,
   uniform input IN,

   out float4 oPosition : POSITION,
   out float4 oColor    : COLOR,
   out float2 otexCoord1 : TEXCOORD0,
   out float2 otexCoord2 : TEXCOORD1
)
{
   float4x4 RotationMatrix = float4x4( height, 0.0, 0.0, 0.0,
         0.0, width, 0.0, 0.0,
         0.0, 0.0, 1.0, 0.0,
         0.0, 0.0, 0.0, 1.0 );

   oPosition = mul(mul(modelViewProj, position), RotationMatrix);
   oColor = color;
   otexCoord1 = mul(position.st , float2x2(0.0, 1.0, -1.0, 0.0)) + float2(location_x, location_y);
   otexCoord2 = -1.0 * (mul(position.st , float2x2(0.0, 1.0, -1.0, 0.0)) + float2((1.0 - location_x), location_y));
}

struct output 
{
   float4 color    : COLOR;
};

float4 main_fragment(float2 texCoord1 : TEXCOORD0, float2 texCoord2 : TEXCOORD1, uniform input IN) : COLOR
{
   float4 color = float4(tex2D(IN.texture, texCoord1));
   return color;
}

It’s just the cocktail table shader with one of the images deleted (if the image is upside down, change that last instance of ‘texCoord1’ to ‘texCoord2’). Put it after any other shaders to get the vertical scanlines of a rotated display.

Thanks for help, although I’m not sure what I should get. The image rotates 90º with either texCoord. If I rotate video (to compensate) image is much narrower than my original custom ratio of 5:7.

I edited the shader to add another parameter that will let you move the image around more effectively, so you can stretch it and move it however you want. It’s not working with scanlines and stuff, though. I’ll see if I can do something about that.

OK, no problem, I didn’t think it would be so a hassle. Fo the mean time I’m using crt-geom-flat-sharpness which doesn’t look “too” bad.

Ok, it doesn’t look like you can just put it before/after other shaders because the texCoord changes are moving the image out from under the other shaders’ effects (I think). However, it’s pretty easy to add directly to other shaders. You just have to modify the vertex with these changes:

   float4x4 RotationMatrix = float4x4( height, 0.0, 0.0, 0.0,
         0.0, width, 0.0, 0.0,
         0.0, 0.0, 1.0, 0.0,
         0.0, 0.0, 0.0, 1.0 );
   oPosition = mul(mul(modelViewProj, position), RotationMatrix); // this keeps the image from getting mirrored
   otexCoord = mul(position.st , float2x2(0.0, 1.0, -1.0, 0.0)) + float2(location_x, location_y); //put this into the existing texCoord line

and add these entries to the runtime parameters:

#pragma parameter height "Rotated Image Height" -1.145 -3.0 3.0 0.01
#pragma parameter width "Rotated Image Width" 2.0 1.0 5.0 0.05
#pragma parameter location_x "Rotated Image Location X" 0.75 -2.0 2.0 0.005
#pragma parameter location_y "Rotated Image Location Y" -0.5 -2.0 2.0 0.005
...
uniform float height;
uniform float width;
uniform float location_x;
uniform float location_y;
...
#define height -1.145
#define width 2.0
#define location_x 0.75
#define location_y -0.5

Here’s crt-geom with the aforementioned changes:

#pragma parameter CRTgamma "CRTGeom Target Gamma" 2.4 0.1 5.0 0.1
#pragma parameter monitorgamma "CRTGeom Monitor Gamma" 2.2 0.1 5.0 0.1
#pragma parameter d "CRTGeom Distance" 1.5 0.1 3.0 0.1
#pragma parameter CURVATURE "CRTGeom Curvature Toggle" 1.0 0.0 1.0 1.0
#pragma parameter R "CRTGeom Curvature Radius" 2.0 0.1 10.0 0.1
#pragma parameter cornersize "CRTGeom Corner Size" 0.03 0.001 1.0 0.005
#pragma parameter cornersmooth "CRTGeom Corner Smoothness" 1000.0 80.0 2000.0 100.0
#pragma parameter x_tilt "CRTGeom Horizontal Tilt" 0.0 -0.5 0.5 0.05
#pragma parameter y_tilt "CRTGeom Vertical Tilt" 0.0 -0.5 0.5 0.05
#pragma parameter overscan_x "CRTGeom Horiz. Overscan %" 100.0 -125.0 125.0 1.0
#pragma parameter overscan_y "CRTGeom Vert. Overscan %" 100.0 -125.0 125.0 1.0
#pragma parameter DOTMASK "CRTGeom Dot Mask Toggle" 0.3 0.0 0.3 0.3
#pragma parameter SHARPER "CRTGeom Sharpness" 1.0 1.0 3.0 1.0
#pragma parameter scanline_weight "CRTGeom Scanline Weight" 0.3 0.1 0.5 0.05

#pragma parameter height "Rotated Image Height" -1.145 -3.0 3.0 0.01
#pragma parameter width "Rotated Image Width" 2.0 1.0 5.0 0.05
#pragma parameter location_x "Rotated Image Location X" 0.75 -2.0 2.0 0.005
#pragma parameter location_y "Rotated Image Location Y" -0.5 -2.0 2.0 0.005

#ifdef PARAMETER_UNIFORM
uniform float CRTgamma;
uniform float monitorgamma;
uniform float d;
uniform float CURVATURE;
uniform float R;
uniform float cornersize;
uniform float cornersmooth;
uniform float x_tilt;
uniform float y_tilt;
uniform float overscan_x;
uniform float overscan_y;
uniform float DOTMASK;
uniform float SHARPER;
uniform float scanline_weight;

uniform float height;
uniform float width;
uniform float location_x;
uniform float location_y;

#else
#define CRTgamma 2.4
#define monitorgamma 2.2
#define d 1.5
#define CURVATURE 1.0
#define R 2.0
#define cornersize 0.03
#define cornersmooth 1000.0
#define x_tilt 0.0
#define y_tilt 0.0
#define overscan_x 100.0
#define overscan_y 100.0
#define DOTMASK 0.3
#define SHARPER 1.0
#define scanline_weight 0.3

#define height -1.145
#define width 2.0
#define location_x 0.75
#define location_y -0.5

#endif
// END PARAMETERS //

/* COMPATIBILITY
   - HLSL compilers
   - Cg   compilers
*/

/*
    CRT-interlaced

    Copyright (C) 2010-2012 cgwg, Themaister and DOLLS

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the Free
    Software Foundation; either version 2 of the License, or (at your option)
    any later version.

    (cgwg gave their consent to have the original version of this shader
    distributed under the GPL in this message:

        http://board.byuu.org/viewtopic.php?p=26075#p26075

        "Feel free to distribute my shaders under the GPL. After all, the
        barrel distortion code was taken from the Curvature shader, which is
        under the GPL."
    )
	This shader variant is pre-configured with screen curvature
*/

        // Comment the next line to disable interpolation in linear gamma (and
        // gain speed).
        #define LINEAR_PROCESSING

        // Enable 3x oversampling of the beam profile; improves moire effect caused by scanlines+curvature
        #define OVERSAMPLE

        // Use the older, purely gaussian beam profile; uncomment for speed
        //#define USEGAUSSIAN
      
        // Use interlacing detection; may interfere with other shaders if combined
        #define INTERLACED
	  
	    // Enable Dot-mask emulation:
        // Output pixels are alternately tinted green and magenta.
//        #define DOTMASK
		
		// Comment to disable gamma correction (useful if other filters/shaders/cores are already providing gamma correction)
		#define GAMMA_CORRECTION

        // Macros.
        #define FIX(c) max(abs(c), 1e-5);
        #define PI 3.141592653589

        #ifdef LINEAR_PROCESSING
        #       define TEX2D(c) pow(tex2D(s0, (c)), float4(CRTgamma))
        #else
        #       define TEX2D(c) tex2D(s0, (c))
        #endif

        // aspect ratio
        static float2 aspect = float2(1.0, 0.75);


        float intersect(float2 xy, float2 sinangle, float2 cosangle)
        {
                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, float2 sinangle, float2 cosangle)
        {
                float c = intersect(xy, sinangle, cosangle);
                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 = FIX(R*acos(a));
                return uv*r/sin(r/R);
        }

        float2 fwtrans(float2 uv, float2 sinangle, float2 cosangle)
        {
                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 sinangle, float2 cosangle)
        {
                float2 c = bkwtrans(-R * sinangle / (1.0 + R/d*cosangle.x*cosangle.y), sinangle, cosangle);
                float2 a = float2(0.5,0.5)*aspect;
                float2 lo = float2(fwtrans(float2(-a.x,c.y), sinangle, cosangle).x,
                             fwtrans(float2(c.x,-a.y), sinangle, cosangle).y)/aspect;
                float2 hi = float2(fwtrans(float2(+a.x,c.y), sinangle, cosangle).x,
                             fwtrans(float2(c.x,+a.y), sinangle, cosangle).y)/aspect;
                return float3((hi+lo)*aspect*0.5,max(hi.x-lo.x,hi.y-lo.y));
        }

        // 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 / scanline_weight);
                return 1.4 * exp(-pow(weights * rsqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid);
        #endif
        }

struct input
{
    float2 tex_coord;
    float2 video_size;
    float2 texture_size;
    float2 output_size;
   float frame_count;
};

struct out_vertex {
    float4 position : POSITION;
    float4 color : COLOR;
    float2 texCoord : TEXCOORD0;
        float2 one;
        float mod_factor;
        float2 ilfac;
        float3 stretch;
        float2 sinangle;
        float2 cosangle;
	float2 TextureSize;
};

/* VERTEX_SHADER */
out_vertex main_vertex
(
    float4 position : POSITION,
    float4 color : COLOR,
    float2 texCoord : TEXCOORD0,
    uniform float4x4 modelViewProj,
    uniform input IN
)
{
    out_vertex OUT;
float4x4 RotationMatrix = float4x4( height, 0.0, 0.0, 0.0,
         0.0, width, 0.0, 0.0,
         0.0, 0.0, 1.0, 0.0,
         0.0, 0.0, 0.0, 1.0 );

    OUT.position = mul(mul(modelViewProj, position), RotationMatrix);
    OUT.color = color;

    // Precalculate a bunch of useful values we'll need in the fragment
    // shader.
    OUT.sinangle = sin(float2(x_tilt, y_tilt));
    OUT.cosangle = cos(float2(x_tilt, y_tilt));
    OUT.stretch = maxscale(OUT.sinangle, OUT.cosangle);
   OUT.texCoord = mul(position.st , float2x2(0.0, 1.0, -1.0, 0.0)) + float2(location_x, location_y);
    OUT.TextureSize = float2(SHARPER * IN.texture_size.x, IN.texture_size.y);

#ifdef INTERLACED
    OUT.ilfac = float2(1.0,clamp(floor(IN.video_size.y/200.0),1.0,2.0));
#else
    OUT.ilfac = float2(1.0,clamp(floor(IN.video_size.y/1000.0),1.0,2.0));
#endif

    // The size of one texel, in texture-coordinates.
    OUT.one = OUT.ilfac / OUT.TextureSize;

    // Resulting X pixel-coordinate of the pixel we're drawing.
    OUT.mod_factor = texCoord.x * IN.texture_size.x * IN.output_size.x / IN.video_size.x;

    return OUT;
}

/* FRAGMENT SHADER */
float4 main_fragment(in out_vertex VAR, uniform input IN, uniform sampler2D s0 : TEXUNIT0) : COLOR
{
                // 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.
	float2 xy = 0.0;
      if (CURVATURE > 0.5)
                {
                float2 cd = VAR.texCoord;
                cd *= IN.texture_size / IN.video_size;
                cd = (cd-float2(0.5))*aspect*VAR.stretch.z+VAR.stretch.xy;
                xy =  (bkwtrans(cd, VAR.sinangle, VAR.cosangle)/float2(overscan_x / 100.0, overscan_y / 100.0)/aspect+float2(0.5)) * IN.video_size / IN.texture_size;
		}
      else
               {
                xy = VAR.texCoord;
		}

                float2 cd2 = xy;
                cd2 *= IN.texture_size / IN.video_size;
                cd2 = (cd2 - float2(0.5)) * float2(overscan_x / 100.0, overscan_y / 100.0) + float2(0.5);
                cd2 = min(cd2, float2(1.0)-cd2) * aspect;
                float2 cdist = float2(cornersize);
                cd2 = (cdist - min(cd2,cdist));
                float dist = sqrt(dot(cd2,cd2));
                float cval = clamp((cdist.x-dist)*cornersmooth,0.0, 1.0);

                float2 xy2 = ((xy*VAR.TextureSize/IN.video_size-float2(0.5))*float2(1.0,1.0)+float2(0.5))*IN.video_size/VAR.TextureSize;
                // Of all the pixels that are mapped onto the texel we are
                // currently rendering, which pixel are we currently rendering?
                float2 ilfloat = float2(0.0,VAR.ilfac.y > 1.5 ? fmod(float(IN.frame_count),2.0) : 0.0);

            float2 ratio_scale = (xy * VAR.TextureSize - float2(0.5) + ilfloat)/VAR.ilfac;
      
      #ifdef OVERSAMPLE
                float filter = IN.video_size.y / IN.output_size.y;
      #endif
                float2 uv_ratio = frac(ratio_scale);

                // Snap to the center of the underlying texel.

            xy = (floor(ratio_scale)*VAR.ilfac + float2(0.5) - ilfloat) / VAR.TextureSize;

                // 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(-VAR.one.x, 0.0)),
                    TEX2D(xy),
                    TEX2D(xy + float2(VAR.one.x, 0.0)),
                    TEX2D(xy + float2(2.0 * VAR.one.x, 0.0)))),
            0.0, 1.0);
    float4 col2 = clamp(mul(coeffs, float4x4(
                    TEX2D(xy + float2(-VAR.one.x, VAR.one.y)),
                    TEX2D(xy + float2(0.0, VAR.one.y)),
                    TEX2D(xy + VAR.one),
                    TEX2D(xy + float2(2.0 * VAR.one.x, VAR.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;
        #ifdef GAMMA_CORRECTION
                mul_res += pow(tex2D(s0, xy2).rgb, float3(monitorgamma))*0.1;
		#endif
                mul_res *= float3(cval);

                // dot-mask emulation:
                // Output pixels are alternately tinted green and magenta.
                float3 dotMaskWeights = lerp(
                        float3(1.0, 1.0 - DOTMASK, 1.0),
                        float3(1.0 - DOTMASK, 1.0, 1.0 - DOTMASK),
                        floor(fmod(VAR.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));

                // Color the texel.
                return float4(mul_res, 1.0);
}

EDIT: btw, you’ll definitely want to toggle off the dot mask bit because it just puts visible pink and green stripes on the screen when rotated.

I tested your crt-geom, and image renders black. Then I modified inerlaced -halation, and image shows horizontal and mirrored, I post a screenshot. Not sure I did it right.

/* COMPATIBILITY
   - HLSL compilers
   - Cg   compilers
*/

/*
	CRT-interlaced-halation shader - pass2 (Tetsuya Mod gives a slight blur)

	Like the CRT-interlaced shader, but adds a subtle glow around bright areas
	of the screen.

	Copyright (C) 2010-2012 cgwg, Themaister and DOLLS

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	(cgwg gave their consent to have the original version of this shader
	distributed under the GPL in this message:

		http://board.byuu.org/viewtopic.php?p=26075#p26075

		"Feel free to distribute my shaders under the GPL. After all, the
		barrel distortion code was taken from the Curvature shader, which is
		under the GPL."
	)
*/
#pragma parameter height "Rotated Image Height" -1.145 -3.0 3.0 0.01
#pragma parameter width "Rotated Image Width" 2.0 1.0 5.0 0.05
#pragma parameter location_x "Rotated Image Location X" 0.75 -2.0 2.0 0.005
#pragma parameter location_y "Rotated Image Location Y" -0.5 -2.0 2.0 0.005

uniform float height;
uniform float width;
uniform float location_x;
uniform float location_y;

#define height -1.145
#define width 2.0
#define location_x 0.75
#define location_y -0.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
	 
		// Use interlacing detection; may interfere with other shaders if combined
		#define INTERLACED
	 
	 // Enable Dot-mask emulation:
		// Output pixels are alternately tinted green and magenta.
	 //#define DOTMASK
	 
	 //This is the halation bloom
	 //Enable if using several shaders.
	 //Disabling it reduces moiré for single pass.
	 #define MULTIPASS
	 
		// Macros.
		#define FIX(c) max(abs(c), 1e-5);
		#define PI 3.141592653589

		#ifdef LINEAR_PROCESSING
		#       define TEX2D(c) pow(tex2D(ORIG.texture, (c)), float4(CRTgamma))
		#else
		#       define TEX2D(c) tex2D(ORIG.texture, (c))
		#endif



				// START of parameters

				// gamma of simulated CRT
				static float CRTgamma = 2.4;
				// gamma of display monitor (typically 2.2 is correct)
				static float monitorgamma = 3.0;
				// overscan (e.g. 1.02 for 2% overscan)
				static float2 overscan = float2(1.0,1.0);
				// aspect ratio
				static float2 aspect = float2(1.0, 0.75);
				// lengths are measured in units of (approximately) the width
				// of the monitor simulated distance from viewer to monitor
				static float d = 1.5;
				// radius of curvature
				static float R = 3.0;
				// tilt angle in radians
				// (behavior might be a bit wrong if both components are
				// nonzero)
				const static float2 angle = float2(0.0,-0.0);
				// size of curved corners
				static float cornersize = 0.02;
				// border smoothness parameter
				// decrease if borders are too aliased
				static float cornersmooth = 500.0;

				// END of parameters


		float intersect(float2 xy, float2 sinangle, float2 cosangle)
		{
				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, float2 sinangle, float2 cosangle)
		{
				float c = intersect(xy, sinangle, cosangle);
				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 = FIX(R*acos(a));
				return uv*r/sin(r/R);
		}

		float2 fwtrans(float2 uv, float2 sinangle, float2 cosangle)
		{
				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 sinangle, float2 cosangle)
		{
				float2 c = bkwtrans(-R * sinangle / (1.0 + R/d*cosangle.x*cosangle.y), sinangle, cosangle);
				float2 a = float2(0.5,0.5)*aspect;
				float2 lo = float2(fwtrans(float2(-a.x,c.y), sinangle, cosangle).x,
							 fwtrans(float2(c.x,-a.y), sinangle, cosangle).y)/aspect;
				float2 hi = float2(fwtrans(float2(+a.x,c.y), sinangle, cosangle).x,
							 fwtrans(float2(c.x,+a.y), sinangle, cosangle).y)/aspect;
				return float3((hi+lo)*aspect*0.5,max(hi.x-lo.x,hi.y-lo.y));
		}

		// 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 * rsqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid);
		#endif
		}

struct orig
{
	float2 tex_coord;
	uniform float2 video_size;
	uniform float2 texture_size;
	uniform float2 output_size;
	uniform sampler2D texture;
};


struct input
{
	float2 video_size;
	float2 texture_size;
	float2 output_size;
	float frame_count;
	float frame_direction;
	float frame_rotation;
};


struct out_vertex {
	float4 position : POSITION;
	float4 color : COLOR;
	float2 texCoord : TEXCOORD0;
		float2 one;
		float mod_factor;
		float2 ilfac;
		float3 stretch;
		float2 sinangle;
		float2 cosangle;
};



/* VERTEX_SHADER */
out_vertex main_vertex
(
	float4 position : POSITION,
	float4 color : COLOR,
	float2 texCoord : TEXCOORD0,

	uniform float4x4 modelViewProj,
	orig ORIG,
	uniform input IN
)
{

	out_vertex OUT;

    float4x4 RotationMatrix = float4x4( height, 0.0, 0.0, 0.0,
             0.0, width, 0.0, 0.0,
             0.0, 0.0, 1.0, 0.0,
             0.0, 0.0, 0.0, 1.0 );

	OUT.position = mul(mul(modelViewProj, position), RotationMatrix); // this keeps the image from getting mirrored
	OUT.color = color;


				// Precalculate a bunch of useful values we'll need in the fragment
				// shader.
				OUT.sinangle = sin(angle);
				OUT.cosangle = cos(angle);
				OUT.stretch = maxscale(OUT.sinangle, OUT.cosangle);
				OUT.texCoord = mul(position.st , float2x2(0.0, 1.0, -1.0, 0.0)) + float2(location_x, location_y); //put this into the existing texCoord line


				OUT.ilfac = float2(1.0,clamp(floor(IN.video_size.y/200.0),1.0,2.0));

				// The size of one texel, in texture-coordinates.
				OUT.one = OUT.ilfac / ORIG.texture_size;

				// Resulting X pixel-coordinate of the pixel we're drawing.
				OUT.mod_factor = texCoord.x * ORIG.texture_size.x * IN.output_size.x / ORIG.video_size.x;

	return OUT;
}


/* FRAGMENT SHADER */
float4 main_fragment(in out_vertex VAR, uniform sampler2D decal : TEXUNIT0, orig ORIG, uniform input IN) : COLOR
{

/*        float2 transform(float2 coord)
		{
				coord *= ORIG.texture_size / ORIG.video_size;
				coord = (coord-float2(0.5))*aspect*stretch.z+stretch.xy;
				return (bkwtrans(coord)/overscan/aspect+float2(0.5)) * ORIG.video_size / ORIG.texture_size;
		}

		float corner(float2 coord)
		{
				coord *= ORIG.texture_size / ORIG.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);
		}
*/

				// 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 cd = VAR.texCoord;
				cd *= ORIG.texture_size / ORIG.video_size;
				cd = (cd-float2(0.5))*aspect*VAR.stretch.z+VAR.stretch.xy;
				float2 xy =  (bkwtrans(cd, VAR.sinangle, VAR.cosangle)/overscan/aspect+float2(0.5)) * ORIG.video_size / ORIG.texture_size;

		#else
				float2 xy = VAR.texCoord;
		#endif
				float2 cd2 = xy;
				cd2 *= ORIG.texture_size / ORIG.video_size;
				cd2 = (cd2 - float2(0.5)) * overscan + float2(0.5);
				cd2 = min(cd2, float2(1.0)-cd2) * aspect;
				float2 cdist = float2(cornersize);
				cd2 = (cdist - min(cd2,cdist));
				float dist = sqrt(dot(cd2,cd2));
				float cval = clamp((cdist.x-dist)*cornersmooth,0.0, 1.0);

				float2 xy2 = ((xy*ORIG.texture_size/ORIG.video_size-float2(0.5))*float2(1.0,1.0)+float2(0.5))*IN.video_size/IN.texture_size;
				// Of all the pixels that are mapped onto the texel we are
				// currently rendering, which pixel are we currently rendering?
				float2 ilfloat = float2(0.0,VAR.ilfac.y > 1.5 ? fmod(float(IN.frame_count),2.0) : 0.0);
	  #ifdef INTERLACED
				float2 ratio_scale = (xy * IN.texture_size - float2(0.5) + ilfloat)/VAR.ilfac;
	  #else
			float2 ratio_scale = xy * IN.texture_size - float2(0.5);
	  #endif
	 
		#ifdef OVERSAMPLE
				//float filter = fwidth(ratio_scale.y);
				float filter = IN.video_size.y / IN.output_size.y;
		#endif
				float2 uv_ratio = frac(ratio_scale);

				// Snap to the center of the underlying texel.
	  #ifdef INTERLACED
				xy = (floor(ratio_scale)*VAR.ilfac + float2(0.5) - ilfloat) / IN.texture_size;
	  #else
			xy = (floor(ratio_scale) + float2(0.5)) / IN.texture_size;
	  #endif

				// 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(-VAR.one.x, 0.0)),
					TEX2D(xy),
					TEX2D(xy + float2(VAR.one.x, 0.0)),
					TEX2D(xy + float2(2.0 * VAR.one.x, 0.0)))),
			  0.0, 1.0);
		   float4 col2 = clamp(mul(coeffs, float4x4(
					TEX2D(xy + float2(-VAR.one.x, VAR.one.y)),
					TEX2D(xy + float2(0.0, VAR.one.y)),
					TEX2D(xy + VAR.one),
					TEX2D(xy + float2(2.0 * VAR.one.x, VAR.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;
	 #ifdef MULTIPASS
		   mul_res += pow(tex2D(decal, xy2).rgb, float3(monitorgamma))*0.1;
	 #endif
		   mul_res *= float3(cval);

				// dot-mask emulation:
				// Output pixels are alternately tinted green and magenta.
	 #ifdef DOTMASK
				float3 dotMaskWeights = lerp(
						float3(1.0, 0.7, 1.0),
						float3(0.7, 1.0, 0.7),
						floor(fmod(VAR.mod_factor, 2.0))
					);
		#else
				float3 dotMaskWeights = lerp(
						float3(1.0, 1.0, 1.0),
						float3(1.0, 1.0, 1.0),
						floor(fmod(VAR.mod_factor, 2.0))
					);
		#endif
			   

				mul_res *= dotMaskWeights;

				// Convert the image gamma for display on our output device.
				mul_res = pow(mul_res, float3(1.0 / monitorgamma));

				// Color the texel.
				return float4(mul_res, 1.0);
}

Weird, works here. Anyway, you’re close with yours, the parameter part just requires a few more lines of code. The earlier bit assumed the shader you were using already had parameters, so you would just add those lines in the appropriate spots. Replace that part with this block instead:

#pragma parameter height "Rotated Image Height" -1.145 -3.0 3.0 0.01
#pragma parameter width "Rotated Image Width" 2.0 1.0 5.0 0.05
#pragma parameter location_x "Rotated Image Location X" 0.75 -2.0 2.0 0.005
#pragma parameter location_y "Rotated Image Location Y" -0.5 -2.0 2.0 0.005
#ifdef PARAMETER_UNIFORM
uniform float height;
uniform float width;
uniform float location_x;
uniform float location_y;
#else
#define height -1.145
#define width 2.0
#define location_x 0.75
#define location_y -0.5
#endif

Didn’t response because wasn’t working and didn’t want you to push you further. I tested in some cores to no avail, I can only think of RA version, I use RetroArch-Win64-2014-07-17 maybe I need something newer/older.

Hmm. Just downloaded lordashram’s 7/30 build and it’s working fine here. I’ve only tried with radeon though.

Just tried with 7/30 build and no change, must be the card, I use nvidia.

Ah, ok, strange. Thanks for trying. Would you mind trying the misc/cocktail-table.cg shader to see if it works for you? If it doesn’t, I’ll have to try and figure out what’s tripping things up…

I get this, not sure if this is the expected output.

Yeah, that’s perfect :smiley:

Interesting. I guess there’s something going awry when it’s added onto cgwg’s code. Thanks again for testing. I’ll have to track down an nvidia gpu to do some debugging, I suppose.