Added TV Color Levels to GTUv50

ChatGPT is pretty cool. I asked it to isolate the parts related to TV color levels from TV out tweaks, pasted the code, and asked for it to be added to the third pass of GTU, and it worked :smiley:

GTUv50 custom third pass with TV Color Levels:

#version 450

layout(push_constant) uniform Push
{
	vec4 OutputSize;
	vec4 OriginalSize;
	vec4 SourceSize;
	float noScanlines;
	float tvVerticalResolution;
	float blackLevel;
	float contrast;
	float compositeConnection;
	float TVOUT_TV_COLOR_LEVELS; // NEW: TV Color Levels Enable
} params;

#pragma parameter noScanlines "No Scanlines" 0.0 0.0 1.0 1.0
#pragma parameter tvVerticalResolution "TV Vertical Resolution" 250.0 20.0 1000.0 10.0
#pragma parameter blackLevel "Black Level" 0.07 -0.30 0.30 0.01
#pragma parameter contrast "Contrast" 1.0 0.0 2.0 0.1
#pragma parameter TVOUT_TV_COLOR_LEVELS "TVOut TV Color Levels Enable" 0.0 0.0 1.0 1.0

layout(std140, set = 0, binding = 0) uniform UBO
{
   mat4 MVP;
} global;

#include "config.h"

#define pi        3.14159265358
#define normalGauss(x) ((exp(-(x)*(x)*0.5))/sqrt(2.0*pi))

float normalGaussIntegral(float x)
{
   float a1 = 0.4361836;
   float a2 = -0.1201676;
   float a3 = 0.9372980;
   float p = 0.3326700;
   float t = 1.0 / (1.0 + p*abs(x));
   return (0.5-normalGauss(x) * (t*(a1 + t*(a2 + a3*t))))*sign(x);
}

vec3 scanlines( float x , vec3 c){
   float temp=sqrt(2*pi)*(params.tvVerticalResolution * params.SourceSize.w);

   float rrr=0.5*(params.SourceSize.y * params.OutputSize.w);
   float x1=(x+rrr)*temp;
   float x2=(x-rrr)*temp;
   c.r=(c.r*(normalGaussIntegral(x1)-normalGaussIntegral(x2)));
   c.g=(c.g*(normalGaussIntegral(x1)-normalGaussIntegral(x2)));
   c.b=(c.b*(normalGaussIntegral(x1)-normalGaussIntegral(x2)));
   c*=(params.OutputSize.y * params.SourceSize.w);
   return c;
}

// TV Color Levels macros and function
#define L(C) clamp((C -16.5/ 256.0)*256.0/(236.0-16.0),0.0,1.0)
#define LCHR(C) clamp((C -16.5/ 256.0)*256.0/(240.0-16.0),0.0,1.0)

vec3 LEVELS(vec3 c0)
{
   if (params.TVOUT_TV_COLOR_LEVELS > 0.5)
   {
      if (params.compositeConnection > 0.5)
         return vec3(L(c0.x),LCHR(c0.y),LCHR(c0.z));
      else
         return L(c0);
   }
   else
      return c0;
}

#define Y(j) (offset.y-(j))
#define a(x) abs(x)
#define d(x,b) (pi*b*min(a(x)+0.5,1.0/b))
#define e(x,b) (pi*b*min(max(a(x)-0.5,-1.0/b),1.0/b))
#define STU(x,b) ((d(x,b)+sin(d(x,b))-e(x,b)-sin(e(x,b)))/(2.0*pi))

#define SOURCE(j) vec2(vTexCoord.x,vTexCoord.y - Y(j) * params.SourceSize.w)
#define C(j) (LEVELS(texture(Source, SOURCE(j)).xyz)) // Apply LEVELS here

#define VAL(j) (C(j)*STU(Y(j),(params.tvVerticalResolution * params.SourceSize.w)))
#define VAL_scanlines(j) (scanlines(Y(j),C(j)))

#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;

void main()
{
   gl_Position = global.MVP * Position;
   vTexCoord = TexCoord;
}

#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;

void main()
{
	vec2	offset	=	fract((vTexCoord.xy * params.SourceSize.xy) - 0.5);
	vec3	tempColor	=	vec3(0.0);

   float range=ceil(0.5+params.SourceSize.y/params.tvVerticalResolution);

   float i;
   
   if (params.noScanlines > 0.0)
      for (i=-range;i<range+2.0;i++){
         tempColor+=VAL(i);
      }
   else
      for (i=-range;i<range+2.0;i++){
         tempColor+=VAL_scanlines(i);
      }

	tempColor-=vec3(params.blackLevel);
	tempColor*=(params.contrast/vec3(1.0-params.blackLevel));
   FragColor = vec4(tempColor, 1.0);
}
5 Likes

Ok so the cool part about this is that you can use ChatGPT for tasks like this- I quickly figured out that I don’t need TV color levels :smiley:

Use the ā€œNTSC colorsā€ shader instead. You don’t need any other adjustment to black level or color levels prior to adding CRT stuff. This should be used for every console except those where you manually select an NTSC palette (e.g., Nestopia).

TV Out Color Levels is pretty much only for connecting a computer to a television.

2 Likes

Okay DON’T use NTSC colors, lol. Unless you want to, of course. But it’s not needed for accuracy or anything.

If you’re using guest-advanced-ntsc, you should also (probably?) set the black level in GTU to 0.00.

2 Likes